Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are a, &a, *a, a[0], &a[0] and &a[0][0] identical pointers?

I have the following C program:

#include <stdio.h>

int main(){
    int a[2][2] = {1, 2, 3, 4};
    printf("a:%p, &a:%p, *a:%p \n", a, &a, *a);
    printf("a[0]:%p, &a[0]:%p \n", a[0], &a[0]);
    printf("&a[0][0]:%p \n", &a[0][0]);
    return 0;
}

It gives the following output:

a:0028FEAC, &a:0028FEAC, *a:0028FEAC
a[0]:0028FEAC, &a[0]:0028FEAC
&a[0][0]:0028FEAC

I am not able to understand why are &a, a, *a - all identical. The same for a[0], &a[0] and &a[0][0].

EDIT:

Thanks to the answers, I've understood the reason why these values are coming out to be equal. This line from the book by Kernighan & Ritchie turned out to be the key to my question:

 the name of an array is a synonym for the location of the initial element.

So, by this, we get

a = &a[0], and

a[0] = &a[0][0] (considering a as an array of arrays)

Intuitively, now the reason is clear behind the output. But, considering how pointers are implemented in C, I can't understand how a and &a are equal. I am assuming that there is a variable a in memory which points to the array(and the starting address of this array-memory-block would be the value of this variable a).

But, when we do &a, doesn't that mean taking the address of the memory location where the variable a was stored? Why are these values equal then?

like image 521
Koderok Avatar asked Aug 21 '13 15:08

Koderok


4 Answers

They're not identical pointers. They're pointers of distinct types that all point to the same memory location. Same value (sort of), different types.

A 2-dimensional array in C is nothing more or less than an array of arrays.

The object a is of type int[2][2], or 2-element array of 2-element array of int.

Any expression of array type is, in most but not all contexts, implicitly converted to ("decays" to) a pointer to the array object's first element. So the expression a, unless it's the operand of unary & or sizeof, is of type int(*)[2], and is equivalent to &a[0] (or &(a[0]) if that's clearer). It becomes a pointer to row 0 of the 2-dimensional array. It's important to remember that this is a pointer value (or equivalently an address), not a pointer object; there is no pointer object here unless you explicitly create one.

So looking at the several expressions you asked about:

  • &a is the address of the entire array object; it's a pointer expression of type int(*)[2][2].
  • a is the name of the array. As discussed above, it "decays" to a pointer to the first element (row) of the array object. It's a pointer expression of type int(*)[2].
  • *a dereferences the pointer expression a. Since a (after it decays) is a pointer to an array of 2 ints, *a is an array of 2 ints. Since that's an array type, it decays (in most but not all contexts) to a pointer to the first element of the array object. So it's of type int*. *a is equivalent to &a[0][0].
  • &a[0] is the address of the first (0th) row of the array object. It's of type int(*)[2]. a[0] is an array object; it doesn't decay to a pointer because it's the direct operand of unary &.
  • &a[0][0] is the address of element 0 of row 0 of the array object. It's of type int*.

All of these pointer expressions refer to the same location in memory. That location is the beginning of the array object a; it's also the beginning of the array object a[0] and of the int object a[0][0].

The correct way to print a pointer value is to use the "%p" format and to convert the pointer value to void*:

printf("&a = %p\n", (void*)&a);
printf("a  = %p\n", (void*)a);
printf("*a = %p\n", (void*)*a);
/* and so forth */

This conversion to void* yields a "raw" address that specifies only a location in memory, not what type of object is at that location. So if you have multiple pointers of different types that point to objects that begin at the same memory location, converting them all to void* yields the same value.

(I've glossed over the inner workings of the [] indexing operator. The expression x[y] is by definition equivalent to *(x+y), where x is a pointer (possibly the result of the implicit conversion of an array) and y is an integer. Or vice versa, but that's ugly; arr[0] and 0[arr] are equivalent, but that's useful only if you're writing deliberately obfuscated code. If we account for that equivalence, it takes a paragraph or so to describe what a[0][0] means, and this answer is probably already too long.)

For the sake of completeness the three contexts in which an expression of array type is not implicitly converted to a pointer to the array's first element are:

  • When it's the operand of unary &, so &arr yields the address of the entire array object;
  • When it's the operand of sizeof, so sizeof arr yields the size in bytes of the array object, not the size of a pointer; and
  • When it's a string literal in an initializer used to initialize an array (sub-)object, so char s[6] = "hello"; copies the array value into s rather than nonsensically initializing an array object with a pointer value. This last exception doesn't apply to the code you're asking about.

(The N1570 draft of the 2011 ISO C standard incorrectly states that _Alignof is a fourth exception; this is incorrect, since _Alignof can only be applied to a parenthesized type name, not to a expression. The error is corrected in the final C11 standard.)

Recommended reading: Section 6 of the comp.lang.c FAQ.

like image 168
Keith Thompson Avatar answered Oct 15 '22 16:10

Keith Thompson


Because all expressions are pointing to the beginning of the array:

a = {{a00},{a01},{a10},{a11}}

a points to the array, just because it is an array, so a == &a[0]

and &a[0][0] is positioned at the first cell of the 2D array.

like image 44
Tomer W Avatar answered Oct 15 '22 18:10

Tomer W


 +------------------------------+
 | a[0][0]   <--   a[0] <--   a | // <--&a, a,*a, &a[0],&a[0][0] 
 |_a[0][1]_                     |
 | a[1][0]   <--   a[1]         |
 | a[1][1]                      |
 +------------------------------+
like image 5
Lidong Guo Avatar answered Oct 15 '22 17:10

Lidong Guo


It is printing out the same values because they all are pointing to the same location.

Having said that,

&a[i][i] is of type int * which is a pointer to an integer.

a and &a[0] have the type int(*)[2] which indicates a pointer to an array of 2 ints.

&a has the type of int(*)[2][2] which indicates a pointer to a 2-D array or a pointer to an array of two elements in which each element is an array of 2-ints.

So, all of them are of different type and behave differently if you start doing pointer arithmetic on them.

(&a[0][1] + 1) points to the next integer element in the 2-D array i.e. to a[0][1]

&a[0] + 1 points to the next array of integers i.e. to a[1][0]

&a + 1 points to the next 2-D array which is non-existent in this case, but would be a[2][0] if present.

like image 5
Uchia Itachi Avatar answered Oct 15 '22 18:10

Uchia Itachi