使用带指针的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 /编译器中工作,也存在严格的别名规则,阻止你这样做。 阅读他提供的链接以获取更多信息。