views:

124

answers:

5

Hi, I am a teaching assistant of a introductory programming course, and some students made this type of error:

char name[20];
scanf("%s",&name);

which is not surprising as they are learning... What is surprising is that, besides gcc warning, the code works (at least this part). I have been trying to understand and I wrote the following code:

void foo(int *v1, int *v2) {
  if (v1 == v2)
    printf("Both pointers are the same\n");
  else
    printf("They are not the same\n");
}

int main() {
  int test[50];
  foo(&test, test);
  if (&test == test)
    printf("Both pointers are the same\n");
  else
    printf("They are not the same\n");
}

Compiling and executing:

$ gcc test.c -g
test.c: In function ‘main’:
test.c:12: warning: passing argument 1 of ‘foo’ from incompatible pointer type
test.c:13: warning: comparison of distinct pointer types lacks a cast
$ ./a.out 
Both pointers are the same
Both pointers are the same

Can anyone explain why they are not different?

I suspect it is because I cannot get the address of an array (as I cannot have & &x), but in this case the code should not compile.

Edit: I know that an array by itself is the same as the address of the first element, but this is not related to this problem, I think. For example:

int main() {
  int a[50];
  int * p = a;
  printf("%d %d %d\n", p == a, p == &a[0], &p[0] == a);
  printf("%d %d %d\n", p == &a, &p == a, &p == &a);
}

prints:

$ ./a.out 
1 1 1
1 0 0

I don't understand why the second line begins with 1.

+6  A: 

Actually, they are different, they don't have the same type at least.

But in C, the address of the array is the same as the address of the first element in the array that's why "they are not different", basically, they point to the same thing.

Soufiane Hassou
dbarbosa
@WhirlWind: No, it's *not* system-dependent. Applying the address-of operator to an array has well-defined semantics - it must produce the address of the array itself. The array is defined as a consecutive sequence of objects of the base type, and thus the address of the array and the address of the first object are the same place (but with different types).
caf
A: 

I believe this is a gcc optimization. Think about it.

  • &test points to the address of test
  • test points to the first element of test or &test[0]
  • [0] is the same(for the most part) as *

So according to this &test could be different than test but gcc optimizes this away because there is no purpose of having an extra level of indirection at that point.

Earlz
It's not a gcc optimisation - it is behaviour that is required by the C standard.
caf
dbarbosa
@dba wow I was. The accepted answer for this completely clears it up
Earlz
+1  A: 

If You define an array like

char name[20];

name is implicitly convertible to char*, but &name is of the type char (*)[20] (a pointer to an array of 20 characters). The addresses are the same.

Check the address of (&name + 1). It differs form &name by the sizeof(char [20]).

Maciej Hehl
Thanks, you made it easier to understand James McNellis answer.
dbarbosa
The pleasure is all mine :)
Maciej Hehl
+2  A: 

The name of an array, in most circumstances, evaluates to the address of its initial element. The two exceptions are when it is the operand of sizeof or the unary &.

The unary & gives the address of its argument. The address of an array is the same as the address of its initial element, so (void*)&array == (void*)array will always be true.

array, when converted to a pointer to its initial element, has the type T *. The type of &array is T (*)[n], where n is the number of elements in the array. Thus,

int* p = array;        // Works; p is a pointer to array[0]
int* q = &array;       // Doesn't work; &array has type int (*)[10]
int (*r)[10] = &array; // Works; r is a pointer to array
James McNellis
Thank you, I only knew the exception of sizeof.
dbarbosa
+8  A: 

In your example, the array test is a block of 50 ints. So it looks like this:

| int | int | ... | int |

When you apply the unary & operator to an array, you get the address of the array. Just like when you apply it to anything else, really. So &test is a pointer that points to that block of 50 ints:

(&test) -----------> | int | int | ... | int |

A pointer that points to an array of 50 ints has type int (*)[50] - that's the type of &test.

When you just use the name test in any place where it's not the operand of either the sizeof or unary-& operators, it is evaluated to a pointer to its first element. So the test that you pass to foo() evaluates to a pointer to the test[0] element:

(test) -----------------\
                        v
(&test) -----------> | int | int | ... | int |

You can see that these both are pointing to the same address - although &test is pointing to the whole array, and test is pointing to the first element of the array (which only shows up in the different types that those values have).

caf
+1; sometimes a picture is worth 1,000 words :-)
James McNellis
Johannes Schaub - litb
After that I had to change the "accepted answer"!
dbarbosa