为什么int * 衰减到int **而不是int ?

我试图理解types衰变的本质。 例如,我们都知道数组在特定的上下文中衰变成指针。 我尝试了解int[]如何等于int*但是二维数组如何与预期的指针types不一致。 这是一个testing用例:

 std::is_same<int*, std::decay<int[]>::type>::value; // true 

如预期的那样返回true,但是这不是:

 std::is_same<int**, std::decay<int[][1]>::type>::value; // false 

为什么这是不正确的? 我终于find了一种方法,使其返回true,这是通过使第一个维度指针:

 std::is_same<int**, std::decay<int*[]>::type>::value; // true 

这个断言适用于任何types的指针,但最后一个是数组。 例如( int***[] == int****; // true )。

我可以解释为什么会发生这种情况吗? 为什么数组types与预期的指针types不一致?

为什么int*[]衰减到int**而不是int[][]

因为用它做指针运算是不可能的。

例如, int p[5][4]表示一个(长度为4的int数组)的数组。 没有涉及的指针,它只是一个大小为5*4*sizeof(int)的连续内存块。 当你问一个特定的元素,例如int a = p[i][j] ,编译器是这样做的:

 char *tmp = (char *)p // Work in units of bytes (char) + i * sizeof(int[4]) // Offset for outer dimension (int[4] is a type) + j * sizeof(int); // Offset for inner dimension int a = *(int *)tmp; // Back to the contained type, and dereference 

显然,它只能这样做,因为它知道“内部”维度的大小。 铸造到一个int (*)[4]保留这个信息; 它是一个指针( int长度为4的数组)。 但是, int ** 不会 ; 它只是一个指针(指向int指针)。

对于另一个承担这一点,请参阅C FAQ的以下部分:

  • 6.18:当我将一个二维数组传递给期望指向指针的函数时,我的编译器抱怨。
  • 6.19:如何在编译时不知道宽度的情况下编写接受二维数组的函数?
  • 6.20:如何在将静态和dynamic分配的multidimensional array传递给函数时互换使用?

(这全部用于C,但是在C ++中这个行为基本上没有改变。)

C并没有真正被devise为一种语言, 相反,function是根据需要添加的,努力不要破坏较早的代码。 在C开发的时代,这种进化的方法是一件好事,因为这意味着大部分开发人员可以在语言可能需要做的所有事情完成之前从早期的语言改进中获益。 不幸的是,数组和指针处理的发展方式导致了各种各样的规则,回想起来,这些规则是不幸的。

在今天的C语言中,有一个相当实际的types系统,variables有明确的types,但并不总是这样。 一个声明char arr[8] ; 会在当前范围内分配8个字节,并将其指向第一个字节。 编译器不会知道arr表示一个数组 – 它将像其他char*一样代表一个char指针。 据我所知,如果有人宣布char arr1[8], arr2[8]; ,语句arr1 = arr2; 本来就是完全合法的,在概念上与char *st1 = "foo, *st2 = "bar"; st1 = st2;在某种程度上是等价的,但是几乎总是代表一个bug。

数组分解成指针的规则源于数组和指针真的是一回事。 从那以后,数组已经被认为是一个独特的types,但是语言需要保持基本上与不存在的时间兼容。 在制定规则时,二维数组应该如何处理的问题不是一个问题,因为没有这样的事情。 人们可以做点像char foo[20]; char *bar[4]; int i; for (i=0; i<4; i++) bar[i] = foo + (i*5); char foo[20]; char *bar[4]; int i; for (i=0; i<4; i++) bar[i] = foo + (i*5); 然后以与现在使用二维数组相同的方式使用bar[x][y] ,但是编译器不会以这种方式查看事物 – 它只是将bar看作指向指针的指针。 如果有人想把foo [1]指向与foo [2]完全不同的地方,那么完全可以合法地这样做。

当将两个二维数组添加到C时,没有必要保持与先前声明二维数组的代码的兼容性,因为没有任何数组。 虽然可以指定char bar[4][5]; 会生成相当于使用foo[20]显示的代码,在这种情况下, char[][]将可用作char** ,正如分配数组variables将被认为是错误的99%的时间,所以也将重新分配数组行,如果这是合法的。 因此,C中的数组被认为是不同的types,它们自己的规则有点奇怪,但它们是什么。

因为int[M][N]int**是不兼容的types。

但是, int[M][N]可以衰减为int (*)[N]types。 所以如下:

 std::is_same<int(*)[1], std::decay<int[1][1]>::type>::value; 

应该给你true

二维数组不作为指针存储,而是作为连续的内存块存储。

声明为types为int[y][x]sizeof(int) * x * y的块,而int **types的对象是指向int*