1

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.

  • A variable can never change its location. `&x` will never change value. – Some programmer dude Jul 31 '22 at 08:35
  • You seem to be confused as to what the two `const` qualifiers in your declaration of `z` mean. In your code, `z` is a `const` pointer to a `const int*` but `y` is a `const` pointer to a non-const `int*`. Thus, the pointed-to types are different. – Adrian Mole Jul 31 '22 at 08:39
  • @Someprogrammerdude Then why would `const char **z = &x` be incorrect? If `&x` is constant – pavel_fyodorovich Jul 31 '22 at 08:39
  • There's likely a duplicate around, somewhere. But try `int *const *const z = y;`. Actually, the question you've linked to is the duplicate ... not sure what to do about this one. – Adrian Mole Jul 31 '22 at 08:41
  • `y` is declared as a pointer to a `const char *`. While `&x` is a pointer to a `char *`. That's not really the same. If you want to emphasize that `*y` is constant (which it effectively is, since you can't modify `&x`) then use `char * const * y`. That means `*y` is constant. If you want `y` itself to be constant (not possible to assign to `y`) then use `char * const * const y`. – Some programmer dude Jul 31 '22 at 10:35

2 Answers2

1

Consider the following example:

char*        x = ...;
char const** z = &x;

char const*  y = ...

y can point to an array that's truely const (non-writable memory). y is compatible to the declaration of z, so you could do:

*z = y;

Now, though, x – which z points to – has been (legally from point of view of z) assigned a const (non-writable) array you might try to modify via the x pointer (undefined behaviour)!

This is why the assignment of x to z is problematic, and this is why you get a warning issued, though admittedly the warning text could be a bit more precise. You consider it as in a way discarding the const-ness of the target of z by assigning a non-const pointer x...

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • OP's question is about why his last code snippet is producing a warning. – Zakk Jul 31 '22 at 09:29
  • @Zakk And that's exactly what I'm referring to – note the pointer types! Though need to admit that I swapped variable names (fixed, now z as is while y newly introduced – in context of second code snipped, unfortunately x, y, z exist in both snippets...). – Aconcagua Jul 31 '22 at 09:35
1

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
Zakk
  • 1,935
  • 1
  • 6
  • 17
  • `**dp = 'd';` has nothing to do with the warning – fully clear that the assignment cannot happen as `**dp` is const, similarly the second example as you try to modify the const pointer `*dp`, while you entirely overlooked the real problem the warning arises from: `char* p /* no assignment needed...*/; char const** dp = &p; /* the critical point */; char const ca[] = "hello";` – as `ca` decays to a pointer to `char const` you can totally legally assign it to `*dp` – however `dp` points to a pointer to *non-const* char, thus with `*dp = ca;` you actually do – Aconcagua Aug 01 '22 at 15:37
  • `x /* char* (!) */ = ca /* decays to char const* (!) */`; and are now able to modify the `const ca` via the pointer to non-const `p`. Next to the same problem existing in C++ you there have an equivalent problem with co- and contravariant function arguments or containers (polymorphism involved) where you wouldn't drop the const, but would be able to assign incompatible pointer types via a pointer to common base – which is why co- and contravariance with function arguments or containers are not allowed (while co-variant return types are legal). – Aconcagua Aug 01 '22 at 15:38