sizeof(bitfield_type)在ANSI C中合法吗?

时间:2008-09-17 09:00:32

标签: c

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

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

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

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

13 个答案:

答案 0 :(得分:4)

您是对的,整数促销不会应用于sizeof的操作数:

  

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

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

约瑟夫迈尔斯告诉我:

  

结论   从C90 DR开始,位字段有自己的类型,并且来自C99 DR   离开是否有他们自己的类型实现定义,和   GCC遵循C90 DR,因此赋值类型为int:1而不是   被提升为sizeof的操作数。

这已在Defect Report #315中讨论过。

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

答案 1 :(得分:3)

C99标准(PDF of latest draft)在第6.5.3.4节中说明sizeof约束:

  

sizeof运算符不应用于具有函数类型的表达式   不完整的类型,这种类型的括号名称,或表达式   指定一个位字段成员。

这意味着允许将sizeof应用于分配表达式

6.5.2.3说:

  

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

6.3.1.1.2关于整数促销的说法:

  

如果可以使用int或unsigned int,则可以在表达式中使用以下内容:

     
      
  • ...
  •   
  • 类型为_Boolintsigned intunsigned int的位字段。
  •   
     

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

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

是否有任何编译器没有这个?

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

岂不

(f.x = 1)

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

sizeof( f.x = 1)

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

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

文章还解释了sizeof适用于表达式。

答案 4 :(得分:0)

sizeof( f.x = 1)

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

答案 5 :(得分:0)

不,你必须考虑使用==运算符,它在C中产生一个int类型的“boolean”表达式,在C ++中实际上是bool。

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

因此,我们实际上可以访问位域类型。

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

我特意使用“f.x = 1”,因为“sizeof f.x”占用了一个位域左值,这显然是不允许的。

答案 6 :(得分:0)

(f.x = 1)

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

unsigned x:1

有1位,其sizeof返回1个字节(8位对齐)

如果你愿意的话

unsigned x:12

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

答案 7 :(得分:0)

  

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

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

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

gcc肯定不同意sizeof(bitfield_type)应该与sizeof(int)相同的断言,但仅限于某些平台。

答案 8 :(得分:0)

  

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

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

这就是我想要找到的。你是否认为遗漏未定义,或者有没有明确宣称它是非法的?

答案 9 :(得分:0)

  

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

首先, IS 包含赋值运算符的表达式。

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

  

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

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

答案 10 :(得分:0)

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

struct foo { unsigned x:12} f;

然后将类似1的值写入f.x - 由于对齐,它使用2个字节。如果你做了像

这样的作业
f.x = 1;

并返回指定的值。这与

非常相似
int a, b, c;
a = b = c = 1;

从右到左评估对齐。 c = 1将1分配给变量c,此asignment返回指定的值并将其分配给b(依此类推),直到将1分配给

等于

a = ( b = ( c = 1 ) )

在你的情况下,sizeof得到你的asignment的大小, NOT 一个位域,但是分配给它的变量。

sizeof ( f.x = 1)

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

答案 11 :(得分:0)

看,我完全理解我正在做的任务技巧。

您告诉我,位域类型的大小向上舍入到cloest字节数,这是我在初始问题中列出的一个选项。但你没有用参考文献支持它。

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

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

但是,我确实想知道构造是否保证才能工作并产生某些值。

答案 12 :(得分:0)

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

  

6.3.1.1.2关于整数促销的说法:

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

char ch;
sizeof ch;

...然后ch也没有升职。

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

我也看过gcc输出1,而许多其他编译器(甚至其他gcc版本)都没有。这并不能让我相信代码是非法,因为大小也可以实现定义足以使结果在多个编译器中不一致。

但是,我对代码是否未定义感到困惑,因为标准中的任何内容似乎都没有说明如何处理sizefield的大小写。