34

我试图了解类型衰减的本质。例如,我们都知道数组在特定上下文中衰减为指针。我的尝试是了解二维数组如何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)。

我可以解释为什么会这样吗?为什么数组类型与预期的指针类型不对应?

4

4 回答 4

63

为什么会int*[]衰变成int**而不是int[][]

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

例如,int p[5][4]表示(长度为 4 的数组int)的数组。不涉及指针,它只是一个连续的 size 内存块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]保留此信息;它是一个指向(长度为 4 的数组int)的指针。但是, anint ** 不会;它只是一个指向(指向int)的指针。

有关此问题的另一种看法,请参阅 C 常见问题解答的以下部分:

(这都是针对 C 的,但这种行为在 C++ 中基本不变。)

于 2013-01-06T15:22:49.757 回答
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]以与现在使用二维数组相同的方式使用,但编译器不会以这种方式看待事物——它只是将bar其视为指向指针的指针。如果想让 foo[1] 指向与 foo[2] 完全不同的某个地方,那么完全可以合法地这样做。

当两个二维数组被添加到 C 中时,没有必要保持与早期声明二维数组的代码的兼容性,因为没有。虽然可以指定char bar[4][5];生成与使用 显示的代码等效的代码foo[20],在这种情况下 achar[][]可以用作 a char**,但人们认为就像分配数组变量一样,在 99% 的情况下都是错误的,如果这是合法的,那么数组行的重新分配也是如此。因此,C 中的数组被认为是不同的类型,它们自己的规则有点奇怪,但它们就是这样。

于 2013-01-06T17:37:07.053 回答
8

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

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

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

应该给你true

于 2013-01-06T15:24:35.457 回答
3

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

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

于 2013-01-06T15:30:12.670 回答