C语言 初始化位域

dced5bon  于 2023-03-28  发布在  其他
关注(0)|答案(2)|浏览(146)

当你写作的时候

struct {
    unsigned a:3, b:2;
} x = {10, 11};

ANSI C(C89)是否保证x.b3?我已经阅读并重读了该标准,但似乎无法找到确切的情况。
例如,“无法由结果的无符号整数类型表示的结果以大于结果的无符号整数类型可以表示的最大值的数字为模进行归约。”谈论的是计算,而不是初始化。此外,位字段不是真正的类型。
此外,(当谈到无符号t:4时)“包含[0,15]范围内的值”,但这并不一定意味着初始化器必须 * 以16为模进行约简 * 才能Map到[0,15]。
结构的初始化描述得非常详细,但我似乎真的找不到确切的行为。(当然编译器确实是这样做的。IBM文档说“当你将一个超出范围的值分配给一个位字段时,低阶位模式被保留,并分配适当的位。”,但我想知道ANSI C是否标准化了这一点。

v440hwme

v440hwme1#

“ANSI C”/C89已经过时25年了,因此,我的回答引用了当前的C标准ISO 9899:2011,也被称为C11。
在C标准中,几乎所有与位域相关的东西都定义得很差。通常,你不会发现任何显式地解决位域行为的东西,但是它们的行为是隐式地指定的,“在行之间”。这就是为什么你应该避免使用位域。
然而,我认为这个具体案例是明确的:它应该像任何其他整数初始化一样工作。
你提到的详细的结构体初始化规则(6.7.9)显示了初始化器列表中的文字11是如何与变量b相关的。这并不奇怪。然后应用的是“简单赋值”,就像你写x.b = 11;一样。
当在C中进行任何类型的赋值或初始化时,右操作数被 * 转换 * 为左操作数的类型。这在C11 6.5.16中指定:
在简单赋值(=)中,右操作数的值转换为赋值表达式的类型,并替换左操作数指定的对象中存储的值。
在本例中,int类型的文字11被转换为unsigned int:2的位字段。
因此,您要寻找的规则应该在处理转换的章节(C11 6.3)中找到。适用的是您在问题中已经引用的内容,C116.3.1.3:
...如果新类型是无符号的,则通过重复添加或减去新类型中可以表示的最大值的一个值来转换该值,直到该值在新类型的范围内。

unsigned int:2的最大值是3。比最大值多一个是3+1=4。编译器应该反复从值11中减去这个:

11 - (3+1) = 7    does not fit, subtract once more:
 7 - (3+1) = 3    does fit, store value 3

当然,这与取十进制值11的2个最低有效位并将其存储在位字段中是完全相同的。

a64a0gku

a64a0gku2#

WRT“谈论计算,而不是初始化”,C89标准明确地将赋值和转换规则应用于初始化。它还说:
位字段被解释为由指定位数组成的整数类型。
考虑到这些,虽然编译器警告显然是有序的,但似乎丢弃高阶位是由标准保证的。

相关问题