4
struct foo { unsigned x:1; } f;
printf("%d\n", (int)sizeof(f.x = 1));

预期的输出是什么,为什么?不允许直接取位域左值的大小。但是通过使用赋值运算符,我们似乎仍然可以获取位域类型的大小。

什么是“位域的字节大小”?它是保存位域的存储单元的大小吗?是 bf 占用的位数四舍五入到最接近的字节数吗?

或者是构造未定义的行为,因为标准中没有任何内容可以回答上述问题?同一平台上的多个编译器给我的结果不一致。

4

13 回答 13

4

你是对的,整数提升不适用于 的操作数sizeof

整数提升仅适用于:作为通常算术转换的一部分,适用于某些参数表达式,适用于一元 +、- 和 ~ 运算符的操作数,以及移位运算符的两个操作数,由它们各自的子条款指定。

真正的问题是位域是否有自己的类型。

约瑟夫迈尔斯告诉我:

C90 DR 的结论是位域有自己的类型,而 C99 DR 的结论是让它们是否有自己的类型实现定义,而 GCC 遵循 C90 DR,因此分配的类型为 int:1 而不是提升为 sizeof 的操作数。

这在缺陷报告 #315中进行了讨论。

总结一下:您的代码是合法的,但由实现定义。

于 2008-09-17T15:18:29.040 回答
3

C99 标准(最新草案的 PDF)在第 6.5.3.4 节中说关于sizeof约束:

sizeof运算符不应应用于具有函数类型或不完整类型的表达式、此类类型的括号名称或指定位域成员的表达式。

这意味着允许应用于sizeof赋值表达式

6.5.16.3 说:

赋值表达式的类型是左操作数的类型...

6.3.1.1.2 说关于整数促销:

可以在可以使用 int 或 unsigned int 的表达式中使用以下内容:

  • ...
  • _Bool, int,signed int或类型的位域unsigned int

如果 int 可以表示原始类型的所有值,则将该值转换为int; 否则,将其转换为unsigned int.

因此,您的测试程序应该输出 的大小int,即 sizeof(int).

有没有不这样做的编译器?

于 2008-09-17T13:42:16.743 回答
1

如您所见,尝试获取位域的大小是不合法的。(sizeof以字节为单位返回大小,这对于位域没有多大意义。)

sizeof(f.x = 1)将返回表达式类型的大小。由于 C 没有真正的“位域类型”,因此在您的示例中,表达式(此处:赋值表达式)通常获取位域的基本类型的类型,unsigned int但编译器可以在内部使用较小的类型(在这种情况下可能是unsigned char因为它足够大一点)。

于 2008-09-17T09:18:51.387 回答
0

不会

(f.x = 1)

是一个表达式,计算结果为真(技术上计算为赋值的结果,在这种情况下为 1/true),因此,

sizeof( f.x = 1)

是根据存储它需要多少个字符来询问 true 的大小?

我还应该补充一点,关于sizeof的维基百科文章很好。特别是,他们说“sizeof 是一个编译时运算符,它返回它前面的变量或带括号的类型说明符的大小,以 char 大小的倍数表示。”

文章还解释了 sizeof 对表达式起作用。

于 2008-09-17T09:10:36.757 回答
0
sizeof( f.x = 1)

返回 1 作为其答案。sizeof(1) 可能是您正在编译的平台上的整数大小,可能是 4 或 8 个字节。

于 2008-09-17T09:18:17.690 回答
0

不,您一定在考虑 == 运算符,它在 C 中产生 int 类型的“布尔”表达式,在 C++ 中确实产生 bool。

我认为表达式会将值 1 转换为对应的位域类型并将其分配给位域。结果也应该是位域类型,因为我看不到隐藏的促销或转换。

因此,我们有效地访问了位域类型。

不需要编译器诊断,因为“fx = 1”不是左值,即它不直接指定位域。它只是“无符号:1”类型的值。

我专门使用“fx = 1”,因为“sizeof fx”采用位域左值的大小,这显然是不允许的。

于 2008-09-17T09:18:37.737 回答
0

(f.x = 1)

不是表达式,它是一个赋值,因此返回赋值。在这种情况下,该值的大小取决于已分配给它的变量。

