The C99 standard says in 6.5.16:2:
An assignment operator shall have a modifiable lvalue as its left operand.
and in 6.3.2.1:1:
A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const-qualified type.
Now, let's consider a non-const
struct
with a const
field.
typedef struct S_s {
const int _a;
} S_t;
By standard, the following code is undefined behavior (UB):
S_t s1;
S_t s2 = { ._a = 2 };
s1 = s2;
The semantic problem with this is that the enclosing entity (struct
) should be considered writable (non-read-only), judging by the declared type of the entity (S_t s1
), but should not be considered writable by the wording of standard (the 2 clauses on the top) because of const
field _a
. The Standard makes it unclear for a programmer reading the code that the assignment is actually a UB, because it's impossible to tell that w/o the definition of struct S_s ... S_t
type.
Moreover, the read-only access to the field is only enforced syntactically anyway. There's no way some const
fields of non-const
struct
are going really be placed to read-only storage. But such wording of standard outlaws the code which deliberately casts away the const
qualifier of fields in accessor procedures of these fields, like so (Is it a good idea to const-qualify the fields of structure in C?):
(*)
#include <stdlib.h>
#include <stdio.h>
typedef struct S_s {
const int _a;
} S_t;
S_t *
create_S(void) {
return calloc(sizeof(S_t), 1);
}
void
destroy_S(S_t *s) {
free(s);
}
const int
get_S_a(const S_t *s) {
return s->_a;
}
void
set_S_a(S_t *s, const int a) {
int *a_p = (int *)&s->_a;
*a_p = a;
}
int
main(void) {
S_t s1;
// s1._a = 5; // Error
set_S_a(&s1, 5); // OK
S_t *s2 = create_S();
// s2->_a = 8; // Error
set_S_a(s2, 8); // OK
printf("s1.a == %d\n", get_S_a(&s1));
printf("s2->a == %d\n", get_S_a(s2));
destroy_S(s2);
}
So, for some reason, for an entire struct
to be read-only it's enough to declare it const
const S_t s3;
But for an entire struct
to be non-read-only it's not enough to declare it w/o const
.
What I think would be better, is either:
- To constrain the creation of non-
const
structures withconst
fields, and issue a diagnostic in such a case. That would make it clear that thestruct
containing read-only fields is read-only itself. - To define the behavior in case of write to a
const
field belonging to a non-const
struct as to make the code above (*) compliant to the Standard.
Otherwise the behavior is not consistent and hard to understand.
So, what's the reason for C Standard to consider const
-ness recursively, as it puts it?