Search code examples
cpointersconstants

About Warning Option "incompatible-pointer-types"


My program(https://godbolt.org/z/Y93eG7K7s):

int main(){
    int temp = 0;
    int* tempp = &temp;
    int** temppp = &tempp;
    int*** tempppp = &temppp;

    const int* intp0;
    intp0 = tempp;    // A

    const int** intp1;
    intp1 = temppp;   // B
}

Either GCC or Clang can compile, but both raise the same "incompatible-pointer-types" warning in line B. I have no problem with that warning, because const int ** and int ** are definitely two incompatible pointer types. However (in my opinions), const int * and int * are also two incompatible pointer types(line A).

Therefore My Question is: Why const int * and int * are considered as compatible pointer types?


Solution

  • The GCC warning message is incorrectly phrased; the rule violated by intp1 = temppp is not that the operands of = must be compatible but that they must conform to certain constraints. Compatibility is one factor in those constraints, but it is not the factor at issue here, so the message is misleading. Although the question states Clang raises an “incompatible-pointer-types” warning, I do not see this; every version of Clang available on Compiler Explorer reports a correct error message, “assigning to 'const int **' from 'int **' discards qualifiers in nested pointer types.”

    An int * may be assigned to a const int * because the rules for simple assignment allow adding qualifiers to the directly pointed-to type. C 2018 6.5.16.1 says:

    One of the following shall hold:

    — the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) 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 unqualified type that const int * points to is int, and the unqualified type that int * points to is also int, and int is compatible with itself. Further, the type const int * points to, const int, has all the qualifiers of the type that int * points to, int.

    In contrast, the unqualified type that const int ** points to is const int *, and the unqualified type that int ** points to is int *, and const int * is not compatible with int *. (Note that while const int * points to a qualified type, it is itself unqualified; an object of type const int * may be changed to point to a different const int; it is not const-qualified.) So intp1 = temppp does not satisfy this constraint, so the compiler warns about it.

    The reason this is not allowed is it could result in a pointer to a qualified type pointing to an object without that qualifier. C 2018 6.5.16.1 6 gives an example, shown here modified slightly and with my comments:

    const int **ipp;
    int *p;
    const int i = 1;
    
    /*  The next line violates the constraints because `&p` is an `int **`, and
        it is assigned to a `const int **`.
        For illustration, suppose we suspend the constraint and allow it.
    */
    ipp = &p;
    
    /*  In the following, both `*ipp` and `&i` are `const int *`, so this is
        an ordinary assignment of identical types.
    */
    *ipp = &i;
    
    /*  In the following, `*p` is an `int`, to which an `int` is assigned. So
        this is an ordinary assignment of identical types. However, `p` points
        to `i`, which is `const`, so this assignment would change the value of
        a `const` object. The assignment `ipp = &p` above enabled this, so it
        is unsafe and should be disallowed.
    */
    *p = 0;
    

    Therefore My Question is: Why const int * and int * are considered as compatible pointer types?

    They are not compatible pointer types. As explained above, the reason the assignment is not allowed is because of rules about qualifiers, not about compatible types.

    The notion of compatible types in C is not about whether one type may be assigned to another type but about whether two types are actually the same except for the parts we do not know about them. For example, an array of three int and an array of an unspecified number of int are compatible—all the parts we know about these two types are the same. They could be the same if the unspecified part is completed. Similarly, a function returning void * that takes unknown parameters is compatible with a function returning void * that takes certain declared parameters. This issue of compatibility is not related to the issue of qualifiers in assignments.