Constant, qualified, and less attemptable

The const type qualifier in the C programming language, and the consequent impossibility of implementing some common operations in a type-safe way.

Let’s say you’re writing a tokenizer in the C programming language, and your basic function is going to look like this (modelled on standard C library functions like strsep and strtok_r ):

struct token tokenize ( char ** stringp );

All goes well until one day you try to tokenize a string via a const char * pointer:

const char * data ; tokenize(&data);

and you get a warning like this:

foo.c:123: warning: passing argument 1 of 'tokenize' from incompatible pointer type

“Ah yes,” you say to yourself, a simple consequence of ISO/IEC 9899:TC3 §6.2.7(1):

Two types have compatible type if their types are the same.

together with §6.7.3(9):

For two qualiﬁed types to be compatible, both shall have the identically qualiﬁed version of a compatible type

and §6.7.5.1(2):

For two pointer types to be compatible, both shall be identically qualiﬁed and both shall be pointers to compatible types.

So let’s change the type of tokenize so that you can pass a const char ** to it:

struct token tokenize ( const char ** stringp );

But what about all your old code that passes char ** ? That’s not going to work any more? Or is it? You dimly remember that there’s a special get-out clause that allows a compiler to automatically convert a pointer to a qualified pointer. §6.3.2.3(2):

For any qualiﬁer q, a pointer to a non-q-qualiﬁed type may be converted to a pointer to the q-qualiﬁed version of the type

Now const is certainly a qualifier q, so surely char ** may be converted to const char ** ? After all, you depend on char * being converted to const char * most of the time you call standard C functions like printf and strlen . So let’s try it.

struct token tokenize ( const char ** stringp ); char * data ; tokenize(&data);

But no, you get the same warning as before.

foo.c:123: warning: passing argument 1 of 'tokenize' from incompatible pointer type

“I see,” you say to yourself, “the type const char ** is not a pointer to a const -qualified type, since it points to const char * which is just an ordinary type, not a const -qualified type. A const -qualified type would be something like const char * const . Elementary.”

✴

Elementary, maybe, but inconvenient, certainly. Why doesn’t the C standard extend its get-out clause so that it works for pointers to pointers to qualified types? The comp.lang.c FAQ points out that if the C standard mandated conversion from char ** to const char ** then the following would compile without a diagnostic:

const char c = 'x' ; char * p ; const char ** q = &p; *q = &c; *p = 'y' ;

But this invokes undefined behaviour because of §6.7.3(5):

If an attempt is made to modify an object deﬁned with a const-qualiﬁed type through use of an lvalue with non-const-qualiﬁed type, the behavior is undeﬁned.

So what are you going to do about it? Here are some workarounds: