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

bhu*_*hni 19 c++

如果以下分配有效:

int a[2] = {1,2};
int* b = a;
Run Code Online (Sandbox Code Playgroud)

那么这有什么问题:

int a[2][2]={1,2,3,4};
int** b = a;
Run Code Online (Sandbox Code Playgroud)

C++提供了一个错误,它无法转换int[][]int**.两者有什么类型之间的区别,如果int[]是一样的int*

Beg*_*ner 31

别紧张.它只是一个编译器错误.数组非常棘手.这是规则:

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

您的第一个代码段如下:

int a[2] = {1,2};
Run Code Online (Sandbox Code Playgroud)

因此,根据规则,如果a在赋值的右侧,则它衰减为元素零的地址,这就是它具有类型的原因int *.这会带你到

int *b = a;
Run Code Online (Sandbox Code Playgroud)

在第二个片段中,您真正拥有的是一个数组数组.(顺便说一句,为了明确我已经改变了你的代码.)

int a[2][2]={{1,2},{3,4}};
Run Code Online (Sandbox Code Playgroud)

这个时间a会衰减到指向两个整数数组的指针!因此,如果您想要分配a某些东西,您需要具有相同类型的东西.

int (*b)[2] = a; //Huh!
Run Code Online (Sandbox Code Playgroud)

(这个语法对你来说可能有点令人惊叹,但只是想一想我们已经写过int *b[2];了重点?b将是一个整数指针数组!不是我们想要的......)

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

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

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

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

int a[2];

int *pi = a ; /* the same as pi = &a[0]; */

printf("%d\n", 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) */
Run Code Online (Sandbox Code Playgroud)

最后

char a[] = "Hello world ";
Run Code Online (Sandbox Code Playgroud)

此处不会复制指向"Hello world"的指针,但会复制整个字符串并指向此副本.

真正大量的信息,这是真的很难一下子明白了一切,所以把你的时间.我建议你阅读关于这个主题的K&R,然后阅读这本优秀的书.

  • +1,在C++中调试时平衡开发人员通常是第一步.:d (11认同)
  • 很好的答案(太多人误解了这一点).建议阅读:[comp.lang.d FAQ](http://c-faq.com)的第6部分. (3认同)
  • OP注意,虽然`b`是有效的并且它将被编译,但是为了使用它来访问`a`的元素,你需要指定边界,即.`int(*b)[2] = a;`. (2认同)

Chr*_*utz 6

这是很多东西,所以我会尝试尽可能清楚地解释它.

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

int arr[2] = { 1, 2 };
Run Code Online (Sandbox Code Playgroud)

翻译为:

arr:
+---+---+
| 1 | 2 |
+---+---+
Run Code Online (Sandbox Code Playgroud)

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

int *ptr = arr;
Run Code Online (Sandbox Code Playgroud)

ptr(或者&ptr[0]如果你愿意的话)指向盒子的1位置,并且ptr + 1(或&ptr[1])指向盒子的2位置.这是有道理的.

但是如果数组在内存中是连续的,那么数组的数组在内存中是连续的.所以:

int arr[2][2] = {{ 1, 2 }, { 3, 4 }};
Run Code Online (Sandbox Code Playgroud)

在内存中看起来像这样:

arr:
+---+---+---+---+
| 1 | 2 | 3 | 4 |
+---+---+---+---+
Run Code Online (Sandbox Code Playgroud)

这看起来很像我们的扁平阵列.

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

ptr:
+-------+-------+
| &sub1 | &sub2 |
+-------+-------+

sub1:
+---+---+
| 1 | 2 |
+---+---+

sub2:
+---+---+
| 3 | 4 |
+---+---+
Run Code Online (Sandbox Code Playgroud)

ptr(或&ptr[0])指向sub1ptr + 1(或&ptr[1])指向sub2.sub1并且sub2彼此之间没有实际关系,并且可以在内存中的任何位置,但因为它是指向指针的指针,所以即使存储器结构不兼容,也会保留2D数组的双重引用.

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

int (*ptr)[2] = arr;
Run Code Online (Sandbox Code Playgroud)

哪种类型正确.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.或者,你可以使用一些邪恶*弄虚作假,使内存平坦,一维的分配行为就像一个二维数组:

// 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 */;
Run Code Online (Sandbox Code Playgroud)

现在arr指向一个连续yint对象大小的数组块.由于它指向的对象是一个数组,我们不需要int **对象的双指针间接,当你完成后,你可以通过一次调用释放它:

free(arr);
Run Code Online (Sandbox Code Playgroud)

将此与使用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);
Run Code Online (Sandbox Code Playgroud)

上面的代码有内存泄漏.看看你是否能找到它.(或者只使用带有那些看似棘手的指针到数组的版本.)