3

I have maybe a very naive question (I am no expert in C programming), but I couldn't get a fully satisfactory explanation. Here is just the declaration of static array and a few prints:

#include <stdlib.h>
#include <stdio.h>
void main() {
    int N=3, a[N];
    for (int i=0; i<N; i++) a[i] = 1000+i;
    printf("&a    = %p\n",&a);
    printf("a     = %p\n",a);
    printf("*a    = %d\n",*a);
    printf("*(&a) = %d   (as an int)\n",*(&a));
    printf("*(&a) = %p\  (as a pointer)\n",*(&a));
}

The output is:

&a    = 0x7ffee9043ae0
a     = 0x7ffee9043ae0
*a    = 1000
*(&a) = -319989024   (as an int)
*(&a) = 0x7ffee9043ae0  (as a pointer)

Since &a and a are identical, showing the same address in memory, I was first expecting *(&a) and *a being identical as well, both equal to 1000.

Then I thought about the types: a is apparently considered as an int*, so &a is a int**. It turns that *a is an int, while *(&a) is an int*: they are not of the same type, the latter is a pointer.

It makes sense... But my question is then: why are &a and a identical in the first place?

PierU
  • 1,391
  • 3
  • 15

3 Answers3

2

When an array is used in an expression, in most cases it decays to a pointer to its first element. This means that a and &a[0] both give you the address of the first element of the array.

why are &a and a identical in the first place

Because the address of an array is the same as the address of its first element, and since a decays to &a[0], &a and &a[0] are the same address. These expressions however have different types.

I said that in most cases an array name decays to a pointer to its first element. &a is not one of those cases. So the type of &a is int (*)[3] i.e. a pointer to an array of size 3 of int, and the type of a in an expression decays to &a[0] is int *.

dbush
  • 205,898
  • 23
  • 218
  • 273
2

The variable a according to its declaration has the array type a[N] that is a[3]. It is a variable length array.

Arrays used in expressions with rare exceptions are converted to pointers to their first elements.

From the C Standard (6.3.2.1 Lvalues, arrays, and function designators)

3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

So this call of printf

printf("a     = %p\n",a);

in fact is the same as

printf("a     = %p\n",&a[0]);

The expression &a has the pointer type int ( * )[N] (not int **).

These two calls of printf

printf("&a    = %p\n",&a);
printf("a     = %p\n",a);

yield the same value because the address of an array as whole and the address of the first element of the array are equal each other and represent the address of the extent of memory allocated for the array. Though as it was mentioned the expressions have different types

The expression *( &a ) is equivalent to the expression a. So this call of printf

printf("*(&a) = %d   (as an int)\n",*(&a));

is equivalent to

printf("*(&a) = %d   (as an int)\n",a);

and invokes undefined behavior because there is used the invalid format specification %d with a pointer.

And the output of this call of printf

printf("*(&a) = %p\  (as a pointer)\n",*(&a))

yields the same value

a     = 0x7ffee9043ae0
*(&a) = 0x7ffeeced5ae0  (as a pointer)

as this call of printf

printf("a     = %p\n",a);
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • I'm starting getting the global idea (and this looks like a kind of trick of the language). However some details are still unclear. I understand there's no pointer decay with the `&` operator, and you say that `*(&a)` is hence equivalent to `a`. But in my exemple the printed values of `*(&a)` and `a` differ. – PierU Nov 14 '22 at 10:14
  • @PierU I can not reproduce. The both calls of printf the conversion specifier &p output teh same address for the expressions a and *&a. – Vlad from Moscow Nov 14 '22 at 10:23
  • OK I'll check this evening, maybe a copy/paste mistake between different runs – PierU Nov 14 '22 at 10:42
  • Indeed it was probably a copy/paste problem, I've rerun the code and the two prints are always identical – PierU Nov 14 '22 at 18:49
0

But my question is then: why are &a and a identical in the first place?

Since pointer &a points to the same address as array a (after its conversion to the address of its first element), they will report as equivalent pointers when properly printed.

In other words: the array and the first array element both begin in the same place.


Deeper

"&a and a are identical"

They have different types, as OP has noted. The values of both are specified as equivalent: they compare equal. Yet C allows them to have different encodings (bit patterns) and even different sizes. Such novel addressing is not seen much these days.


Technically all the printf() of OP's code with "%p" are undefined behavior (UB) and that specifier is for void *. Best to cast to void * first.

  // Improper
  printf("&a    = %p\n",&a);
  // Correct
  printf("&a    = %p\n", (void *) &a);

The usually UB is benign.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256