19

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

struct { signed int x:1; } foo;

foo.xtype 的左值int,还是别的什么?它是类型的左值似乎不自然,int因为您不能在其中存储任何类型int的值,只能存储 0 或 -1,但我找不到任何可以为其分配不同类型的语言。当然,在大多数表达式中使用,它int无论如何都会被提升为,但实际类型在 C11 中会产生差异_Generic,而且我在标准中找不到任何关于位域如何交互的语言_Generic

4

7 回答 7

5

正如 Jonathan 已经引用的那样,p5 清楚地说明了位域的类型。

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

a 中的类型_Generic应该是声明的类型(以符号故障为模),因为似乎一致认为在那里不适用算术转换。所以

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

如果X是一个无符号位域,其宽度的所有值都适合int.

于 2012-10-28T07:59:03.887 回答
3

C11 规范当然没有明确说明这一点,并且可能存在缺陷。

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

6.2.7 第 1 段说:

如果它们的类型相同,则两种类型具有兼容的类型。

6.3 第 2 段说:

将操作数值转换为兼容类型不会导致值或表示形式发生变化。

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

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

还有 6.3.1.1 第 1 段第 2 条,它说:

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

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

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

我建议提交一份关于此的缺陷报告。

于 2013-07-20T06:26:58.903 回答
3

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

该标准的相关部分是:

§6.7.2.1 结构和联合说明符

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

¶5 位字段的类型应为 、 、 或其他一些实现定义的类型的合格或非_Bool合格signed int版本unsigned int。是否允许原子类型是实现定义的。

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

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

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

脚注 125 指出:

§6.7.2 类型说明符

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

于 2012-10-28T00:34:45.817 回答
2

C11 标准在 6.7.2.1p5 中规定:

位域的类型应为 _Bool、signed int、unsigned int 或其他一些实现定义的类型的合格或非合格版本。

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

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

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

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

不幸的是,标准没有提供任何命名类型 T 的方法,所以我看不出它如何用于 a _Generic,至少不能以可移植的方式使用。C 语言的特定实现可能会提供一种机制来命名这种类型,但标准并不强制它们这样做。

于 2014-07-21T23:50:43.997 回答
2

...实际类型在 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 <stdio.h>

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
于 2016-10-13T14:38:03.740 回答
1

位域的类型是:

类型的位域T

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

在您的示例中,foo.x是 type: bit-field of type 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;        
于 2012-10-28T10:19:23.347 回答
0

我怀疑这将取决于:

  1. 编译器
  2. 优化设置
  3. 位域中的最大位数
于 2012-10-28T00:37:00.167 回答