Reputation: 2048
What exactly happens when I cast a [pointer to a variable sized array] to a [pointer to pointer] ?
int ar[r][c];
int **ptr = (int**)ar; // implicit casting not allowed
ptr[0][0] = 100;
The above code gives a runtime error.
Casting a variable sized array to a pointer works as expected:
int ar[c];
int *ptr = ar;
ptr[0] = 100;
Here ar
decays into a pointer to the first element.
But what happens internally when casting an int(*)[c]
to int**
? Why does it result in a runtime error when reading/writing to the int**
variable?
Upvotes: 1
Views: 194
Reputation: 310970
The problem is that ptr[0]
or *ptr
is supposed to be a pointer, but it is not. That is, ptr[0]
or *ptr
does not contain a valid pointer. At this address there is the first element of array ar. So you will get a run-time error when will use expression ptr[0][0]
That is in general case the program behaviour is undefined.
Upvotes: 1
Reputation: 275385
An array is not a pointer.
An array is a block of contiguous memory of data all of the same type, all packed together. As it happens, if you have a pointer to the first element, and you know the type of the data, you can do many of the same things with your pointer that you could do with your array.
foo[5]
on an array gets the 5th element, and on a pointer to the first element also gets the 5th element.
In fact, you can convert an array to a type foo
to a pointer to the first element implicitly.
Now, what you are doing is something completely different. The pointer to the first element is a pointer to int[5]
-- an entire array.
Suppose you had an array of pointers to int*
of length 5. Each one of them could point to a different int[5]
, and you could use the int*[6]
as a two-dimensional array. But you'll note here we have an array of pointers to int*
, not an array of int[5]
. And as arrays are not pointers, these are different things.
Now, we can fix this.
template<unsigned...>struct indexes{typedef indexes type;};
template<unsigned Max, unsigned...Is> struct make_indexes:make_indexes<Max-1, Max-1, Is...>{};
template<unsigned...Is> struct make_indexes<0, Is...>:indexes<Is...>{};
template<unsigned Max> using make_indexes_t = typename make_indexes<Max>::type;
template<typename T, unsigned N, unsigned M, unsigned... Is>
std::array<T*, M> as_array_of_pointers( indexes<Is...>, T(&arr)[M][N] ) {
return { arr[Is]... };
};
template<typename T, unsigned N, unsigned M>
std::array<T*, M> as_array_of_pointers( T(&arr)[M][N] ) {
return as_array_of_pointers( make_indexes_t<M>{}, arr );
}
The above is a fancy C++11 way to write:
std::array<int*, 5> arr = { ar[0], ar[1], ar[2] };
Now you can take your ar
and turn it into an array-of-pointers. If you have a function taking int**
you can call as_array_of_pointers
and take an explicit pointer to the first element, and rely on temporary lifetime to do the work:
void foo( int** x ) {}
int main() {
int a[5][3] = {0};
foo( &(as_array_of_pointers(a)[0]) );
}
this requires C++11. You can instead do it manually in C++03.
The crash you are seeing (via undefined behavior) is probably the result of reinterpreting the first element of your array as a pointer-to-int
, instead of one or more int
(depending on the relative size of pointers and int
on your system).
Upvotes: 2
Reputation: 23793
When you declare int **ptr
, it refers to an array of int pointers. But you declared an array of arrays of int.
This is why the language does not provide any implicit cast for this, since the two types really don't relate.
Manipulating the result of the cast as you did has undefined behavior.
Upvotes: 0