在结构体声明中,比如:1、:7、:16或:32,冒号代表什么意思?

63

以下的C++代码是什么意思?

unsigned char a : 1; 
unsigned char b : 7;

我猜这段代码创建了两个字符a和b,它们都应该是一个字节长的,但我不知道": 1"和": 7"是什么意思。

3个回答

73

数字1和7表示限制值的范围的位大小。它们通常出现在结构体和联合体中。例如,在某些系统上(取决于char的宽度和打包规则等),以下代码:

typedef struct {
    unsigned char a : 1;
    unsigned char b : 7;
} tOneAndSevenBits;

创建一个8位值,其中1个比特用于表示a,其余7个比特用于表示b

通常在C语言中用于访问“压缩”值,例如4位的nibble可能包含在8位字符的上半部分中:

typedef struct {
    unsigned char leftFour  : 4;
    unsigned char rightFour : 4;
} tTwoNybbles;

对于我们中的语言律师,C++11标准的9.6节详细解释了这一点,稍有改述:


位域 [class.bit]

形式为

     identifieropt   attribute-specifieropt   :   constant-expression

的成员声明符指定一个位域;它的长度由冒号与位域名称分隔开。可选的attribute-specifier适用于正在声明的实体。位域属性不是类成员类型的一部分。

constant-expression必须是一个值大于或等于零的整数常量表达式。整数常量表达式的值可能大于位域类型的对象表示中的位数;在这种情况下,额外的位被用作填充位,不参与位域的值表示。

类对象内的位域分配是实现定义的。位域的对齐方式是实现定义的。位域被打包到某个可寻址的分配单元中。

注意:位域在某些机器上跨越分配单元,在其他机器上则不是。位域在某些机器上从右到左分配,在其他机器上则从左到右。- 注解


1
严格来说,在纯C中,它们不会与“unsigned char”一起使用。 C只允许在位域声明中使用“int”,“signed int”和“unsigned int”。 C99添加了“_Bool”。在C中,“unsigned char”不是位域的合法类型。 - AnT stands with Russia
1
@paxdiablo:是的,但“某些其他实现定义类型”部分真的是多余的,因为实现始终可以以任何它认为合适的方式扩展语言,而不需要在标准的特定部分中明确许可。 C实现可以在C代码中编译Pascal和Fortran代码-没有人禁止它这样做。然而,由于这个原因将Fortran代码视为可能有效的C代码似乎有些奇怪。同样的道理也适用于“实现定义类型”。 - AnT stands with Russia
@AndreyT:然而,这种措辞非常普遍;它记录了常见的、合理的扩展。 - MSalters
4
@AndreyT,这里的“实现定义类型”并不完全是多余的。如果没有这个,编译器就必须发出一条错误提示,因为违反了限制条件。而“实现定义类型”允许编译器接受其他类型而不发出错误提示。 - Johannes Schaub - litb
@jalf:不,MSVC不会在这里插入填充。只要声明的类型保持不变,MSVC就会继续填充最后一个单元,直到它被填满,并在类型改变时插入填充(或更准确地说,切换到新的分配单元)。 - AnT stands with Russia
显示剩余4条评论

9
我认为这些应该是位域(bitfields)。

13
不确定,除非是简洁或不确定性,但既然你技术上是正确的并且为了分享爱心,所以给你点赞。 - paxdiablo

1
严格来说,位域必须是int、unsigned int或_Bool类型,尽管大多数编译器将接受任何整数类型。
参考C11 6.7.2.1:

位域的类型应为_Bool、signed int、unsigned int或某种其他实现定义的类型的限定或未限定版本。

您的编译器可能会分配1个字节的存储空间,但它可以自由地获取更多空间。
参考C11 6.7.2.1:

一个实现可以分配任何可寻址的存储单元,以容纳位域。

当您有多个位域在声明后面时,节省存储空间。在这种情况下,如果可能,分配的存储空间将被打包。
参考C11 6.7.2.1:

如果有足够的空间,紧随结构体中另一个位域后面的位域将被打包到同一单元的相邻位中。如果剩余空间不足,则不适合的位域是放入下一个单元还是重叠相邻单元是实现定义的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接