unsigned x:1

有 1 位,它的 sizeof 返回 1 字节(8 位对齐)

如果你会使用

unsigned x:12

然后 sizeof(fx = 1) 将返回 2 字节(再次因为 8 位对齐)

于 2008-09-17T09:19:05.240 回答
0

sizeof(1) 可能是您正在编译的平台上的整数大小,可能是 4 或 8 个字节。

请注意,我没有使用 sizeof(1),它实际上是 sizeof(int)。仔细看,我正在使用 sizeof(fx = 1),它实际上应该是 sizeof(bitfield_type)。

我想看到一个参考,告诉我构造是否合法。作为额外的奖励,如果它告诉我预期什么样的结果会很好。

gcc 当然不同意 sizeof(bitfield_type) 应该与 sizeof(int) 相同的断言,但仅在某些平台上。

于 2008-09-17T09:21:28.807 回答
0

如您所见,尝试获取位域的大小是不合法的。(sizeof 以字节为单位返回大小,这对于位域没有多大意义。)

那么你是说行为是未定义的,即它与“*(int *)0 = 0;”具有相同程度的合法性,编译器可以选择不明智地处理这个问题?

这就是我想要找出的。您是否认为它因遗漏而未定义,或者是否有明确声明它是非法的?

于 2008-09-17T09:24:09.983 回答
0

不是表达式,它是一个赋值,因此返回赋值。在这种情况下,该值的大小取决于已分配给它的变量。

首先,它一个包含赋值运算符的表达式。

其次,我很清楚我的例子中发生了什么:)

然后 sizeof(fx = 1) 将返回 2 字节(再次因为 8 位对齐)

你从哪里得到这个?这是在您尝试过的特定编译器上发生的情况,还是标准中规定了这些语义?因为我还没有找到任何这样的说法。我想知道该构造是否完全可以保证工作。

于 2008-09-17T09:36:20.970 回答
0

在第二个示例中,如果您将结构定义为

struct foo { unsigned x:12} f;

然后将像 1 这样的值写入 fx - 由于对齐,它使用 2 个字节。如果你做一个像

f.x = 1;

这将返回分配的值。这与

int a, b, c;
a = b = c = 1;

从右到左评估分配的位置。c = 1 将 1 分配给变量 c,并且此分配返回分配的值并将其分配给 b(依此类推),直到 1 分配给 a

它等于

a = ( b = ( c = 1 ) )

在您的情况下, sizeof 获取分配的大小,这不是位域,而是分配给它的变量。

sizeof ( f.x = 1)

不返回位域大小,但变量赋值是 1 的 12 位表示(在我的情况下),因此 sizeof() 返回 2 字节(因为 8 位对齐)

于 2008-09-17T09:52:06.203 回答
0

看,我完全理解我在用分配技巧做什么。

您是在告诉我位域类型的大小四舍五入到最近的字节数,这是我在最初的问题中列出的一个选项。但是你没有用引用来支持它。

特别是,我尝试了各种编译器,即使我将它应用于只有一个位的位域,它们也会给我 sizeof(int) 而不是 sizeof(char) 。

我什至不介意多个编译器随机选择他们自己对这个结构的解释。当然,位域存储分配是完全由实现定义的。

但是,我真的很想知道该构造是否可以保证工作并产生一些价值。

于 2008-09-17T09:58:45.003 回答
0

CL,我以前看过你的引用,并且同意它们是完全相关的,但即使在阅读它们之后,我也不确定代码是否已定义。

6.3.1.1.2 说关于整数促销:

是的,但整数促销规则仅适用于实际执行促销的情况。我不认为我的例子需要提升。同样,如果你这样做

char ch;
sizeof ch;

...然后 ch 也没有被提升。

我认为我们在这里直接处理位域类型。

我还看到了 gcc 输出 1,而许多其他编译器(甚至其他 gcc 版本)却没有。这并不能说服我该代码是非法的,因为大小也可以由实现定义,足以使多个编译器之间的结果不一致。

但是,我对代码是否可能未定义感到困惑,因为标准中似乎没有任何内容说明如何处理 sizeof 位域的情况。

于 2008-09-17T15:03:57.937 回答