17

GCC gives me an 'Initialization from incompatible pointer type' warning when I use this code (though the code works fine and does what it's supposed to do, which is print all the elements of the array).

#include <stdio.h>

int main(void)
{
    int arr[5] = {3, 0, 3, 4, 1};
    int *p = &arr;

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

    for (int a = 0; a < 5; a++)
        printf("%d ", *(p++));
    printf("\n");
}

However no warning is given when I use this bit of code

int main(void)
{
    int arr[5] = {3, 0, 3, 4, 1};
    int *q = arr;

    printf("%p\n%p\n\n", q);

    for (int a = 0; a < 5; a++)
        printf("%d ", *(q++));
    printf("\n");
}

The only difference between these two snippets is that I assign *p = &arr and *q = arr .

  • Exactly what different is the & making ?
  • And why does the code execute and give the exact same output in both the cases ?
Nathu
  • 449
  • 1
  • 4
  • 11
  • 1
    Array type is compatible with ([decays into](https://stackoverflow.com/questions/1461432/what-is-array-decaying); but is not equivalent to) pointer to array item, thus no problem in second case. – spectras Jun 13 '17 at 14:06
  • 1
    It is good practice to compile C code with `gcc -std=c11 -pedantic-errors`, particularly when learning. This will prevent you from executing code that isn't valid C. – Lundin Jun 13 '17 at 14:24

5 Answers5

23
  • &arr gives an array pointer, a special pointer type int(*)[5] which points at the array as whole.
  • arr, when written in an expression such a int *q = arr;, "decays" into a pointer to the first element. Completely equivalent to int *q = &arr[0];

In the first case you try to assign a int(*)[5] to a int*. These are incompatible pointer types, hence the compiler diagnostic message.

As it turns out, the array pointer and the int pointer to the first element will very likely have the same representation and the same address internally. This is why the first example "works" even though it is not correct C.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • That makes sense. Thanks ! – Nathu Jun 13 '17 at 15:06
  • "In the first case you try to assign a int(*)[5] to a int*. These are incompatible pointer types, hence the compiler diagnostic message." ----What does it mean ? please provide simple explanation. – Whois7pi Mar 18 '23 at 19:53
  • @Whois7pi The only pointer types in C that can get assigned to each other without casts are pointers to exactly the same type (as well as the special cases of void pointers and null pointers). – Lundin Mar 20 '23 at 07:19
5

TL;DR Check the types.

  • &arr is of type int (*) [5] (pointer to an array of 5 ints).
  • arr is of type int [5], but not always.

Quoting C11, chapter §6.3.2.1, (emphasis mine)

Except when it is the operand of the sizeof operator, the _Alignof 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.

hence,

 int *q = arr;   // int[5] decays to int *, == LHS

and

 int *q = &arr[0];   // RHS == LHS

are same, whereas,

 int *q = &arr; // LHS (int *) != RHS (int (*) [5])

is a mismatched type expression.

Now, it works, because, as already mentioned in Lundin's answer, the address of the array variable is likely to be the same as the address of the first element of the array, so despite the type mismatch, the value is same, so this seems to work.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
4

These are the ways to point to a (beginning of) array (without a warning), both work:

int *q = arr;
/* OR */
int *q = &arr[0];

This one is something in between, and will generate a warning:

int *q = &arr;
SHG
  • 2,516
  • 1
  • 14
  • 20
  • 1
    Neither of the first examples point to an array, they point to the first item of the array (second by directly taking its address, first by virtue of array decaying). Nitpicking in many situations, but probably relevant in the context of the question. – spectras Jun 13 '17 at 14:11
1

The output is the same because the address of arr[0] is literally equivalent to the pointer to arr[]. Any pointer initialized to point to arr[0] will have as its value the address of arr[0]; that's what a pointer is. Read up on pointer and especially on their relationship to arrays. The are countless tutorials out there, some of which probably show your two cases as examples.

TomServo
  • 7,248
  • 5
  • 30
  • 47
0

When used in an expression as an lvalue, an array decays to a pointer to its first element. So int *q = arr; initializes int pointer q with the address of the first element of the array arr: all is fine and no warning is emited.

But &arr is the address of an array. It could only be used correctly to initialize (or assign to) a pointer to array or same size, or a pointer to an array of undeterminated size. You use it to initialize a pointer to int (which is a different and non compatible type) and the compiler warns you about that. Because using a pointer initialized from a pointer to a different type is Undefined Behaviour according to the standard.

But on common implementations, pointers to any type have the same representation which is the address of the first byte of the object. So even if it is not allowed by the standard, the instruction int *p = arr; ends with the same value for p as would give the correct int *p = arr;. That explains why your program still gives the expected value.

BTW, Undefined Behaviour does not forbid expected result, simply a different compiler could give different results, crash, prematurely ends without error, kick your dog, etc. (even if no compiler could hit my dog till now ;-) )

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252