简体   繁体   中英

C assigning the address of a 2D array to a pointer

I was reading through some lecture notes that in order for a pointer to reference a 2D array, it has to be given the address of the first element.

int a[10][10];
int *p = &a[0][0];

I've never tried this, so I was curious why isn't it enough to assign the array itself to the pointer, just as we do in a 1D case.

int a[10][10];
int *p = a;

The array is kept in an uninterrupted 'line' of memory anyway, and 2D arrays only have a different type, but the same structure as 1D arrays.

By doing this

int *p = &a[0][0];

I don't see how we give the pointer any more information than by doing this

int *p = a;

Or maybe all arrays regardless of their number of dimensions have the same type, the only difference being that multidimensional arrays store their extra dimensions before their first element and we need to jump over those memory spaces which remember sizes of an array's dimensions?

First, some background:

Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T " will be converted ("decay") to an expression of type "pointer to T ", and the value of the expression will be the address of the first element of the array.

Given the declaration

int a[10][10];

the expression a has type "10-element array of 10-element array of int ". Unless this expression is the operand of the sizeof or unary & operators, it will be converted to an expression of type "pointer to 10-element array of int ", or int (*)[10] .

Given that declaration, all of the following are true:

    Expression    Type                Decays to
    ----------    ----                ---------
             a    int [10][10]        int (*)[10]      
            &a    int (*)[10][10]     
            *a    int [10]            int *
          a[i]    int [10]            int *
         &a[i]    int (*)[10]         
         *a[i]    int          
       a[i][j]    int
      &a[i][j]    int *

Also,

    sizeof a        == sizeof (int) * 10 * 10
    sizeof &a       == sizeof (int (*)[10][10])
    sizeof *a       == sizeof (int) * 10
    sizeof a[i]     == sizeof (int) * 10
    sizeof &a[i]    == sizeof (int (*)[10] )
    sizeof *a[i]    == sizeif (int)
    sizeof a[i][j]  == sizeof (int)
    sizeof &a[i][j] == sizeof (int *)

Note that the different pointer types int (*)[10][10] , int (*)[10] , and int * don't have to be the same size or have the same representation, although on the platforms I'm familiar with they do.

The address of the first element of the array is the same as the address of the array itself; thus, all of a , &a , a[0] , &a[0] , and &a[0][0] will yield the same value , but the types will be different (as shown in the table above).

So, assume we add the following declarations:

int           *p0 = &a[0][0]; // equivalent to int       *p0 = a[0];
int     (*p1)[10] = &a[0];    // equivalent to int (*p1)[10] = a;
int (*p2)[10][10] = &a;

All of p0 , p1 , and p2 initially have the same value, which is the address of the first element in a ; however, because of the different pointer types, the results operations involving pointer arithmetic will be different. The expression p0 + 1 will yield the address of the next int object ( &a[0][1] ). The expression p1 + 1 will yield the address of the next 10-element array of int ( &a[1][0] ). And finally, the expression p2 + 1 will yield the address of the next 10-element array of 10-element array of int (effectively, &a[11][0] ).

Note the types of p1 and p2 ; neither is a simple int * , because the expressions being used to initialize them are not that type (refer to the first table).

Note the pattern; for an array type, the simpler the expression, the more complicated the corresponding type will be. The expression a does not refer to a single int object; it refers to a 10x10 array of int objects, so when it appears in an expression, it is treated as a pointer to an array of integers, not a pointer to a single integer.

The compiler knows that "a" is a pointer to ten integers. If you don't declare the dimensions, then the compiler sees the new pointer as a pointer to an unknown number of integers. This will work in your case, but it will generate a compiler warning because the compiler sees them as incompatible pointers. The syntax for what you are trying to do (without generating a compiler warning) is:

int a[10][10];
int *p1 = &a[0][0];
int (*p2)[10] = a;
printf("p1: %p p2: %p\n", p1, p2);

One reason this is important is pointer arithmetic:

p1++; //move forward sizeof(int) bytes
p2++; //move forward sizeof(int) * 10 bytes

You understanding is close, the difference is the type information. Pointer does has its type. For example int* p, the pointer type is int*, as int a[10][10], the corresponding pointer type is int *[10][10].

In your example, p and a do point to the same address, but they're different type, which matters when perform arithmetic operation on them.

Here's an example from this URL

Suppose now that we define three pointers :

char *mychar;
short *myshort;
long *mylong;

and that we know that they point to the memory locations 1000, 2000, and 3000, respectively.

Therefore, if we write:

++mychar;

++myshort;

++mylong;

mychar, as one would expect, would contain the value 1001. But not so obviously, myshort would contain the value 2002, and mylong would contain 3004, even though they have each been incremented only once. The reason is that, when adding one to a pointer, the pointer is made to point to the following element of the same type, and, therefore, the size in bytes of the type it points to is added to the pointer.

You are right, you can assign the array itself to the pointer:

int a[10][10] = {[0][0]=6,[0][1]=1,[1][0]=10,[1][1]=11};
int b[10][10][10] = {[0][0][0]=8,[0][0][1]=1,[0][1][0]=10,[1][0][0]=100};

int *p, *q, *r, *s;

p = &a[0][0];   
q = a;      // what you are saying  

r = &b[0][0][0];    
s = b;      // what you are saying


printf("p= %p,*p= %d\n",p,*p);
printf("q= %p,*q= %d\n",q,*q);

printf("r= %p,*r= %d\n",r,*r);
printf("s= %p,*s= %d\n",s,*s);

And the output is:

p= 0xbfdd2eb0,*p= 6
q= 0xbfdd2eb0,*q= 6
r= 0xbfdd3040,*r= 8
s= 0xbfdd3040,*s= 8

They point to the same address, regardless of the dimension of the matrix. So, what you are saying is right.

Well in 2D array, the outcome of *a and a is the same, they all point to the first address of this 2D array!

But if you want to define a pointer to point to this array, you could use int (*ptr)[10] for example.

You are right, 1D and 2D share the same structure, but 2D has some additional manipulation on pointers like above.

So all in all, in 2D array, a , *a and &a[0][0] prints the same address, but their usages may vary.

Like this:

#include<stdio.h>

int main() {
  int a[10][10];
  int *pa1 = &a[0][0];
  int *pa2 = *a;
  printf("pa1 is %p\n", pa1);
  printf("pa2 is %p\n", pa2);
  printf("Address of a is %p\n", a);

  // pointer to array
  int (*pa3)[10];
  pa3 = a;
  printf("pa3 is %p\n", pa3);

  return 0;
}

They print the same address.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM