比特域的类型是什么?

我在C标准中找不到指定的任何地方。 例如,在

struct { signed int x:1; } foo; 

foo.xint类型的左值还是其他什么? 因为你不能在其中存储int类型的任何值,只有0或-1,但是我找不到任何可以为它分配不同类型的语言,因此它似乎不自然地成为int类型的左值。 当然,在大多数表达式中使用它,无论如何它都会被提升为int ,但是实际的类型在C11中与_Generic有所不同,我在标准中找不到任何关于bitfields如何与_Generic交互的语言。

正如Jonathan所引用的那样,p5清楚地说明了比特字段的类型。

您还应该记住的是,6.3.1.1中的位域算术转换有一个特殊规则,基本上说明如果int可以表示所有值,则在大多数表达式中,这样的位字段将转换int

_Generic中的类型应该是声明的类型(模符号模糊),因为似乎是算术转换不适用的共识。 所以

 _Generic((X), int: toto, unsigned: tutu) _Generic(+(X), int: toto, unsigned: tutu) 

如果X是无符号位域,其宽度使所有值都适合int ,则可以给出不同的结果。

鉴于您包含了有signed限定符,那么可以存储在1位位字段中的唯一值确实为-1和0.如果省略了限定符,那么实现将定义是否为’ int位字段已签名或未签名。 如果你指定了unsigned int ,那么值当然是0和+1。

该标准的相关部分是:

§6.7.2.1结构和联合说明符

¶4指定位域宽度的表达式应为整数常量表达式,其非负值不超过指定类型的对象的宽度,冒号和表达式省略。 122)如果该值为零,则声明不应具有声明者。

¶5位字段的类型应为_Boolsigned intunsigned int或其他实现定义类型的限定或非限定版本。 它是实现定义的,是否允许primefaces类型。

¶10位字段被解释为具有由指定位数组成的有符号或无符号整数类型。 125)如果将值0或1存储到_Bool类型的非零宽度位字段中,则位字段的值应等于存储的值; _Bool位字段具有_Bool的语义。

122)虽然_Bool对象中的位数至少为CHAR_BIT ,但_Bool的宽度(符号和值位数)可能只有1位。

125)如上面6.7.2中所规定的,如果使用的实际类型说明符是int或定义为int的typedef-name,则无论位字段是有符号还是无符号,它都是实现定义的。

脚注125指出:

§6.7.2类型说明符

¶5每个逗号分隔的多重集都指定相同的类型,除了对于位域,它是实现定义的,指定符int指定与signed int相同的类型还是与unsigned int相同的类型。

C11规范当然不会明确这一点,也许是有缺陷的。

我相信foo.x是一个非int类型的左值,但我的理由相当弱:

6.2.7第1段说:

如果类型相同,则两种类型具有兼容类型

6.3第2段说:

将操作数值转换为兼容类型不会导致值或表示forms发生更改。

如果foo.xint类型的左值,那么它将与其他int兼容,因此foo.x = 5应该导致foo.x具有值5 (每6.3p2)。 这显然不会发生,表明foo.xint不兼容,这表明foo.x不是int类型的左值。

foo.xint不兼容并没有foo.x意义。 也许没有转换(在6.3.1意义上),并且foo.x通过标准中未讨论的某种机制获得其值。 或者我可能误解了“算术操作数”的含义,而6.3.1不适用于左值。

还有6.3.1.1第1段子弹2,其中说:

  • 有符号整数类型的等级应大于精度较低的任何有符号整数类型的等级。

foo.x精度低于普通的int (当用作左值时,不是当它“转换为存储在指定对象中的值”时,如6.3.2.1p2中所述),因此它必须具有不同的整数转换等级。 这也表明它不是一个int

但我不确定我的解释是否有效或符合委员会的意图。

我建议提交一份有关此问题的缺陷报告。

C11标准在6.7.2.1p5中说明:

位字段的类型应为_Bool,signed int,unsigned int或其他实现定义类型的限定或非限定版本。

这是一个约束,这意味着,如果程序声明一个类型不属于上述类别的位字段,则必须打印诊断。

然而,它继续在6.7.2.1p10中说:

位字段被解释为具有由指定位数组成的有符号或无符号整数类型。

我相信他们的意思是,虽然你必须用signed int x:n声明bit字段,但是左值表达式foo.x的类型是一些其他有符号整数类型,称之为T.T是有符号整数类型由n位组成,必须符合Sec中给出的所有有符号整数类型的约束。 6.2.6。 但是T不一定与名为signed int的标准有符号整数类型兼容。 (事实上​​,T可能与该标准类型兼容的唯一情况是,n恰好等于有符号整数中的位数。)

不幸的是,标准没有提供任何命名类型T的方法,所以我看不出它如何在_Generic ,至少,不是以便携方式。 C语言的特定实现可以提供命名此类型的机制,但标准不强制它们。

…实际类型在C11与_Generic有所不同

1编译器如何处理位字段类型的结果如下所示。

8位和32位字段与通常的嫌疑人相匹配。

1位位域的类型是什么? 正如其他人所引用的那样,它的“名称”没有明确规定,但它不是任何预期的标准类型。

这并未引用规范,但确实展示了受尊重的编译器如何解释C规范。


GNU C11(GCC)版本5.3.0(i686-pc-cygwin)
由GNU C版本5.3.0,GMP版本6.1.0,MPFR版本3.1.4,MPC版本1.0.3编译

 #define info_typename(X) _Generic((X), \ _Bool: "_Bool", \ unsigned char: "unsigned char", \ signed char: "signed char", \ char: "char", \ int: "int", \ unsigned : "unsigned", \ default: "default" \ ) #define TYPE_NAME(t) { printf("%s\n", info_typename(t)); } #include  int main() { struct { signed int x1 :1; signed int x8 :8; signed int x32 :32; _Bool b; signed char sc; char c; unsigned char uc; int i; unsigned u; } foo; TYPE_NAME(foo.b); TYPE_NAME(foo.sc); TYPE_NAME(foo.c); TYPE_NAME(foo.uc); TYPE_NAME(foo.i); TYPE_NAME(foo.u); TYPE_NAME(foo.x1); TYPE_NAME(foo.x8); TYPE_NAME(foo.x32); } 

产量

 _Bool signed char char unsigned char int unsigned default (int:1) signed char (int:8) int (int:32) 

请注意-Wconversion和下面的代码,我收到此警告。 这至少是一个编译器调用它的小位字段类型: unsigned char:3

警告:从’unsigned int’转换为’unsigned char:3’可能会改变其值[-Wconversion]

  struct { unsigned int x3 :3; } foo; unsigned u = 1; foo.x3 = u; // warning 

我怀疑这取决于:

  1. 编译器
  2. 优化设置
  3. 位字段中的最大位数

位域的类型是:

类型为T位域

其中T_Boolintsigned intunsigned int或某些实现定义的类型。

在您的示例中, foo.x的类型为: signed int类型的位字段。

这与signed int不同,因为这两种类型不共享相同的约束和要求。

例如:

 /* foo.x is of type bit-field of type signed int */ struct { signed int x:1; } foo; /* y is of type signed int */ signed int y; /* valid, taking the size of an expression of type signed int */ sizeof y; /* not valid, taking the size of an expression of type bit-field * of signed int */ sizeof foo.x; /* valid, taking the address of a lvalue of type signed int */ &y; /* invalid, taking the address of a lvalue of type bit-field * of signed int */ &foo.x;