Home > Software engineering >  About Warning Option "incompatible-pointer-types"
About Warning Option "incompatible-pointer-types"

Time:02-14

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?

CodePudding user response:

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 error 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; a 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 **`.  Suppose it is allowed.
*/
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.

CodePudding user response:

They are compatible in that direction (non-const to const)

You can assign a non-constant pointer of a given type to a constant pointer of that given type, so when you use that constant pointer with some code, that code can only access it in read-only mode.

If that code tries to assign it to a non-constant pointer, the compiler will refuse it (unless you cast it, which is sometimes necessary with ill-written APIs but generally not recommended)

int *a;
const int *b = a;  // allowed
a = b;             // not allowed
a = (int *)b;      // works if you really know what you're doing

That's why you can call a function like strdup

char *strdup(const char *s);

with a non-constant char * string. And strdup guarantees that s is not changed within the routine you don't have the code for. What is known is that it doesn't violate the constant contract for that parameter.

So it's important to handle those warnings when encountered, don't let them hanging in your code. Either force the cast if that's an exception, or ensure that const qualifier is used all the way.

  • Related