66

A pointer to non-const data can be implicitly converted to a pointer to const data of the same type:

int       *x = NULL;
int const *y = x;

Adding additional const qualifiers to match the additional indirection should logically work the same way:

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

Compiling this with GCC or Clang with the -Wall flag, however, results in the following warning:

test.c:4:23: warning: initializing 'int const *const *' with an expression of type
      'int *const *' discards qualifiers in nested pointer types
    int const *const *z = y; /* warning */
                      ^   ~

Why does adding an additional const qualifier "discard qualifiers in nested pointer types"?

km3h
  • 81
  • 2
  • 14
Michael Koval
  • 8,207
  • 5
  • 42
  • 53
  • 3
    Small point of terminology: A cast is an explicit conversion. What you're doing here is an implicit conversion, not a cast. – Tim Martin Feb 20 '11 at 06:46
  • 2
    ITYM pointer to const pointer to const ('const pointer' can be ambiguous). If `x -> y -> z` (where `->` just means 'points to' and not the dereference operator) you can change the contents `z` without changing `y`. So if it's pointer to const pointer to non-const, you can change the value `z` without changing `y`. – Tim Martin Feb 20 '11 at 07:05
  • There was a comment there just a moment ago, which I was replying to. I guess it was deleted. – Tim Martin Feb 20 '11 at 07:06
  • 1
    Isn't this just an instance of what is explained in the c++ faq on [const correctness](http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.17)? – Jeff Mercado Feb 20 '11 at 07:07
  • 2
    @Jeff The FAQ states that it's OK to cast `Foo **` to `Foo const * const *`. I don't quite see how it that doesn't apply here. But it sounds like this is relevant. If you care to post it as an answer, you'll get my vote. – Tim Martin Feb 20 '11 at 07:11
  • 3
    @Jeff M: The C and C++ rules on const-correctness are different, and this question is about C. – caf Feb 20 '11 at 07:21
  • @Tim: I didn't know enough about the problem to even offer that as an answer. I try to fully understand the problem and have a sense of what could be done before doing so. Otherwise I'll just use comments. – Jeff Mercado Feb 20 '11 at 07:35
  • @caf: I was aware of that but don't know how they really are different from each other. I thought the explanation there was related with this problem. – Jeff Mercado Feb 20 '11 at 07:37
  • Related: https://stackoverflow.com/questions/78125/why-cant-i-convert-char-to-a-const-char-const-in-c?rq=1 – ecatmur Jul 20 '17 at 10:04

3 Answers3

62

The reason why const can only be added one level deep is subtle, and is explained by Question 11.10 in the comp.lang.c FAQ.

Briefly, consider this example closely related to yours:

const int i;
int *p;
int const **z = &p;
*z = &i;
/* Now p points to i */

C avoids this problem by only allowing assignment to discard qualifiers at the first pointed-to level (so the assignment to z here is not allowed).

Your exact example does not suffer from this problem, because the const the second level means that the assignment to *z would not be allowed anyway. C++ would allow it in this exact case, but C's simpler rules do not distinguish between your case and the example above.

caf
  • 233,326
  • 40
  • 323
  • 462
14

The FAQ entry linked by the other answer explains why the following code is not permitted:

int **x = whatever;
const int **z = x;

However, your code const int *const *z = x; is quite different, and it does not suffer from the same flaw raised by the FAQ.

In fact, there is conceptually nothing wrong with the latter code. It is just a flaw in the C specification that it is not permitted, and it forces C programmers to include ugly casts in their code.

It would have been possible for C to use the same rules that C++ did; however the C standard committee didn't decide to do that.

M.M
  • 138,810
  • 21
  • 208
  • 365
0

The reason, why the automatism of adding qualifiers does only work for the 1st indirection level, can be read from the standard:

The Standard claims in 6.5.16.1 for an assignment, that "both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right"
The last part of the sentence means that adding a qualifier to the pointed-to type is no problem.
And the first part claims "compatible" types. And (I think,) 6.7.3 (11) does describe this for qualified types: "For two qualified types to be compatible, both shall have the identically qualified version of a compatible type."

Reading this, your pointed-to types are not considered as compatible (even if it would be possible to assign one to the other).

Hence I would say that the clang warning about discarding qualifiers is a bit misleading, but it refers to the non-identically qualified pointed-to types.

MattTT
  • 339
  • 3
  • 9