Search code examples
cpointerspointer-to-pointer

Regarding double and triple pointers/double dimension arrays


So, i was playing with C pointers and pointer arithmetic since i'm not entirely comfortable with them. I came up with this code.

char* a[5] = { "Hi", "My", "Name", "Is" , "Dennis"};
char** aPtr = a; // This is acceptable because 'a' is double pointer
char*** aPtr2 = &aPtr; // This is also acceptable because they are triple pointers
//char ***aPtr2 = &a // This is not acceptable according to gcc 4.8.3, why ?

//This is the rest of the code, the side notes are only for checking
printf("%s\n",a[0]); //Prints Hi
printf("%s\n",a[1]); //Prints My
printf("%s\n",a[2]); //Prints Name
printf("%s\n",a[3]); //Prints Is
printf("%s\n",a[4]); //Prints Dennis

printf("%s\n",*(a+0)); //Prints Hi
printf("%s\n",*(a+1)); //Prints My
printf("%s\n",*(a+2)); //Prints Name
printf("%s\n",*(a+3)); //Prints Is
printf("%s\n",*(a+4)); //Prints Dennis

printf("%s\n",*(*(aPtr2) +0)); //Prints Hi 
printf("%s\n",*(*(aPtr2) +1)); //Prints My // ap = a, *ap = *a, *(ap)+1 = *a+1 ?
printf("%s\n",*(*(aPtr2) +2)); //Prints Name
printf("%s\n",*(*(aPtr2) +3)); //Prints Is
printf("%s\n",*(*(aPtr2) +4)); //Prints Dennis

char*** aPtr2 = &a is not acceptable according to gcc 4.8.3, why?

Sorry forgot to add compiler warning:

warning: initialization from incompatible pointer type [enabled by default]

It maybe unclear what I'm trying to say, so I had to add this links:

Notice the commented out lines.


Solution

  • This comes from the way C treats arrays and addresses:

    int a[5];
    

    a is of the type int * const, true, meaning you can use it where a pointer is expected. However, on the stack the space for the pointer isn't allocated, only the space for the five ints. Meaning a is the same as &a[0]. This is all expected, but here comes the weird part:

    a is the same as &a. This is because the pointer isn't stored anywhere, you can't get it's address. But instead of failing the compile, the C standard just says that they are the same, so some arithmetic will work.

    However, since you are doing char ***aPtr2 = &a;, you are effectively doing char ***aPtr2 = a;, which is why you get a segfault. It is only a double pointer, not a triple one.

    You can inspect all the values in the debugger to see it more clearly, or run a program such as:

    #include <stdio.h>
    
    int main(void)
    {
        char *a[5] = { "Hi", "My", "Name", "Is" , "Dennis"};
        char **aPtr = a; 
        char ***aPtr2 = &aPtr;
        char ***aPtr3 = &a; 
    
        printf("%p %c\n", a, *a);
        printf("%p %p %c\n", aPtr, *aPtr, **aPtr);
        printf("%p %p %p %c\n", aPtr2, *aPtr2, **aPtr2, ***aPtr2);
        printf("%p %p %c\n", aPtr3, *aPtr3, **aPtr3);
    
        return 0;
    }
    

    which produces the output:

    0xfff65578 H
    0xfff65578 0x8048648 H
    0xfff65574 0xfff65578 0x8048648 H
    0xfff65578 0x8048648 H
    

    EDIT: Another interesting thing is the function pointers are treated in the same fashion. &func is the same as func.

    int add(int a, int b) { return a + b; }
    typedef int (*PFUNC)(int, int);
    
    PFUNC p1 = add;
    PFUNC p2 = &add;
    if (p1 == p2) 
         printf("Huh. %d, %d\n", (*p1)(1,2), p2(3, 4)); // the dereference is also optional