C++ 将(指向可变大小数组的指针)强制转换为(指向指针的指针)

C++ 将(指向可变大小数组的指针)强制转换为(指向指针的指针),c++,pointers,semantics,C++,Pointers,Semantics,当我将[pointer to a variable size array]转换为[pointer to pointer]时,会发生什么 int ar[r][c]; int **ptr = (int**)ar; // implicit casting not allowed ptr[0][0] = 100; 上面的代码给出了一个运行时错误 将可变大小的数组强制转换为指针可以正常工作: int ar[c]; int *ptr = ar; ptr[0] = 100; 这里,ar衰减为指向第一个元

当我将[pointer to a variable size array]转换为[pointer to pointer]时,会发生什么

int ar[r][c];
int **ptr = (int**)ar;  // implicit casting not allowed
ptr[0][0] = 100;
上面的代码给出了一个运行时错误

将可变大小的数组强制转换为指针可以正常工作:

int ar[c];
int *ptr = ar;
ptr[0] = 100;
这里,
ar
衰减为指向第一个元素的指针


但是,当将
int(*)[c]
强制转换为
int**
时,内部会发生什么?为什么在读取/写入
int**
变量时会导致运行时错误?

问题在于
ptr[0]
*ptr
应该是指针,但它不是。也就是说,
ptr[0]
*ptr
不包含有效指针。在这个地址中有数组ar的第一个元素。因此,当使用表达式
ptr[0][0]
时,您将得到一个运行时错误,通常情况下,程序行为是未定义的。

当您声明
int**ptr
时,它指的是一个int指针数组。但是您声明了一个int数组的数组

这就是为什么语言没有为此提供任何隐式强制转换,因为这两种类型实际上并不相关


像您那样操作强制转换的结果具有未定义的行为。

数组不是指针

数组是一块连续的内存,其中包含所有相同类型的数据,并打包在一起。事实上,如果您有一个指向第一个元素的指针,并且您知道数据的类型,那么您可以使用指针执行与数组相同的许多操作

foo[5]
在数组上获取第5个元素,在指向第一个元素的指针上也获取第5个元素

实际上,您可以将数组转换为类型
foo
,隐式转换为指向第一个元素的指针

现在,你正在做的是完全不同的事情。指向第一个元素的指针是指向整个数组的指针

假设您有一个长度为5的
int*
指针数组。它们中的每一个都可以指向不同的
int[5]
,您可以将
int*[6]
用作二维数组。但是你会注意到这里有一个指向
int*
的指针数组,而不是
int[5]
的数组。由于数组不是指针,它们是不同的东西

现在,我们可以解决这个问题了

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 );
}
这需要C++11。您可以在C++03中手动执行此操作

您看到的崩溃(通过未定义的行为)可能是将数组的第一个元素重新解释为指向-
int
,而不是一个或多个
int
(取决于系统上指针和
int
的相对大小)的指针的结果


你能在帖子中描述一下你的“运行时错误”吗?应用程序刚刚挂起。我猜它得到一个SIGSEGV
intar[r][c]
主要相当于
intar[r*c]使用修改的访问器。“到底发生了什么”-未定义的行为。在强制转换期间,内部没有发生任何特殊情况。你告诉编译器“相信我,这个你认为是数组地址的东西实际上是指针的地址”,它相信你。然后你崩溃了。因为你撒谎了。int
**ptr
有类型(
int**
)。int指针数组的类型为int
*ptr[c]
。它们有点不同没有,它们没有什么不同,int*ptr[]衰减为int**,这其实是一回事。顺便说一句,我不认为最终(tmp)数组的生存期足够长。@Jarod42它将与它所在的代码行一样长,这足够长,可以运行
foo
。(经验法则是,临时值持续“直到
”,或者如果直接绑定到引用,则持续到块的末尾)我看到您能够将数组数组转换为指针数组。虽然我不明白(那个代码对我来说太复杂了):-)
std::array<int*, 5> arr = { ar[0], ar[1], ar[2] };
void foo( int** x ) {}

int main() {
  int a[5][3] = {0};
  foo( &(as_array_of_pointers(a)[0]) );
}