slartibartfast
slartibartfast

Reputation: 4428

Pointer to dynamic array

Suppose I have a grid of squared defined like so in a class:

Square (* grid)[];

This, oddly, seems to compile fine. I would think it would error because the compiler doesn't know how big the array is? Anyways, it means it is a pointer to an array. Then to initialize it, I do:

grid(new Square[width * height])

This isn't accepted by the compiler, because the new statement returns a pointer to squares rather than a pointer to an array of squares. It makes sense that it does that. Now, is there a simple way to accomplish what I'm asking, other than just declaring Square ** grid and looping through it and doing separate allocations for each column of the 2D array?

Upvotes: 0

Views: 625

Answers (2)

bames53
bames53

Reputation: 88155

I believe the reason Square (* grid)[]; works is because pointers to incomplete types are allowed, and an array without a size counts as an incomplete type.

The reason that you can't do

Square (* grid)[] = new Square[width * height];

even though it looks like the types match up perfectly is just another manifestation of the bug in the design of C where array types are treated specially. It seems like newing an object of type Square[] should return a pointer to an object of that type. However you're not really newing an array type, but new[]ing the element type Square. The result of new[] is a pointer to the element type, in line with the C convention for arrays.

You can use a cast to 'fix' this to use the 'right' type:

// pretend array types behave rationally
Square (* grid)[] = (Square (*)[]) new Square[width * height];
(*grid)[3] = 10;

// the above is equivalent to the following
Square *grid = new Square[width * height];
grid[3] = 10;  

Or you can just do it the C++ way and use a std::vector

std::vector<Square> grid(width * height);

If you have a fixed size array you can use std::array

std::array<Square,10> *grid = new std::array<Square,10>;

std::array pretty much fixes all the mistakes made in the design of array types. For example std::array can't 'forget' its size, functions can take std::array parameters by value (whereas with raw arrays the array syntax just becomes a synonym for pointers), functions can return std::array whereas they are inexplicably prohibited from returning arrays (The syntax would be int foo()[3];), and with std::array there's no need for a special array allocator new[] which must be matched to an array deallocator delete[] (instead you can say foo = new std::array<int,3> and then 'delete foo;')

Upvotes: 0

Mike Seymour
Mike Seymour

Reputation: 254441

Square (* grid)[];

This, oddly, seems to compile fine. I would think it would error because the compiler doesn't know how big the array is?

That's declaring a pointer to an array, not an array; it's fine to declare a pointer to any incomplete type, including an array of unknown size. However, it's quite an unusual thing to do, and not what you want for a dynamic array.

Now, is there a simple way to accomplish what I'm asking?

The easiest dynamic array to use is:

std::vector<Square> grid;

initialised as

grid(width * height)

If you really want to manage the memory yourself, then change your pointer-to-array to a pointer-to-object:

Square * grid;

initialised as

grid(new Square[width * height])

A pointer can point to either a single object, or the start of an array; if it does point to an array, then you can use [] on it just like with a non-dynamic array. Make sure you deallocate it (delete [] grid;) once you've finished with it.

If you want a 2-dimensional array, it's often easiest to use a 1-dimensional array, and wrap the necessary arithmetic in an accessor function:

Square & get_square(size_t row, size_t col) {
    return grid[row * width + col];
}

Upvotes: 3

Related Questions