constness and pointers to pointers

The reason char ** -> const char** is a “dangerous” conversion is the following code:

const char immutable[] = "don't modify this";

void get_immutable_str(const char **p) {
    *p = immutable;
    return;
}

int main() {
    char *ptr;
    get_immutable_str(&ptr); // <--- here is the dangerous conversion
    ptr[0] = 0;
}

The above code attempts to modify a non-modifiable object (the global array of const char), which is undefined behavior. There is no other candidate in this code for something to define as “bad”, so const-safety dictates that the pointer conversion is bad.

C does not forbid the conversion, but gcc warns you that it’s bad. FYI, C++ does forbid the conversion, it has stricter const-safety than C.

I would have used a string literal for the example, except that string literals in C are “dangerous” to begin with — you’re not allowed to modify them but they have type array-of-char rather than array-of-const char. This is for historical reasons.

I thought a pointer to an address can always be casted to a const pointer

A pointer-to-non-const-T can be converted to a pointer-to-const-T. char ** -> const char** isn’t an example of that pattern, because if T is char * then const T is char * const, not const char * (at this point it’s probably worthwhile not writing the const on the left any more: write char const * and you won’t expect it to be the same as T const where T is char *).

You can safely convert char ** to char * const *, and (for reasons that require a little more than just the simple rule) you can safely convert char ** to char const * const *.

Leave a Comment