简体   繁体   中英

Casting a (pointer to a variable sized array) to (pointer to pointer)

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?

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).

live example

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM