在练习一些简单的数组和指针基础知识时,遇到了一些我不明白的事情:
在我能找到的所有书面资料中,他们说2D数组的名称实际上是2D指针, 意思是如果我写:
int a[3][4];
并声明一个指针:
int **d2;
它们都是相同的类型,我可以放心地分配:
d2 = a;
使指针指向第一行的开头。
但最令人惊讶的是,这行不通,我的问题是-为什么?
我会为您复制我收到的代码和警告:
#include <stdio.h>
int main() {
int **d2;
int a[5][2] = { {1, 2}, {3, 4}, {5, 7}, {7, 8}, {9, 11} };
d2 = a;
while (d2 < a + 2) {
printf("%d", **d2);
d2++;
}
return 0;
}
我得到此诊断信息:
warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
d=a;
^
ptr.c:11:9: warning: comparison of distinct pointer types lacks a cast
while(d<a+2)
答案 0 :(得分:4)
它们的内部结构完全不同,因此它们是不可互换的。
2D数组是这样的:
p[3][3]
|
v
+---------+---------+---------+---------+---------+---------+---------+
| p[0][0] | p[0][1] | p[0][2] | p[1][0] | p[1][1] | p[1][2] | ... |
+---------+---------+---------+---------+---------+---------+---------+
一个连续的空间。
指针是这样的:
**p
|
v
+------+ +---------+---------+---------+---------+---------+
| p[0] |-->| p[0][0] | p[0][1] | p[0][2] | p[0][3] | ... |
+------+ +---------+---------+---------+---------+---------+
| p[1] |-->| p[1][0] | p[1][1] | p[1][2] | p[1][3] | ... |
+------+ +---------+---------+---------+---------+---------+
| p[2] |-->| p[2][0] | p[2][1] | p[2][2] | p[2][3] | ... |
+------+ +---------+---------+---------+---------+---------+
| .... |
+------+
每个p[i]
可能指向大小不同的不同空间。
答案 1 :(得分:1)
int **d2;
不是2D指针,它是指向int
的指针的指针,有时也称为双指针。
int a[3][4];
定义了一个由3个4个int
组成的数组,也称为2D数组。
这2个对象的类型不兼容:这里没有int
指针变量,其地址可以存储在d2
中。
举个比喻:
int
就像一间拥有32个房间的房子; 二维数组中的行在内存中是连续的,因此您可以通过以下方式枚举数组中的条目:
#include <stdio.h>
int main() {
int *p;
int a[5][2] = { {1, 2}, {3, 4}, {5, 7}, {7, 8}, {9, 11} };
printf("iterating as a 2D array:\n");
p = &a[0][0];
for (int row = 0; row < 5; row++) {
for (int col = 0; col < 2; col++) {
printf("%d ", *p);
p++;
}
printf("\n");
}
printf("\n");
printf("iterating as a single array:\n");
p = &a[0][0];
while (p < &a[5][0]) {
printf("%d ", *p);
p++;
}
printf("\n");
return 0;
}
输出:
iterating as a 2D array: 1 2 3 4 5 7 7 8 9 11 iterating as a single array: 1 2 3 4 5 7 7 8 9 11
答案 2 :(得分:1)
int **d2
定义了一个指向int的指针。您也可以将其视为指向数组的指针,并且每个数组元素都包含一个指向int的指针。 ang敏杰的回答很好地显示了这一点。
数组int a[3][4];
是连续的内存块,因此等效于int a[3*4];
。如您所见,它不是指向行的指针数组。
请注意,编译器知道何时使用连续数组和指针数组,并使用正确的索引方法。例如:
void f(int **d2)
{
printf("%d", d2[3][4]);
}
void g(void)
{
int a[5][2] = { {1, 2}, {3, 4}, {5, 7}, {7, 8}, {9, 11} };
printf("%d", a[3][1]);
}
如您所见,即使基础表示形式完全不同,编写索引表达式的方式也相同。我知道这令人困惑。只要记住“编译器知道”即可。
答案 3 :(得分:1)
这是因为int **d2
是指向指针的指针。没有2d指针之类的东西。
例如,让我们考虑一维数组:
int a[2] = {1, 2};
现在,如果按以下方式分配,则整数指针将指向数组的第一个元素:
int *d = a;
谈论指针的指针时,指向指针的指针指向一个保存其他指针地址的内存位置!
例如,
// This will declare an integer pointer
int *a;
上面的指针将存储其他一些变量的地址。
例如,您可以为其分配值,如下所示:
int x = 5;
int *a = &x;
现在,这是一个指向指针的指针:
指向指针的指针保存其他指针的内存地址。
这意味着,在上面的示例中,我们可以声明一个指向指针的指针,该指针保存指针a
的地址:
int x = 5;
int *a = &x;
// A pointer to pointer
int **b = &a;
// To print value of x using b
printf("%d", **b);
上面的代码可以理解如下:
希望这可以消除您对指针的困惑。
现在,针对您的用例,如果您想要一个指针指向您的2d整数数组,则只需使用一个指针并为其分配数组第一个元素的地址即可。可以按照以下步骤进行操作:
int a[2][2] = {{1, 2}, {3, 4}};
// Declare a pointer that points to first element of array
int *b = &a[0][0];
现在,如果要使用指针访问此数组的元素a[i][j]
,可以将其作为b + 2*i + j
进行访问。
通常,如果数组的大小为p*q
,则可以使用a[i][j]
访问元素b + q * i + j
。
例如,要使用2个for循环打印数组,
int rows = 2;
int cols = 3;
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
int *b = &a[0][0];
for(int i=0; i<rows; i++) {
for(int j=0; j<cols; j++) {
// Print a[i][j] using pointer b
printf("%d ", *(b + cols * i + j));
}
}
// 1 2 3 4 5 6
您还可以使用单循环,
int rows = 2;
int cols = 3;
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
int *b = &a[0][0];
for(int i=0; i<rows*cols; i++) {
printf("%d ", *(b + i));
}
// 1 2 3 4 5 6