C结构体中的“:”代表什么意思?

144
struct _USBCHECK_FLAGS
    {
        unsigned char   DEVICE_DEFAULT_STATE       : 1;
        unsigned char   DEVICE_ADDRESS_STATE       : 1;
        unsigned char   DEVICE_CONFIGURATION_STATE : 1;
        unsigned char   DEVICE_INTERFACE_STATE     : 1;
        unsigned char   FOUR_RESERVED_BITS         : 8;
        unsigned char   RESET_BITS                 : 8;
    } State_bits;

:1:8是什么意思?


9
C语言中的位域位域是C语言中一种数据结构,它允许程序员在一个字节(byte)的空间内存储多个不同长度的变量。使用位域可以有效地减小数据结构的大小,从而节约内存。这对于嵌入式系统等资源受限的环境尤为重要。定义位域需要指定所占用的位数以及变量名。访问位域时,可以直接使用其变量名,编译器会自动将该变量与相应的位偏移量进行配对。需要注意的是,由于不同的机器可能具有不同的字节序,因此在使用位域时需要特别小心。 - wkl
请参考http://en.wikipedia.org/wiki/Bit_field以获取示例。 - Martin Beckett
80
四个保留位 : 8 - RJFalconer
3个回答

107
这些是位域。基本上,冒号后面的数字描述了该域使用的位数。这里有一段来自MSDN的引用描述位域:

常量表达式指定字段的位宽。声明符的类型说明符必须为unsigned int、signed int或int,常量表达式必须为非负整数值。如果值为零,则声明没有声明符。不能使用位域数组、位域指针和返回位域的函数。可选的声明符命名位域。位域只能作为结构的一部分声明。取地址运算符(&)不能应用于位域组件。

无名位域无法被引用,其运行时内容是不可预测的。它们可以用作“虚拟”字段,用于对齐目的。宽度为0的无名位域保证在struct-declaration-list中跟随它的成员的存储始于int边界。

此示例定义了一个名为screen的结构的二维数组。

struct 
{
    unsigned short icon : 8;
    unsigned short color : 4;
    unsigned short underline : 1;
    unsigned short blink : 1;
} screen[25][80];

编辑:MSDN链接中的另一要点:

位域具有与整数类型相同的语义。这意味着,在表达式中,无论位域中有多少位,它都会像同一基本类型的变量一样使用。

一个简单示例可以很好地说明这一点。有趣的是,对于混合类型,编译器似乎默认为sizeof(int)

  struct
  {
    int a : 4;
    int b : 13;
    int c : 1;
  } test1;

  struct
  {
    short a : 4;
    short b : 3;
  } test2;

  struct
  {
    char a : 4;
    char b : 3;
  } test3;

  struct
  {
    char a : 4;
    short b : 3;
  } test4;

  printf("test1: %d\ntest2: %d\ntest3: %d\ntest4: %d\n", sizeof(test1), sizeof(test2), sizeof(test3), sizeof(test4));

测试1: 4

测试2: 2

测试3: 1

测试4: 4


我还是不太明白。这是否意味着C++会自动将您的“变量”合并为int大小,以便使用所有位?您能否将位域结构转换为数字以获得典型的“带标志的int”? - Tomáš Zato
@TomášZato 我已经为您更新了我的答案,并提供了更多信息。 - JoeFish
1
@JoeFish,你能详细说明一下test1-4的合并细节吗? - smwikipedia
1
我不明白为什么在Darwin Kernel Version 17.0.0 x86_64上,test4: 4变成了test4: 2 - Aleksey
@Aleksey 请记住,关于位域的几乎所有内容都取决于实现。它取决于硬件架构和结构成员对齐模型。 - Kevin Chou
2
@smwikipedia 在这里提供了简单易懂的原则:如果int类型的两个字段可以适合一个单独的int(例如上面的32位),编译器只分配一个int大小的内存(test_1,short类型的test_2,char类型的test3)。在上面的示例中被忽略的是,如果一个单独的int不能再容纳位域,我们就添加第二个。请参考此示例以获取更详细的解释。 - Kevin Chou

25

我也遇到了冒号标记,但在我的情境下,位字段并没有意义。所以我进行了一些研究。这种标记也用于赋值,特别是对于指向函数的指针等情况。

来源:http://www.tldp.org/LDP/lkmpg/2.4/html/c577.htm

以下是一个示例和摘录以进行解释。

"有一个gcc扩展可以使赋值给这个结构更加方便。你会在现代驱动程序中看到它,并可能感到惊讶。下面是新的结构赋值方式的样子:"

struct file_operations fops = {
   read: device_read,
   write: device_write,
   open: device_open,
   release: device_release
};

C99(旧的,兼容的)方式如下:

struct file_operations fops = {
   .read = device_read,
   .write = device_write,
   .open = device_open,
   .release = device_release
};

我也看到过这个!我一直以为gcc扩展是.x = y,然后标准采用了它,直到后来我看到了x: y的表示法。我认为标准的那种看起来更好。 - Shahbaz
10
首先,这不是任务,而是“初始化”。其次,你所描述的特性(指定初始化器)与问题(位域)完全没有关联。仅仅因为过时的GCC扩展在语法中使用了“:”并不意味着它与问题中的“:”有任何关系。 - AnT stands with Russia
24
AnT,有人搜索“C 结构体初始化冒号”可能会看到这个答案,而且正好是我在找的。感谢 user27346! - ChrisPhoenix
2
后来的读者需要注意:在C语言结构体声明中出现的冒号:和在定义时进行初始化的冒号:是不同的东西。 - Kevin Chou

1

它定义了宽度为1和8的位域。


37
虽然准确,但回答还应该解释什么是位域(bitfield)。 - Mooing Duck

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