C99标准在6.5.16:2中指出:
赋值运算符的左值应为可修改的左值。
并在6.3.2.1:1中:
可修改的左值是不具有数组类型,不完整的类型,不具有const限定类型的左值,并且如果是结构或联合,则不具有任何成员(递归包括任何成员)或const限定类型的所有包含的集合或联合的元素)。
现在,让我们考虑一个非const
struct
带const
字段。
typedef struct S_s {
const int _a;
} S_t;
按照标准,以下代码是未定义行为(UB):
S_t s1;
S_t s2 = { ._a = 2 };
s1 = s2;
这样做的语义问题是,根据实体(struct
)的声明类型,应将包围实体()视为可写(非只读S_t s1
),但不应通过标准措辞将其视为可写(第2条在顶部),因为const
现场_a
。对于读代码的程序员,该标准不清楚,该赋值实际上是一个UB,因为无法分辨出struct S_s ... S_t
类型的定义是不可能的。
此外,无论如何,仅以语法方式强制执行对该字段的只读访问。不可能const
将非字段的某些部分const
struct
真正放置到只读存储中。但是这样的标准措辞使故意丢弃const
这些字段的访问程序中的字段限定符的代码无效,就像这样(对C中的结构字段进行const限定是一个好主意吗?):
(*)
#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);
}
因此,由于某种原因,对于一个整体来说struct
,它是只读的就足以声明它const
const S_t s3;
但是对于一个整体来说struct
,它是非只读的,仅仅声明为w / o是不够的const
。
我认为会更好的是:
- 为了限制
const
具有const
字段的非结构的创建,并在这种情况下发出诊断信息。可以很清楚地看到,struct
包含的只读字段本身就是只读的。 - 定义在写入
const
属于非const
结构的字段时的行为,以使上面的代码(*)符合标准。
否则,行为将不一致且难以理解。
那么,正如所说的那样,C Standard const
递归考虑-ness 的原因是什么?