问题
The four programs below input a 2 D array and then print it out.
- The first one prints out garbage values and also gives out a few warnings (Which I did not understand).
- The 2nd one works correctly , also it kinda makes sense as I believed that a 2 D array is stored linearly in memory.
- The 3rd one works correctly , but I have no idea why it works.
The 4th one works as well.
So it would be of great help if someone could explain how each of the methods work , cheers
I'm afraid my understanding of how pointers work isn't as good as I thought.
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;
}
回答1:
It seems to boil down to a common misconception reading "an array is a pointer" -- this is not true.
What's true is that an array identifier evaluates to a pointer to the first array element in most contexts (this excludes e.g. the &
and sizeof
operators).
Now have a look at your array:
int a[3][4];
Here, a
is a 3 elements array and the element type is int ()[4]
. This directly explains why your first example doesn't work as expected. In your expression, a
evaluates to a pointer to the first element, so the type is int (*)[4]
and not just int *
as you seem to expect. Therefore, a+1
would add 4 times the size of an int
to the pointer value. With this explanation, you should also understand the warnings you're getting.
Also note that for the same reason, the second example is wrong. The added dereferencing operator makes the types used correct, but you're still accessing invalid locations as your pointer arithmetics still operate on int (*)[4]
. Of course there's no guarantee to "fail", it's just undefined behavior.
The only correct version using a 2d-array is the third example:
(a+i)
is a pointer to the i'th element of a
, dereferencing it yields an array of type int ()[4]
and this again evaluates to a pointer to its first element, so adding j
to it gives you the location of your desired value.
The fourth example isn't a 2d array, it's a dynamic allocation used as a flat array, so it's not that interesting here. If you wanted to dynamically allocate a 2d-array, you'd have to write for example:
int (*a)[4] = malloc(3 * sizeof *a);
Re-visiting my first paragraph now: With int a[3][4]
, you have (void *)a == (void *)*a
because the first element of a
starts at the same location as the first element of a[0]
, only the types of a
and *a
differ. This wouldn't be possible if arrays and pointers were the same thing.
回答2:
(a+4*i+j)
: compiler complains about type because a
is not of type int *
. You declared it as int a[3][4]
which is commonly read as 2D array of ints of dimension 3x4. In real, a
is an array of 3 arrays of 4 ints. Then a
is the address of the first array of 4 ints. Remember that defining type v[N]
defines v
as the address of the first element of the array, and that a
can decay to pointer to element type. Then in your case a
decays to a pointer to array of 4 ints. This is why the compiler complains as %d
needs a corresponding argument of type int *
.
Even if the address could be correct, arithmetic on it is not what you think. a+3*i+j
means the address of the 3*i+j-th array of 4 ints starting from address a
.
You can force to decay to 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));
}
}
This is the same for second example, arithmetic is the wrong one.
The third and fourth are correct. The third because you jump (with i) to the correct array and then jump in this one to the correct int.
The fourth is almost the same as the code I gave.
---EDIT---
As Felix said in comments, be careful that even if this work in many common architecture/OS/compiler there is a strict aliasing rule that prevents you doing so. Read the link he provided for more.
来源:https://stackoverflow.com/questions/46488768/4-different-ways-of-using-2-d-arrays-with-pointers-which-is-right-an-explanat