int** 和 int[][] 类型有什么区别

What is difference between types int** and int[][]?

本文关键字:int 区别 什么 类型      更新时间:2023-10-16

如果以下赋值有效:

int a[2] = {1,2};
int* b = a;

那么这有什么问题:

int a[2][2]={1,2,3,4};
int** b = a;

C++给出一个错误,指出它无法将int[][]转换为int**。如果int[]int*相同,这两种类型有什么区别?

放轻松。这只是编译器错误。数组非常棘手。规则如下:

数组

类型的变量的值衰减到该数组的元素零的地址

您的第一个代码段如下所示:

int a[2] = {1,2};

因此,根据规则,如果a位于赋值的右侧,那么它会衰减到元素零的地址,这就是为什么它具有类型 int * .这将您带到

int *b = a;

在第二个代码段中,您真正拥有的是一个数组数组。(顺便说一下,为了明确起见,我已经对您的代码进行了一些更改。

int a[2][2]={{1,2},{3,4}};

这次a将衰减到指向两个整数数组的指针!因此,如果您想将a分配给某些内容,则需要此内容具有相同的类型。

int (*b)[2] = a; //Huh!

(这种语法可能对你来说有点令人惊讶,但只要想一想我们已经写了int *b[2]; 明白了吗? b将是指向整数的指针数组!不是我们真正想要的...

你可以停止阅读这里,但你也可以继续前进,因为我还没有告诉你所有的真相。我提到的规则有三个例外...

数组的值不会衰减到元素零的地址,如果

  1. 数组是sizeof的操作数
  2. 数组是&的操作数
  3. 数组
  4. 是字符数组的文本字符串初始值设定项

让我们更详细地解释这些异常,并举例说明:

int a[2];
int *pi = a ; /* the same as pi = &a[0]; */
printf("%dn", sizeof(a)); /* size of the array, not of a pointer is printed! */
int (*pi2)[2] = &a; /* address of the array itself is taken (not the address of a pointer) */

最后

char a[] = "Hello world ";

这里没有复制指向"Hello world"的指针,而是复制了整个字符串并指向此副本。

信息真的很多,一次理解所有内容真的很难,所以慢慢来。我建议你阅读K&R关于这个主题,然后阅读这本优秀的书。

这是经常

出现的事情,所以我会尽量清楚地解释它。

当你创建一个数组时,它会将元素连续地存储在内存中,因此:

int arr[2] = { 1, 2 };

翻译为:

arr:
+---+---+
| 1 | 2 |
+---+---+

指针指向内存中的对象,当取消引用时,通过一元*或通过[],它访问该连续内存。所以之后

int *ptr = arr;

ptr(或&ptr[0],如果您愿意(指向1所在的框,ptr + 1(或&ptr[1](指向2所在的框。这是有道理的。

但是,如果数组在内存中是

连续的,则数组的数组在内存中是连续的。所以:

int arr[2][2] = {{ 1, 2 }, { 3, 4 }};

在内存中看起来像这样:

arr:
+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+

这看起来很像我们的平面阵列。

现在,让我们考虑指向int指针的指针如何在内存中布局:

ptr:
+-------+-------+
| &sub1 | &sub2 |
+-------+-------+
sub1:
+---+---+
| 1 | 2 |
+---+---+
sub2:
+---+---+
| 3 | 4 |
+---+---+

ptr(或&ptr[0](指向sub1ptr + 1(或&ptr[1](指向sub2sub1sub2 彼此之间没有实际关系,并且可以位于内存中的任何位置,但由于它是指向指针的指针,因此保留了 2D 数组的双重取消引用,即使内存结构不兼容也是如此。

T 类型的数组衰减为指向 T 类型的指针,但 T 类型的数组数组不会衰减到指向类型 T 的指针,它们衰减到指向类型 T 数组的指针。因此,当我们的 2D arr衰减到指针时,它不是指向int指针的指针,而是指向int [2]的指针。这种类型的全名是 int (*)[2] ,为了使您的代码行正常工作,您需要使用

int (*ptr)[2] = arr;

哪种类型是正确的。 ptr期望指向一个连续的内存数组,就像arr所做的那样 - ptr(或&ptr[0](指向arrptr + 1(或&ptr[1](指向&arr[1]ptr[0]指向容纳1的盒子,ptr[1]指向容纳3的盒子,因此ptr[0][0]产生 1,ptr[0][1]产生 2,依此类推。

你为什么需要知道这个? 2D 指针似乎比它们的价值更复杂 - 如果您使用malloc则必须在循环中反复调用malloc,并对free执行相同的操作。或者,您可以使用一些邪恶的*诡计来使扁平的一维内存分配像 2D 数组一样运行

// x and y are the first and second dimensions of your array
// so it would be declared T arr[x][y] if x and y were static
int (*arr)[y] = malloc(x * y * sizeof(arr[0][0]));
if(!arr) /* error */;

现在,arr指向大小y int对象的连续数组块。由于它指向的对象是一个数组,因此我们不需要int **对象的双指针间接寻址,完成后,您可以通过一次调用来释放它:

free(arr);

将其与使用 int ** 的版本进行比较:

int **arr = malloc(x * sizeof(*arr));
if(!arr) /* error */;
for(size_t ii = 0; ii < x; ii++)
  {
    arr[ii] = malloc(y * sizeof(**arr));
    if(!arr[ii])
      {
        free(arr[ii]);
        free(arr);
      }
  }
// do work
for(size_t ii = 0; ii < x; ii++)
    free(arr[ii]);
free(arr);

上面的代码有内存泄漏。看看你能不能找到它。(或者只是将版本与那些看似棘手的指向数组的指针一起使用。

著名的衰减约定:数组被视为指向数组第一个元素的指针。

int a[2] = {1,2};
int* b = a; //decay

但是衰减约定不应该多次应用于同一个对象。

int a[2][2]={1,2,3,4};
int** b = a; //decay more than once

int[]int* 不同。 在某些情况下int[]衰变成int*

您可能应该阅读 comp.lang.c 常见问题解答,特别是:

  • Q6.2 但我听说char a[]char *a一模一样。
  • Q6.3 那么C语言中"指针和数组的等价性"是什么意思呢?

也许是数组和指针部分的其余部分。