14

I have a struct that looks like this:

struct packet {
  int a;
  char data[500];
};
typedef struct packet packet_t;

I'm a little confused why the following code outputs the same address for each printf:

void myfunction() {
  packet_t packet;
  printf("%p\n", packet.data);   //e.g., outputs 0x7fff1c323c9c
  printf("%p\n", &packet.data);  //e.g., outputs 0x7fff1c323c9c
}

Does anyone have a good explanation for this?

Alec
  • 470
  • 3
  • 10
  • Do you mean "why do the two lines give the same result", or "why are the results the same for each function call"? The former is easy (because the pointers point to the same thing), the latter is impossible to tell. – Kerrek SB Oct 09 '11 at 23:11
  • 2
    If you refer to an array by name without using brackets, it'll masquerade as a pointer to the first element. – Sam Dufel Oct 09 '11 at 23:12
  • possible duplicate of ["Address of" (&) an array / address of being ignored be gcc?](http://stackoverflow.com/questions/2893911/address-of-an-array-address-of-being-ignored-be-gcc) – Caleb Oct 09 '11 at 23:28

6 Answers6

15

Under most circumstances, an expression that has type "N-element array of T" will be converted to an expression of type "pointer to T", and its value will be the address of the first element in the array. This is what happens in the first printf call; the expression packet.data, which has type char [500], is replaced with an expression of type char *, and its value is the address of the first element, so you're effectively printing &packet.data[0].

One exception to this rule occurs when the array expression is an operand of the unary & operator; the type of the expression &packet.data is char (*)[500] (pointer to 500-element array of char).

The address of an array is the same as the address of the first element, so both calls to printf display the same value; it's just that the types of the expressions are different. To be pedantic, both expressions should be cast to void * in the printf calls (the %p conversion specifier expects a void * argument):

printf("%p\n", (void *) packet.data);
printf("%p\n", (void *) &packet.data);
John Bode
  • 119,563
  • 19
  • 122
  • 198
4

That's because array decays to a pointer pointing to first element in the sequence. So, packet.data address location is same as &packet.data or &packet.data[0].

Mahesh
  • 34,573
  • 20
  • 89
  • 115
  • 5
    The two are different types, though: the former is a pointer to an array, the latter a pointer to a char. The two just happen to be at the same numeric address. – Kerrek SB Oct 09 '11 at 23:20
3

According to C11 (6.3.2.1.3) sections. Arrays decays to pointer to its first element except when they are used as operand of sizeof and unary & operators.

Thus packet.data does not decay to &packet.data[0] in expression &packet.data. The result will be a pointer of type char (*)[500] and it will be numerically the same as the address of its first element which is packet.data[0].

tstanisl
  • 13,520
  • 2
  • 25
  • 40
0

While existing answers are already technically correct and also includes the relevant C ISO standard section, I feel like it’s missing the reason why any of this matters at all.

Since the question is about the difference, I will try to provide a practical difference.

Consider:

  1 #include <stdio.h>
  2 
  3 int main(void)
  4 {
  5   char s[10];
  6 
  7   char *p;
  8 
  9   p = s;
 10 
 11   p = &s; // this will give a compiler warning
 12 
 13   printf("%p %p %p\n", s, p, &s);
 14 
 15   printf("%p %p %p\n", s + 1, p + 1, &s + 1);
 16 
 17   return 0;
 18 }

Compile and run:

$ gcc array.c -o array && ./array
array.c: In function 'main':
array.c:11:5: warning: assignment from incompatible pointer type [-Wincompatible-pointer-types]
   p = &s;
     ^
0x7f7ffff0622e 0x7f7ffff0622e 0x7f7ffff0622e
0x7f7ffff0622f 0x7f7ffff0622f 0x7f7ffff06238

There are two things to note here:

  1. The compiler will emit an error for the incompatible pointer types (already explained in other answers). So in this way, they are not only academically different, but also tangibly different (type wise).

  2. The pointer arithmetic for (char *) and (char (*)[10]) is different. In the first printf line we can see that all the different variables do point to the same address. However, their values diverge when performing pointer arithmetic. In the case of (char *), the pointer arithmetic works as expected, adding 1 to the variable will increase the address by 1. In the case of (char (*)[10]), adding 1 to the variable will increase the address by 10.

-1

Because it's the only reasonable thing to do besides making &packet.data cause a compile error. Integer a and the data char array are laid out sequentially in the stack, there is no dereferencing involved.

Forrest Voight
  • 2,249
  • 16
  • 21
-2

I don't know why this was voted down, it's a good question that exposes a confusing behaviour of C.

The confusion comes because normally when you define an array a real pointer is created:

char data[100];
printf("%p\n", data);    // print a pointer to the first element of data[]
printf("%p\n", &data);   // print a pointer to a pointer to the first element of data[]

So on a typical 32 bit desktop system 4 bytes are allocated for data, which is a pointer to 100 chars. Data, the pointer, itself exists somewhere in memory.

When you create an array in a struct no pointer is allocated. Instead the compiler converts references to packet.data into a pointer at runtime but does not allocate any memory for storing it. Rather it just uses &packet + offsetof(data).

Personally I would prefer the syntax to be consistent and require an ampersand, with packet.data generating some kind of compile time error.

  • 2
    This answer might be incorrect. I tried running the code (gcc 4.8) and it had the exact same behavior as the OP's code in both C and C++; i.e. "&arr" is the same as &(arr[0]) regardless of whether the array is declared separately or as a member of a struct. – Emily Chen Jan 13 '18 at 23:56
  • @EmilyChen is right. Specifically, the code snippet in this answer that does the same thing as the question but without the struct still prints the same address twice. So there does not seem to be anything special about how structs store arrays in terms of how it relates to the question. `&data` where `data` is not in a struct is not a pointer to a pointer to the first element, it is just a pointer to the first element, and is the same as `data` when both are cast to `(void*)`. – nog642 May 22 '19 at 03:59
  • When you just call `printf("%p", data);` where `data` is an array, no pointer would be allocated _in memory_ at all. The address of the first element of `data` is kept in a register. When you don't define a (pointer) variable to store the address, the compiler is not required to keep the address in memory. (You could see this behavior even when optimization is turned off). – Explorer09 Jun 18 '21 at 01:13
  • 3
    This answer is *totally wrong*: John Bode's answer below is the correct answer, and tstanisl's answer cites the relevant part of the C11 standard. – Keeley Hoek Jul 25 '21 at 14:07