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

时间:2022-06-21 13:23:48

If the following assignment is valid:

如果以下分配有效:

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

then what is wrong with this:

那么这有什么问题:

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

C++ gives an error that it can't convert int[][] to int**. What is difference between the two types if int[] is the same as int*?

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

4 个解决方案

#1


31  

Take it easy. It is only a compiler error. Arrays are pretty tricky. Here is the rule:

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

The value of a variable of type array decays to the address of element zero of this array

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

Your first snippet looks like:

您的第一个代码段如下:

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

So according to the rule if a is in the right hand side of a assignment then it decays to address of the element zero and that is why it has type int *. This brings you to

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

int *b = a;

In the second snippet what you really have is an array of arrays. (By the way, to make it explicit I've changed your code a bit.)

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

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

This time a will decay to the pointer to an array of two integers! So if you would want to assign a to something, you would need this something to have the same type.

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

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

(This syntax maybe a bit stunning to you, but just think for a moment that we have written int *b[2]; Got the point? b would be an array of pointers to integers! Not really what we wanted...)

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

You could stop reading here, but you could also move on, because I have not told you all the truth. The rule I mentioned has three exceptions...

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

The value of the array will not decay to the address of the element zero if

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

  1. array is operand of sizeof
  2. array是sizeof的操作数

  3. array is operand of &
  4. 数组是&的操作数

  5. array is a literal string initializer for a character array
  6. array是字符数组的文字字符串初始值设定项

Let's explain these exceptions in more detail and with examples:

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

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) */

And finally

char a[] = "Hello world ";

Here not a pointer to "Hello world" is copied, but the whole string is copied and a points to this copy.

这里不会复制指向“Hello world”的指针,但会复制整个字符串并指向此副本。

There is really a lot of information and it is really difficult to understand everything at once, so take your time. I recommend you to read K&R on this topic and afterwards this excellent book.

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

#2


6  

This is something that comes up a lot, so I will attempt to explain it as clearly as I can.

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

When you make an array, it stores the elements contiguously in memory, so:

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

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

Translates to:

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

A pointer points to an object in memory, and when dereferenced, via unary * or via [], it accesses that contiguous memory. So after

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

int *ptr = arr;

ptr (or &ptr[0] if you like) points to the box 1 is in, and ptr + 1 (or &ptr[1]) points to the box 2 is in. This makes sense.

ptr(或者&ptr [0]如果你愿意的话)指向框1,并且ptr + 1(或&ptr [1])指向框2所在。这是有道理的。

But if arrays are contiguous in memory, arrays of arrays are contiguous in memory. So:

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

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

Looks in memory like this:

在内存中看起来像这样:

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

Which looks a lot like our flat array.

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

Now, let's consider how a pointer to a pointer to an int would be laid out in memory:

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

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

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

sub2:
+---+---+
| 3 | 4 |
+---+---+

ptr (or &ptr[0]) points to sub1, and ptr + 1 (or &ptr[1]) points to sub2. sub1 and sub2 have no actual relation to each other, and can be anywhere in memory, but because it's a pointer to a pointer, the double-dereference of a 2D array is preserved, even though the memory structure is not compatible.

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

Arrays of type T decay to pointers to type T, but arrays of arrays of type T do not decay to pointers to pointers to type T, they decay to pointers to arrays of type T. So when our 2D arr decays to a pointer, it is not a pointer to a pointer to an int, but a pointer to an int [2]. The full name of this type is int (*)[2], and to make your line of code work you'd want to use

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

int (*ptr)[2] = arr;

Which is the correct type. ptr expects to point to a contiguous array of memory, like arr does - ptr (or &ptr[0]) points to arr and ptr + 1 (or &ptr[1]) points to &arr[1]. ptr[0] points to the box that holds 1, and ptr[1] points to the box that holds 3, so ptr[0][0] yields 1, ptr[0][1] yields 2, and so on.

哪种类型正确。 ptr期望指向一个连续的内存数组,比如arr do - ptr(或&ptr [0])指向arr和ptr + 1(或&ptr [1])指向&arr [1]。 ptr [0]指向保持1的框,ptr [1]指向保持3的框,因此ptr [0] [0]产生1,ptr [0] [1]产生2,依此类推。

Why do you need to know this? 2D pointers seem more complicated than they're worth - if you were using malloc you'd have to call malloc repeatedly in a loop, and do the same for free. OR, you could use some evil* trickery to make a flat, 1-dimensional allocation of memory act like a 2D array:

为什么你需要知道这个? 2D指针似乎比它们的价值更复杂 - 如果你使用malloc,你必须在循环中重复调用malloc,并且免费执行相同操作。或者,你可以使用一些邪恶的*技巧来做一个平坦的,一维的内存分配就像一个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 */;

Now arr points to a contiguous block of arrays of size y of int objects. Since the object it points to is an array, we don't need the double-pointer-indirection of int ** objects, and when you're done, you can free it with one call:

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

free(arr);

Compare this to a version using int **:

将其与使用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);

The above code has a memory leak. See if you can find it. (Or just use the version with those seemingly tricky pointers-to-arrays.)

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

#3


4  

The famous decay convention: an array is treated as a pointer that points to the first element of the array.

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

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

But the decay convention shouldn't be applied more than once to the same object.

但是衰变惯例不应该多次应用于同一个对象。

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

#4


2  

int[] isn't the same as int*. int[] will decay into an int* in certain contexts.

int []与int *不同。 int []将在某些上下文中衰减为int *。

You probably should read the comp.lang.c FAQ, particularly:

