Unhandled Exception
Unhandled Exception

Reputation: 1513

Array Pointer compile time warning : warning: initialization from incompatible pointer type

Using a simple sample that involves an array point generates the following warnings. The code codes compile and execute which also leads to questions.

This is the int Array. int numbers[3] = { 101, 202, 303};

These 2 calls generate the warnings.

    int *p2 = &numbers;
    int **p3 = &numbers;

The following are the Warning generated.

 > cc -o myApp test.c
 test.c: In function 'main':
 test.c:9:12: warning: initialization from incompatible pointer type [enabled by default]
 int *p2 = &numbers;
        ^
 test.c:10:13: warning: initialization from incompatible pointer type [enabled by default]
 int **p3 = &numbers;
         ^

The code used is the following :

#include <stdio.h>

int main ()
{
    int numbers[3] = { 101, 202, 303};
    int size = sizeof(numbers) / sizeof (numbers[0]);

    int *p = numbers;
    int *p2 = &numbers;
    int **p3 = &numbers;
    int *p4 = &numbers[0];

    int *end = p + size;

    printf("p           = 0x%d\n", p);
    printf("p2          = 0x%d\n", p2);
    printf("p3          = 0x%d\n", p3);
    printf("numbers     = 0x%d\n", numbers);
    printf("&numbers    = 0x%d\n", &numbers);
    printf("numbers[0]  = 0x%d\n", numbers[0]);
    printf("&numbers[0] = 0x%d\n", &numbers[0]);

    for (; p != end; p++)
    {
            printf ("0x%d\n", p);
    }
}

When executed, here is the output.

> ./runTest
p           = 0x-207214704
p2          = 0x-207214704
p3          = 0x-207214704
numbers     = 0x-207214704
&numbers    = 0x-207214704
numbers[0]  = 0x101
&numbers[0] = 0x-207214704
0x-207214704
0x-207214700
0x-207214696

Both p2 and p3 as well as p all point to the same address. I really thought using p3 as a double pointer would have resolved the Warning, but it appears not. Why does p2 and p3 generate the Warnings and what is it trying to say?

Upvotes: 1

Views: 194

Answers (3)

John Bode
John Bode

Reputation: 123448

First, always use %p to print pointer values:

printf( "%p\n", (void *) p );
printf( "%p\n", (void *) p1 );

Given the declaration

int numbers[3];

then the following are true:

Expression        Type        "Decays" to         Value
----------        ----        -----------         -----
   numbers        int [3]     int *               Address of first element of array
  &numbers        int (*)[3]  n/a                 Address of array (which is the same as above)
  *numbers        int         n/a                 Value of numbers[0]

Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array. Thus, under most circumstances (such as when it's the RHS of an assignment, or a parameter to a function), the expression numbers will have type int *.

So how do you properly initialize p2 and p3? That depends on what you want to happen when you write p2 + 1 or p2[1] and p3 + 1 or p3[1]. If you want p2 + 1 to point to the second element in numbers, or if you want p2[1] to be equal to numbers[1], then you'd write

int *p2 = numbers;

If you want p2 + 1 to point to the next 3-element array of int following the end of the numbers array, then you'd write

int (*p2)[3] = &numbers;

Then you'd initialize p3 as

int **p3 = &p2;

or

int (**p3)[3] = &p2;

based on how p2 was declared.

A picture might help. Assume the following declarations:

int numbers[3] = { 0, 1, 2 };
int *p0 = numbers;
int (*p1)[3] = &numbers;

then things look something like this:

         +---+                    
numbers: | 0 | numbers[0] <------ p0, p1
         +---+                    
         | 1 | numbers[1] <------ p0 + 1
         +---+
         | 2 | numbers[2]
         +---+
          ...             <------ p1 + 1

Remember that with pointer arithmetic, p + 1 points to the next object of the pointed-to type; if p points to an object that's 4 bytes wide, then p + 1 points to the object starting 4 bytes after p. If p points to an object that's 32 bytes wide (such as a small array), then p + 1 points to the object starting 32 bytes after p.

Upvotes: 2

user2736738
user2736738

Reputation: 30926

I get the point which causes you this confusion. You first saw

 int *p = numbers;

Well don't think that numbers is of type pointer. It is simply an array object (int[3]) which is converted into pointer to first element of it - that is int*. This is known as array decaying.

Now you think in int **p3 = &numbers;- &numbers will be of type int**.

Well this is where the trick is - array decaying doesn't occure in some cases - when array is used as an operand to & operator - then it is one case like this - so it won't decay. It will be an array object whose pointer is considered which is int(*)[3] in this case.

With this logic int *p2=&numbers will also be something of which compiler will complain. The correct would be what is said here int (*pp)[3] = &numbers. Note that print the pointers(not a function pointer) use %p format specifier with the argument cast to void*.

The compiler tried to warn you about the pointer type mismatch. (For pointers the type to which it points to is of importance because of the pointer arithmetic is dictated by that).

From §6.3.2.1p3

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.

Upvotes: 3

sjsam
sjsam

Reputation: 21955

It should have been

int numbers[3] = { 101, 202, 303};

and

int (*p2)[3] = &numbers; // Here p2 is a pointer to an array of 3 members

The members should be accessed like

(*p2)[0],(*p2)[1] and (*p2)[2]

which is eqivalent to

*(*(p2)+0),*(*(p2)+1) and *(*(p2)+2)

and also to

p2[0][0],p2[0][1] and p2[0][2] // Since array notation can be used to dereference pointers.

Upvotes: 1

Related Questions