Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

arrays

c

pointers

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?

like image 903
octavian Avatar asked Oct 17 '25 23:10

octavian


2 Answers

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.

like image 97
John Bode Avatar answered Oct 19 '25 12:10

John Bode


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
like image 37
emsworth Avatar answered Oct 19 '25 12:10

emsworth