考虑:
typedef union
{
int intval;
const int const_intval;
}myUnion_t;
int main(void)
{
myUnion_t x = {0};
x.intval = 5;
printf("intval = %d\n", x.intval);
printf("const_intval = %d\n", x.const_intval);
}
- UB 本身的定义是否
myUnion_t
与此处相同? - 有
intval
UB 的作业吗?
经过测试的所有编译器均未发出警告(使用 C++ 编译器编译时也如此)
编辑
重复仅与问题有关2
,与主要问题无关。
不。
问题中提供的联合说明符符合 C23 6.7.3.2/1 中描述的 C 语法
不存在对成员是否合格或成员和非成员
const
混合的约束(C23 6.7.3.2/2-6)。约束是我们发现语法之上的限制,例如在通常允许合格类型的情况下,对资格的使用进行限制。const
const
const
C明确允许
(C23 6.7.3.2/11)
const
作为类型限定符,它是任何被限定成员类型的一部分。也就是说,const int
是不同于 的类型int
,并且属于允许用于联合成员的“任何完整类型”。唯一需要争论的是以 结尾的类型名称
_t
。C 对此没有特别的规定,但如果 POSIX 对您很重要,那么您应该知道它保留了该形式的名称。对类似问题的回答也是如此,依据第 6.4.7.1/7 段中该 C23 条款的 C11 版本:
我倾向于同意,但我不认为该条款的应用是完全明确的。联合由逻辑上不同的成员组成,具有重叠的存储(C23 6.2.5/25),并且从概念上讲,联合一次只包含一个成员的值(C23 注释 38)。那么,有一个问题,将 分配给 是否
x.intval
应解释为修改x.const_intval
。上述规定应在哪个级别应用?事实上,对于 C++ 来说,答案可能有所不同,它通过放弃基于联合的类型双关的定义行为来对联合成员进行更严格的区分——但在那里,即使赋值具有定义的行为,第二次printf()
调用(x.const_intval
访问 )也不会。不同的路径将遵循赋值操作的规范——具体来说,赋值运算符必须具有可修改的左值作为其左操作数 (C23 6.5.17.1/2)。然后,根据 C23 6.3.3.1/1,
我们还知道,当使用
.
或->
运算符通过主机结构或联合访问成员时,生成的左值的类型将继承指定主机结构或联合的左值的限定符。这意味着如果x
声明了const
,则分配给x.intval
将具有未定义的行为。但这也不能让我们得出预期的结论,因为从类型x.intval
继承了限定符x
,而不是直接继承了可修改性。x
本身不是可修改的左值,但x.intval
似乎满足规范对“可修改的左值”的定义。我认为这是规范的一个缺陷。
最安全的解释是赋值给会产生 UB。如果随后访问,则会产生
x.intval
双重 UB ,如果初始化了,则会产生三重 UB。实际上,即使您更愿意将赋值给解释为具有定义的行为,但您的 C 实现也可能不具有定义的行为,因此赋值在实践中会引发程序中意外的、不需要的行为。x.const_intval
x.const_intval
x.intval