4

我想知道为什么位域可以与联合/结构一起使用,但不能与普通变量(如intor )一起使用short
这有效:

struct foo {
    int bar : 10;
};

但这失败了:

int bar : 10; // "Expected ';' at end of declaration"

为什么此功能仅适用于联合/结构而不适用于变量?技术不一样吗?


编辑:

如果允许,您可以创建一个 3 个字节的变量,例如,无需每次都使用 struct/union 成员。这就是我使用结构的方式:

struct int24_t {
    int x : 24 __attribute__((packed));
};

struct int24_t var; // sizeof(var) is now 3
// access the value would be easier:
var.x = 123;
4

5 回答 5

7

这是一个主观问题,“为什么规范会这样说?” 但我会全力以赴。

函数中的变量通常具有“自动”存储,而不是其他持续时间之一(静态持续时间、线程持续时间和分配的持续时间)。

在结构中,您明确定义了某个对象的内存布局。但是在函数中,编译器会以某种未指定的方式自动为您的变量分配存储空间。x这里有一个问题:堆栈上占用了多少字节?

// sizeof(unsigned) == 4
unsigned x;

它可能占用 4 个字节,也可能占用 8 个字节,或 12 个字节,或 0 个字节,或者它可以同时放在三个不同的寄存器中,或者堆栈和一个寄存器,或者它可以在堆栈上获得四个位置.

关键是编译器正在为您进行分配。由于您没有进行堆栈布局,因此不应指定位宽。

扩展讨论:位域实际上有点特殊。该规范规定相邻的位域被打包到同一个存储单元中。位域实际上不是对象。

  1. 您不能sizeof()使用位字段。

  2. 您不能malloc()使用位字段。

  3. 您不能&addressof使用位字段。

所有这些事情你都可以用 C 中的对象来做,但不能用位域来做。位域是一种特殊的东西,专为结构而设计,其他任何地方都没有。

关于int24_t(更新):它适用于某些架构,但不适用于其他架构。它甚至没有一点便携性。

typedef struct {
    int x : 24 __attribute__((packed));
} int24_t;

在 Linux ELF/x64、OS X/x86、OS X/x64 sizeof(int24_t) == 3、. 但在 OS X/PowerPC 上,sizeof(int24_t) == 4.

注意 GCC 为加载生成的代码int24_t基本上等同于:

int result = (((char *) ptr)[0] << 16) |
             (((unsigned char *) ptr)[1] << 8) |
             ((unsigned char *)ptr)[2];

它是 x64 上的 9 条指令,仅用于加载单个值。

于 2012-12-19T18:24:54.677 回答
3

结构或联合的成员在其存储位置之间具有关系。由于布局的严格限制,编译器无法以巧妙的方式重新排序或打包它们以节省空间;基本上,编译器在布局结构方面的唯一自由是可以自由添加超出对齐所需数量的额外填充。位域允许您通过承诺 (1) 您不需要这些成员的地址,以及 (2) 您不需要存储某个有限范围之外的值,从而手动给编译器更多的自由来紧密打包信息。

如果您谈论的是单个变量而不是结构成员,那么在抽象机器中,它们的存储位置之间没有关系。如果它们是函数中的局部自动变量并且它们的地址从未被占用,则编译器可以随意将它们保存在寄存器中或将它们打包在内存中。手动向编译器提供此类提示几乎没有好处。

于 2012-12-19T19:06:32.980 回答
1

因为没有意义。位域声明用于在struct. 如果您没有成员,只有一个变量,它的大小是恒定的(这是实现定义的),例如,将char几乎可以肯定是 8 位宽的 a 声明为一位或十二位变量是矛盾的。

于 2012-12-19T18:21:59.417 回答
0

如果一个结构体QBLOB包含将四个 2 位位域组合成一个字节,那么与仅包含四个类型字段的结构体相比,每次使用该结构体都将节省三个字节unsigned char。如果声明一个数组QBLOB myArray[1000000],这样的数组将只占用 1,000,000 个字节;如果 QBLOB 是一个有四个unsigned char字段的结构,它就需要多 3,000,000 字节。因此,使用位域的能力可能会节省大量内存。

相比之下,在大多数体系结构中,将简单变量声明为最佳大小的位域类型与将其声明为最小的合适标准整数类型相比,最多可以节省 15 位。由于访问位域通常比访问标准整数类型的变量需要更多的代码,因此很少有将单个变量声明为位域会提供任何优势的情况。

不过,这一原则有一个明显的例外:一些架构包含的功能可以设置、清除和测试单个位,甚至比读取和写入字节的效率更高。一些此类架构的编译器包含一个bit类型,并将该类型的八个变量打包到存储的每个字节中。此类变量通常仅限于静态或全局范围,因为处理它们的专用指令可能仅限于使用某些内存区域(链接器可以确保将任何此类变量放置在它们必须去的地方)。

于 2012-12-19T20:14:26.110 回答
0

All objects must occupy one or more contiguous bytes or words, but a bitfield is not an object; it's simply a user-friendly way of masking out bits in a word. The struct containing the bitfield must occupy a whole number of bytes or words; the compiler just adds the necessary padding in case the bitfield sizes don't add up to a full word.

There's no technical reason why you couldn't extend C syntax to define bitfields outside of a struct (AFAIK), but they'd be of questionable utility for the amount of work involved.

于 2012-12-19T20:30:08.833 回答