Search code examples
cpointersconstants

Does the address of a pointer (&ptr) change when reassigned (*ptr = n)? Why doesn't C allow const pointer (const int**) to non-const (int**) pointer


In this question, the example used is:

int       *      *x = NULL;
int       *const *y = x; /* okay */
int const *const *z = y; /* warning */

Where int const *const *z = y; /* warning */ compiles with warning 'int const *const *' with an expression of type 'int *const *' discards qualifiers in nested pointer types. The answer links to Question 11.10 in the comp.lang.c FAQ.

I am getting a similar warning when attempting to do:

char *x = strstr(...)
const char **z = &x;

But, after reading the answers & the FAQ, I still don't understand why the compiler discards the const qualifier. From what I remember, *x = ... does not affect &x and &x remains constant until x is reassigned (x = ...).

In this post, the warning makes sense because there is non-const pointer to const, however I don't understand why there is a warning in this case.


Solution

  • Say you have the following code:

    char *p = strdup("bark");
    const char **dp = &p;
    

    Compiling and running with gcc in.c -o out.c -Wall -Wextra -pedantic -Wpedantic:

    warning: initialization of ‘const char **’ from incompatible pointer type ‘char **’ [-Wincompatible-pointer-types]
    const char **dp = &p;
                      ^
    

    The warning message is clear: the two pointers are of incompatible types. To understand why this is problematic, try and modify the content of p via dp:

    char *p = strdup("bark");
    const char **dp = &p;
    **dp = 'd';
    

    Compile and run with the same command:

    warning: initialization of ‘const char **’ from incompatible pointer type ‘char **’ [-Wincompatible-pointer-types]
    const char **dp = &p;
                      ^
    error: assignment of read-only location ‘**dp’
    **dp = 'd';
         ^
    

    The const in const char **dp applies to the content at which *dp is pointing to, namely p (which is non-const). That's why you see the incompatibility warning message.


    Now, try and do:

    char *p = strdup("bark");
    const char **dp = &p;
    *dp = strdup("dark");
    

    The code compiles just fine (with the above warning). However, changing the above code to

    char *p = strdup("bark");
    char *const *dp = &p;
    *dp = strdup("dark");
    

    Will produce the following error:

    error: assignment of read-only location ‘*dp’
    *dp = strdup("dark");
        ^
    

    Unlike in const char **dp, the const in char *const *dp applies to the pointer at which dp is pointing to, namely &p. Therefore, changing it is not allowed.

    Note that, in this same case, **dp = 'd'; will compile just fine (with the above warning).


    You can go further and try:

    char *p = strdup("bark");
    const char *const *dp = &p;
    *dp = strdup("dark"); // Error
    **dp = 'p';           // Error
    
    warning: initialization of ‘const char * const*’ from incompatible pointer type ‘char **’ [-Wincompatible-pointer-types]
    const char *const *dp = &p;
                          ^
    error: assignment of read-only location ‘*dp’
    *dp = strdup("dark");
        ^
    error: assignment of read-only location ‘**dp’
    **dp = 'x';
         ^
    

    Maybe writing the syntax differently will make things clearer:

    1. const char **dp == (const char)* *dp
    2. char *const *dp == const (char*) *dp
    3. const char *const *dp == const (const char)* *dp