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.