Summary
Kernighan and Ritchie are wrong; attempting to modify const objects is undefined, not implementation-defined.
This rules applies only to objects originally defined with const.
const on function parameters is advisory, not enforced. It is possible, and defined by the C standard, for a function to modify an object pointed to with a const parameter if that object was not defined with const.
Details
The quoted passage is wrong. Attempting to modify an object defined with const has undefined behavior, not implementation-defined behavior. And this applies only to objects defined with const, not to objects passed via const-qualified pointers, if those objects were not originally defined with const.
C 2018 6.7.3 7 says:
If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.
The same wording appears in C 1990 6.5.3.
“Undefined“ means the C standard does not impose any requirements on the behavior (C 2018 3.4.3). This is different from “implementation-defined,” which means the C implementation must document how a choice among possibilities is made (C 2018 3.4.1).
Note that that rule applies only to objects defined with const. 6.7 5 tells us that, for object identifiers, a definition is a declaration that causes storage to be reserved for the object. If we declare int x; inside a function, that will cause storage to be reserved for x, so it is a definition. However, the statement int strlen(const char[]); merely declares a function and its parameter type. The actual parameter is not declared because there is no name for it. If we consider the actual function definition, such as:
int strlen(const char s[])
{
…
}
then this function definition includes a declaration of the parameter s. And it does define s; storage for the parameter itself will be reserved when the function executes. However, this s is only a pointer to some object that the caller passes the address of. So this is not a definition of that object.
So far, we know the rule in 6.7.3 7 tells us that modifying an object defined with const has undefined behavior. Are there any other rules about a function modifying an object it has received through a pointer with const? There are. The left operand of an assignment operator must be modifiable. C 2018 6.5.16 2 says:
An assignment operator shall have a modifiable lvalue as its left operand.
An lvalue qualified with const is not modifiable, per C 2018 6.3.2.1 1. This paragraph is a constraint in the C standard, which means a C implementation is required to diagnose violations. (So, again, this is not implementation-defined behavior. The C implementation must produce a message.) The ++ and -- operators, both pre- and post-, have similar constraints.
So, a function with a parameter const char s[] cannot directly modify *s or s[i], at least not without getting a diagnostic message. However, a program is allowed to remove const in a conversion operator if it was not originally present. C 2018 6.3.2.3 2 says we can add const:
For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type; the values stored in the original and converted pointers shall compare equal.
and then C 2018 6.3.2.3 7 says that, after we have done that, we can convert the const version back to the original type:
A pointer to an object type may be converted to a pointer to a different object type… when converted back again, the result shall compare equal to the original pointer.
What this means is that if a calling routine has:
int x = 3;
foo(&x);
printf("%d\n", x);
and foo is:
void foo(const int *p)
{
* (int *) p = 4;
}
then this is allowed and defined by the C standard. The function foo removes const and modifies the object it points to, and “4” will be printed.
A lesson here is that const in function parameters is advisory, not enforced by C. It serves two purposes:
const on a function parameter is generally an indication for humans that the function will not modify the pointed-to object through that parameter. (However, there are circumstances, not discussed here, where this indication does not hold.)
- The compiler will enforce a rule that the pointed-to object cannot be modified through the
const type. This prevents inadvertent errors where a typographical error might result in an unwanted assignment to a const object. However, a function is permitted to explicitly remove const and then attempt to modify the object.