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};
int* b = a;
那么这样做有什么问题呢?int a[2][2]={1,2,3,4};
int** b = a;
C++ 报错,无法将 int[][]
转换为 int**
。如果 int[]
与 int*
相同,这两种类型有什么区别?
放轻松,这只是编译器错误。数组非常棘手。这里是一个规则:
类型为数组的变量的值会衰减(decay)为该数组的第一个元素的地址
你的第一段代码看起来像:
int a[2] = {1,2};
根据规则,如果a
出现在赋值语句的右侧,则会衰减为元素0的地址,这就是它为什么具有int *
类型的原因。这将带您到
int *b = a;
在第二个代码片段中,你实际上有的是一个数组嵌套另一个数组。(顺便说一下,为了更明确起见,我稍微改了一下你的代码。)int a[2][2]={{1,2},{3,4}};
这次a
将会退化为指向一个包含两个整数的数组的指针!因此,如果你想要将a
赋值给某个东西,你需要确保这个东西有相同的类型。
int (*b)[2] = a; //Huh!
(这种语法可能会让您感到惊讶,但请思考一下我们写的 int *b[2];
。明白了吗?b
将是一个指向整数的指针数组!这不是我们想要的……)
你可以在这里停止阅读,但你也可以继续看下去,因为我没有告诉你全部真相。我提到的规则有三个例外...
如果:
sizeof
的操作数&
的操作数那么数组的值不会衰减为元素零的地址。让我们通过例子详细解释这些例外情况:
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) */
最后
char a[] = "Hello world ";
这里不是复制了指向“Hello world”的指针,而是复制了整个字符串,并且 a 指向该副本。
这里的信息非常详细,一次性理解所有内容确实很难,所以请慢慢来。我建议您先阅读 K&R 上关于这个主题的内容,然后再阅读 这本 绝佳的书籍。
b
是有效的并且可以编译,但是为了使用它来访问 a
的元素,你需要指定边界,即 int (*b)[2] = a;
。 - AusCBlokeint[]
赋值给int*
,编译器会自动进行转换”(你在回答中的例子中进行了说明)。我不喜欢使用“decay”这个术语,因为访问int*
需要比访问int[]
更多的步骤。 - Timothy Jones这是一个经常出现的问题,我将尽力清晰地解释它。
当您创建一个数组时,它会将元素在内存中连续存储,因此:
int arr[2] = { 1, 2 };
Translates to:
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]
)指向sub1
,ptr + 1
(或&ptr[1]
)指向sub2
。尽管sub1
和sub2
在内存中可能不相邻且没有实际的关系,但由于它是指针的指针,所以即使内存结构不兼容,2D数组的双重解引用也得以保留。
类型为T
的数组会衰变成指向类型为T
的指针,但是类型为T
的数组的数组不会衰变成指向类型为T
的指针的指针,而是衰变成指向类型为T [n]
的数组的指针。因此,当我们的2D arr
衰变为指针时,它不是一个指向int
的指针,而是一个指向int [2]
的指针。该类型的完整名称为int (*)[2]
,为了使您的代码行起作用,您需要使用:
int (*ptr)[2] = arr;
// 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