使用带指针的2 D数组的4种不同方法,这是对的吗? 解释会有很大帮助
下面的四个程序输入一个二维数组,然后打印出来。
- 第一个打印出垃圾值,并发出一些警告(我不明白)。
- 第二个工作正常,因为我相信二维数组线性存储在内存中,它也有意义。
- 第3个工作正常,但我不知道为什么它的工作原理。
-
第四个也适用。
-
因此,如果有人能够解释每种方法的运作方式,那将会很有帮助
我担心我对指针如何工作的理解并不像我想象的那么好。
int main(){ int n; int a [3][4]; int i=0,j=0,count=0; for(i=0;i<3;i++){ for(j=0;j<4;j++){ scanf("%d",(a+4*i+j)); // Output-514623632 514623648 514623664 514623680 514623696 514623712 514623728 514623744 514623760 514623776 514623792 514623808 } } for(i=0;i<3;i++){ for(j=0;j<4;j++){ printf("%d\t",*(a+4*i+j)); } } return 0; } Warnings -solution.c:15:21: warning: format '%d' expects argument of type 'int *', but argument 2 has type 'int (*)[4]' [-Wformat=] scanf("%d",(a+4*i+j)); solution.c:20:22: warning: format '%d' expects argument of type 'int', but argument 2 has type 'int *' [-Wformat=] printf("%d\t",*(a+4*i+j)); ~^ ~~~~~~~~~ %ls
int main(){ int n; int a [3][4]; int i=0,j=0,count=0; for(i=0;i<3;i++){ for(j=0;j<4;j++){ scanf("%d",*(a+4*i+j)); } } for(i=0;i<3;i++){ for(j=0;j<4;j++){ printf("%d\t",**(a+4*i+j)); // Output -1 2 3 4 5 6 7 8 9 10 11 12 } } return 0; }
int main(){ int n; int a [3][4]; int i=0,j=0,count=0; for(i=0;i<3;i++){ for(j=0;j<4;j++){ scanf("%d",(*(a+i)+j)); } } for(i=0;i<3;i++){ for(j=0;j<4;j++){ printf("%d\t",*(*(a+i)+j)); // Output- 1 2 3 4 5 6 7 8 9 10 11 12 } } return 0; }
int main(){ int n; int * a=(int*)malloc(12*sizeof(int)) ; int i=0,j=0,count=0; for(i=0;i<3;i++){ for(j=0;j<4;j++){ *(a+4*i+j)=++count; } } for(i=0;i<3;i++){ for(j=0;j<4;j++){ printf("%d\n",*(a+4*i+j)); // Output- 1 2 3 4 5 6 7 8 9 10 11 12 } } return 0; }
它似乎归结为一个常见的误解,即“数组是一个指针” – 这不是真的。
真实的是,数组标识符在大多数上下文中评估指向第一个数组元素的指针 (这不包括例如&
和sizeof
运算符)。
现在来看看你的数组:
int a[3][4];
这里, a
是一个3元素数组,元素类型是int ()[4]
。 这直接解释了为什么您的第一个示例无法按预期工作。 在表达式中, a
求值为指向第一个元素的指针,因此类型为int (*)[4]
, 而不仅仅是int *
正如您所期望的那样。 因此, a+1
会将int
大小的4 int
加到指针值。 有了这个解释,你还应该理解你得到的警告。
另请注意,出于同样的原因,第二个例子是错误的。 添加的解除引用运算符使得使用的类型正确,但您仍然访问无效位置,因为指针算术仍然在int (*)[4]
。 当然不能保证“失败”,这只是未定义的行为。
使用2darrays的唯一正确版本是第三个示例:
(a+i)
是指向a的第i个元素的指针,取消引用它会产生一个int ()[4]
类型的数组,并且这会再次计算到指向其第一个元素的指针,因此向它添加j
会给你您所需价值的位置。
第四个例子不是一个二维数组,它是一个用作平面数组的动态分配,所以这里没有那么有意思。 如果你想动态分配一个二维数组,你必须写例如:
int (*a)[4] = malloc(3 * sizeof *a);
现在重新访问我的第一段:使用int a[3][4]
,你有(void *)a == (void *)*a
因为(void *)a == (void *)*a
的第一个元素开始于与(void *)a == (void *)*a
的第一个元素相同的位置a[0]
,只有a
和*a
的类型不同。 如果数组和指针是相同的,那么这是不可能的。
(a+4*i+j)
:编译器抱怨类型,因为a
不是int *
类型。 你将它声明为int a[3][4]
,它通常被读作尺寸为3×4的int的二维数组。 实际上, a
是由3个int组成的3个数组的数组。 然后a
是第一个4个整数数组的地址。 请记住,定义type v[N]
将v
定义为数组的第一个元素的地址,并且a
可以衰减为指向元素类型的指针。 然后在你的情况下衰减指向4个整数数组的指针。 这就是编译器抱怨的原因,因为%d
需要类型为int *
的相应参数。
即使地址可能是正确的,对它的算术也不是你想的。 a+3*i+j
表示从地址a
开始的4个第3个第4个数组的地址。
你可以强制衰减到int *
:
int *ta = (int *)a; // ta contains the same address but of different type... int i=0,j=0,count=0; for(i=0;i<3;i++){ for(j=0;j<4;j++){ scanf("%d",(ta+4*i+j)); // arithmetic is the right one (ints by ints) } } for(i=0;i<3;i++){ for(j=0;j<4;j++){ printf("%d\t",*(ta+4*i+j)); } }
对于第二个例子,这是相同的,算术是错误的。
第三和第四是正确的。 第三个因为你跳(用i)到正确的数组,然后跳到这个正确的int。
第四个几乎与我给出的代码相同。
- -编辑 - -
正如Felix在评论中所说,要小心,即使这在许多常见的架构/ OS /编译器中工作,也存在严格的别名规则,阻止你这样做。 阅读他提供的链接以获取更多信息。