你可能应该阅读comp.lang.c FAQ,特别是:

and perhaps the rest of the Array and Pointers section.

也许是阵列和指针部分的其余部分。

#1


31  

Take it easy. It is only a compiler error. Arrays are pretty tricky. Here is the rule:

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

The value of a variable of type array decays to the address of element zero of this array

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

Your first snippet looks like:

您的第一个代码段如下:

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

So according to the rule if a is in the right hand side of a assignment then it decays to address of the element zero and that is why it has type int *. This brings you to

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

int *b = a;

In the second snippet what you really have is an array of arrays. (By the way, to make it explicit I've changed your code a bit.)

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

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

This time a will decay to the pointer to an array of two integers! So if you would want to assign a to something, you would need this something to have the same type.

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

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

(This syntax maybe a bit stunning to you, but just think for a moment that we have written int *b[2]; Got the point? b would be an array of pointers to integers! Not really what we wanted...)

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

You could stop reading here, but you could also move on, because I have not told you all the truth. The rule I mentioned has three exceptions...

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

The value of the array will not decay to the address of the element zero if

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

  1. array is operand of sizeof
  2. array是sizeof的操作数

  3. array is operand of &
  4. 数组是&的操作数

  5. array is a literal string initializer for a character array
  6. array是字符数组的文字字符串初始值设定项

Let's explain these exceptions in more detail and with examples:

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

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) */

And finally

char a[] = "Hello world ";

Here not a pointer to "Hello world" is copied, but the whole string is copied and a points to this copy.

这里不会复制指向“Hello world”的指针,但会复制整个字符串并指向此副本。

There is really a lot of information and it is really difficult to understand everything at once, so take your time. I recommend you to read K&R on this topic and afterwards this excellent book.

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

#2


6  

This is something that comes up a lot, so I will attempt to explain it as clearly as I can.

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

When you make an array, it stores the elements contiguously in memory, so:

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

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

Translates to:

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

A pointer points to an object in memory, and when dereferenced, via unary * or via [], it accesses that contiguous memory. So after

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

int *ptr = arr;

ptr (or &ptr[0] if you like) points to the box 1 is in, and ptr + 1 (or &ptr[1]) points to the box 2 is in. This makes sense.

ptr(或者&ptr [0]如果你愿意的话)指向框1,并且ptr + 1(或&ptr [1])指向框2所在。这是有道理的。

But if arrays are contiguous in memory, arrays of arrays are contiguous in memory. So:

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

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

Looks in memory like this:

在内存中看起来像这样:

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

Which looks a lot like our flat array.

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

Now, let's consider how a pointer to a pointer to an int would be laid out in memory:

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

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

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

sub2:
+---+---+
| 3 | 4 |
+---+---+

ptr (or &ptr[0]) points to sub1, and ptr + 1 (or &ptr[1]) points to sub2. sub1 and sub2 have no actual relation to each other, and can be anywhere in memory, but because it's a pointer to a pointer, the double-dereference of a 2D array is preserved, even though the memory structure is not compatible.

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

Arrays of type T decay to pointers to type T, but arrays of arrays of type T do not decay to pointers to pointers to type T, they decay to pointers to arrays of type T. So when our 2D arr decays to a pointer, it is not a pointer to a pointer to an int, but a pointer to an int [2]. The full name of this type is int (*)[2], and to make your line of code work you'd want to use

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

int (*ptr)[2] = arr;

Which is the correct type. ptr expects to point to a contiguous array of memory, like arr does - ptr (or &ptr[0]) points to arr and ptr + 1 (or &ptr[1]) points to &arr[1]. ptr[0] points to the box that holds 1, and ptr[1] points to the box that holds 3, so ptr[0][0] yields 1, ptr[0][1] yields 2, and so on.

哪种类型正确。 ptr期望指向一个连续的内存数组,比如arr do - ptr(或&ptr [0])指向arr和ptr + 1(或&ptr [1])指向&arr [1]。 ptr [0]指向保持1的框,ptr [1]指向保持3的框,因此ptr [0] [0]产生1,ptr [0] [1]产生2,依此类推。

Why do you need to know this? 2D pointers seem more complicated than they're worth - if you were using malloc you'd have to call malloc repeatedly in a loop, and do the same for free. OR, you could use some evil* trickery to make a flat, 1-dimensional allocation of memory act like a 2D array:

为什么你需要知道这个? 2D指针似乎比它们的价值更复杂 - 如果你使用malloc,你必须在循环中重复调用malloc,并且免费执行相同操作。或者,你可以使用一些邪恶的*技巧来做一个平坦的,一维的内存分配就像一个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 */;

Now arr points to a contiguous block of arrays of size y of int objects. Since the object it points to is an array, we don't need the double-pointer-indirection of int ** objects, and when you're done, you can free it with one call:

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

free(arr);

Compare this to a version using int **:

将其与使用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);

The above code has a memory leak. See if you can find it. (Or just use the version with those seemingly tricky pointers-to-arrays.)

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

#3


4  

The famous decay convention: an array is treated as a pointer that points to the first element of the array.

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

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

But the decay convention shouldn't be applied more than once to the same object.

但是衰变惯例不应该多次应用于同一个对象。

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

#4


2  

int[] isn't the same as int*. int[] will decay into an int* in certain contexts.

int []与int *不同。 int []将在某些上下文中衰减为int *。

You probably should read the comp.lang.c FAQ, particularly:

你可能应该阅读comp.lang.c FAQ,特别是:

and perhaps the rest of the Array and Pointers section.

也许是阵列和指针部分的其余部分。