Deepak D
Deepak D

Reputation: 11

4 different ways of using 2 D arrays with pointers , which is right? An explanation would help a lot

The four programs below input a 2 D array and then print it out.

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;
}

Upvotes: 0

Views: 86

Answers (2)

user2371524
user2371524

Reputation:

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.

Upvotes: 3

Jean-Baptiste Yun&#232;s
Jean-Baptiste Yun&#232;s

Reputation: 36401

(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.

Upvotes: 1

Related Questions