2013-01-06 70 views
33

我想了解类型衰减的性质。例如,我们都知道数组在特定的上下文中衰变成指针。我的尝试是了解int[]等于int*但二维数组如何与预期的指针类型不匹配。下面是测试情况:为什么int * []衰减为int **而不是int [] []?

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

此预期返回true,但这并不:

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

为什么这是不是真的?我终于找到了一种方法,使其返回true,这是通过使第一维指针:

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

和断言的任何类型的指针,但最后被数组也是如此。例如(int***[] == int****; // true)。

我可以解释为什么会发生这种情况吗?为什么数组类型不像预期的那样对应于指针类型?

回答

61

为什么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 

很明显,它只能做到这一点,因为它知道“内部”尺寸(S)的大小。投到int (*)[4]保留此信息;它是一个指针(长度为4的int)。但是,int **不是;它只是一个指针(指向int)。

对于另取此,看到C FAQ的以下部分:

(这是所有C,但这种行为在C++中基本不变)。

+3

+1。好答案。 – Nawaz

+0

+1简洁的解释。 –

+0

另一种看待这个问题的方法是:'int [M] [N]'到'int **'需要两次转换(这是不允许的),而不是一次转换(这是允许的)。第一次转换需要从'int [M] [N]'转换为*指针*到数组的第一个元素。 [first]元素的类型是'int [N]',因此'int [M] [N]'首先转换成'int(*)[N]',然后需要转换成'int **' * inner *数组的第一个元素'int [N]'转换为'int *'。 – Nawaz

7

Be原因int[M][N]int**是不兼容的类型。

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

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

应该给你true

+1

很简单,很好的答案Nawaz! –

3

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

声明为int[y][x]类型的对象是大小sizeof(int) * x * y的块而,int **类型的对象是一个指向int*

+1

准确地说,“声明为类型为int [y] [x]'**的对象是** sizeof(int)* x * y'”大小的块。 –

10

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

在今天的C语言中,有一个相当实际的类型系统,变量有明确的类型,但事情并非总是如此。声明char arr[8];将在当前范围内分配8个字节,并使arr指向其中的第一个字节。编译器不会知道arr表示一个数组 - 它将代表一个字符指针,就像其他任何char*一样。据我所知,如果一个人宣称char arr1[8], arr2[8];,声明arr1 = arr2;本来就是完全合法的,在概念上与char *st1 = "foo, *st2 = "bar"; st1 = st2;有些相同,但几乎总是代表一个错误。

数组分解成指针的规则起源于数组和指针真的是同一件事。从那以后,数组已经被认为是一种独特的类型,但是语言需要基本上与它们不存在的日子保持一致。在制定规则时,如何处理二维数组并不是问题,因为没有这样的事情。可以像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完全不同的地方,那么可以完全合法地做到这一点。

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

+0

这很有趣,但是你知道任何这方面的参考吗? (这是一个真正的问题,我真的有兴趣了解这个历史。) –

+0

不幸的是,我没有任何单一的参考资料,尽管历史的各个部分已经在其他SO问题中讨论过了。 – supercat

+0

至少有几个SO线程来支持该声明?好贴。 – SparKot

相关问题