Protecting the world

from criminally bad code

Const-incorrect standard library functions

Overview

Some of the functions provided by the standard library have the ability to convert a const pointer into a non-const pointer. In C99 these are memchr, strchr, strrchr, strpbrk, strstr, strtod, strtof, strtold, strtol, strtoll, strtoul, strtoull, and the corresponding wide character functions.

There is a reason for this behaviour, however it is open to misuse and it can prevent the compiler from detecting some types of error.

Description

Consider the function strchr. This has the prototype:

char* strchr(const char* s,int c);

The intent is to make it suitable for use with both const and non-const arguments. If s is a char* then the result can be assigned to a char*:

char* s[]="hello world";
char* f=strchr(s,'w');

and if s is a const char* then the result can be assigned to a const char*:

const char* s[]="hello world";
const char* f=strchr(s,'w');

C++ implements this behaviour in a const-safe manner by overloading. Two functions are provided, each called strchr. One is used when s is a char* and the other when it is a const char*:

char* strchr(char* s,int c);
const char* strchr(const char* s,int c);

C does not support overloading, so a const-safe implementation would need to use different names for the const and non-const variant of each function. The standardisation committee decided the consequences of that would be worse than a loss of const safety.

Effects

The ability of these functions to perform a const cast is harmless when they are used as intended because the net effect is const safe. The concern is that they are not limited to those uses, and that if a substantive cast were performed (accidentally or deliberately) then that would not be obvious from the source code.

Stylistic issues aside, it is permissible for a const pointer to be converted to a non-const pointer even if it refers to a const object, but using it to modify a const object would result in undefined behaviour.

Recommendations

  • If the string to be processed is represented by a const pointer then the result should be treated as if it were a const pointer.
  • If there is a need to convert a const pointer to a non-const pointer then this should be done using an explicit cast so that the intent is obvious.