使用带指针的二维数组的 4 种不同方式,哪个是正确的?一个解释会有很大帮助
4 different ways of using 2 D arrays with pointers , which is right? An explanation would help a lot
下面四个程序输入一个二维数组然后打印出来
- 第一个打印出垃圾值并发出一些警告
(我不明白)。
- 第二个工作正常,也有点道理,因为我相信
2维数组在内存中线性存储。
- 第三个工作正常,但我不知道为什么会工作。
第 4 个也可以。
所以,如果有人能解释一下每种方法是如何工作的,那将是非常有帮助的
工作,干杯
恐怕我对指针工作原理的理解不如我想象的那么好。
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;
}
这似乎归结为一种常见的误解阅读 "an array is a pointer" -- 这是 不 正确的。
在大多数情况下,数组标识符 评估为指向第一个数组元素 的指针是正确的(这不包括 &
和 sizeof
运算符)。
现在看看你的数组:
int a[3][4];
这里,a
是一个3元素数组,元素类型是int ()[4]
。这直接解释了为什么您的第一个示例没有按预期工作。在您的表达式中, a
计算为指向第一个元素的指针,因此类型是 int (*)[4]
而 而不是 只是 int *
正如您所期望的那样.因此,a+1
会将指针值的大小增加到 int
的 4 倍。有了这个解释,您应该也理解了您收到的警告。
还要注意,同理,第二个例子是错误的。添加的取消引用运算符使使用的类型正确,但您仍然访问无效位置,因为您的指针算法仍在 int (*)[4]
上运行。当然不能保证 "fail",这只是未定义的行为。
使用二维数组的唯一正确版本是第三个示例:
(a+i)
是指向 a
的第 i 个元素的指针,取消引用它会产生类型为 int ()[4]
的数组,这再次计算为指向其第一个元素的指针,所以向它添加 j
可以为您提供所需值的位置。
第四个例子不是一个二维数组,它是一个用作平面数组的动态分配,所以这里不是很有趣。如果你想动态分配一个二维数组,你必须写例如:
int (*a)[4] = malloc(3 * sizeof *a);
现在重温我的第一段:使用 int a[3][4]
,你有 (void *)a == (void *)*a
,因为 a
的第一个元素与 [=29] 的第一个元素从相同的位置开始=],只有 a
和 *a
的 类型 不同。如果数组和指针是一回事,这是不可能的。
(a+4*i+j)
:编译器抱怨类型,因为 a
不是 int *
类型。您将其声明为 int a[3][4]
,通常将其读作 3x4 维整数的二维数组。实际上,a
是一个由 3 个 4 个整数组成的数组。那么a
就是第一个4个int数组的地址。请记住,定义 type v[N]
会将 v
定义为数组第一个元素的地址,并且 a
可以衰减为指向元素类型的指针。然后在你的情况下 a
衰减到指向 4 个整数数组的指针。这就是编译器抱怨 %d
需要类型为 int *
.
的相应参数的原因
就算地址是对的,算起来也不是你想的那样。 a+3*i+j
表示从地址 a
.
开始的第 3*i+j 个 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 在评论中所说,请注意,即使这在许多常见的情况下有效 architecture/OS/compiler 也有严格的别名规则阻止您这样做。阅读他提供的 link 以获得更多信息。
下面四个程序输入一个二维数组然后打印出来
- 第一个打印出垃圾值并发出一些警告 (我不明白)。
- 第二个工作正常,也有点道理,因为我相信 2维数组在内存中线性存储。
- 第三个工作正常,但我不知道为什么会工作。
第 4 个也可以。
所以,如果有人能解释一下每种方法是如何工作的,那将是非常有帮助的 工作,干杯
恐怕我对指针工作原理的理解不如我想象的那么好。
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;
}
这似乎归结为一种常见的误解阅读 "an array is a pointer" -- 这是 不 正确的。
在大多数情况下,数组标识符 评估为指向第一个数组元素 的指针是正确的(这不包括 &
和 sizeof
运算符)。
现在看看你的数组:
int a[3][4];
这里,a
是一个3元素数组,元素类型是int ()[4]
。这直接解释了为什么您的第一个示例没有按预期工作。在您的表达式中, a
计算为指向第一个元素的指针,因此类型是 int (*)[4]
而 而不是 只是 int *
正如您所期望的那样.因此,a+1
会将指针值的大小增加到 int
的 4 倍。有了这个解释,您应该也理解了您收到的警告。
还要注意,同理,第二个例子是错误的。添加的取消引用运算符使使用的类型正确,但您仍然访问无效位置,因为您的指针算法仍在 int (*)[4]
上运行。当然不能保证 "fail",这只是未定义的行为。
使用二维数组的唯一正确版本是第三个示例:
(a+i)
是指向 a
的第 i 个元素的指针,取消引用它会产生类型为 int ()[4]
的数组,这再次计算为指向其第一个元素的指针,所以向它添加 j
可以为您提供所需值的位置。
第四个例子不是一个二维数组,它是一个用作平面数组的动态分配,所以这里不是很有趣。如果你想动态分配一个二维数组,你必须写例如:
int (*a)[4] = malloc(3 * sizeof *a);
现在重温我的第一段:使用 int a[3][4]
,你有 (void *)a == (void *)*a
,因为 a
的第一个元素与 [=29] 的第一个元素从相同的位置开始=],只有 a
和 *a
的 类型 不同。如果数组和指针是一回事,这是不可能的。
(a+4*i+j)
:编译器抱怨类型,因为 a
不是 int *
类型。您将其声明为 int a[3][4]
,通常将其读作 3x4 维整数的二维数组。实际上,a
是一个由 3 个 4 个整数组成的数组。那么a
就是第一个4个int数组的地址。请记住,定义 type v[N]
会将 v
定义为数组第一个元素的地址,并且 a
可以衰减为指向元素类型的指针。然后在你的情况下 a
衰减到指向 4 个整数数组的指针。这就是编译器抱怨 %d
需要类型为 int *
.
就算地址是对的,算起来也不是你想的那样。 a+3*i+j
表示从地址 a
.
你可以强制衰减到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 在评论中所说,请注意,即使这在许多常见的情况下有效 architecture/OS/compiler 也有严格的别名规则阻止您这样做。阅读他提供的 link 以获得更多信息。