我试图了解类型衰变的本质。例如,我们都知道数组在特定上下文中衰减为指针。我的尝试是理解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
)。
我可以解释为什么会这样吗?为什么数组类型不符合预期的指针类型?
答案 0 :(得分:62)
为什么
int*[]
会衰变为int**
而非int[][]
?
因为用它做指针运算是不可能的。
例如,int p[5][4]
表示(int
的长度为4的数组)的数组。没有涉及指针,它只是一个大小为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的以下部分:
(这是C的所有内容,但这种行为在C ++中基本没有变化。)
答案 1 :(得分:10)
C并非真正“设计”为一种语言;相反,随着需求的增加添加了功能,努力不破坏早期的代码。在开发C的时代,这种渐进式方法是一件好事,因为它意味着在大多数情况下,开发人员可以在语言可能需要做的所有事情都得到解决之前从早期的语言改进中获益。不幸的是,数组和指针处理方式的演变导致了各种各样的规则,这些规则在回想起来是不幸的。
在今天的C语言中,有一个相当实质的类型系统,变量有明确定义的类型,但事情并非总是如此。宣言char arr[8]
;将在当前范围内分配8个字节,并使arr
指向其中的第一个。编译器不会知道arr
代表一个数组 - 它代表一个char指针,就像任何其他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]
,就像现在使用二维数组一样,但编译器不会以这种方式查看事物 - 它只是看到{{ 1}}作为指针的指针。如果有人想让foo [1]指向与foo [2]完全不同的地方,那么可以合法地完成这一点。
当向C添加两个二维数组时,没有必要保持与声明二维数组的早期代码的兼容性,因为没有任何数组。虽然可以指定bar
生成与使用char bar[4][5];
显示的代码相同的代码,但在这种情况下,foo[20]
可用作char[][]
,有人认为,正如在99%的时间内分配数组变量一样是错误的,因此也可以重新分配数组行,这是合法的。因此,C中的数组被识别为不同的类型,它们自己的规则有点奇怪,但它们就是它们。
答案 2 :(得分:7)
因为int[M][N]
和int**
是不兼容的类型。
但是,int[M][N]
可以衰减为int (*)[N]
类型。以下是:
std::is_same<int(*)[1], std::decay<int[1][1]>::type>::value;
应该给你true
。
答案 3 :(得分:3)
二维数组不是存储为指针的指针,而是存储为连续的内存块。
声明为类型int[y][x]
的对象是大小为sizeof(int) * x * y
的块,而类型为int **
的对象是指向int*