5

可能重复:
为什么结构的 sizeof 不等于每个成员的 sizeof 之和?

我试图理解位域的概念。但我无法找到为什么 CASE III 中以下结构的大小为 8 个字节。

案例一:

struct B    
{
    unsigned char c;  // +8 bits
} b;

大小(b);// 输出:1(因为 unsigned char 在我的系统上占用 1 个字节)

案例二:

struct B
{
    unsigned b: 1;
} b;

 sizeof(b); // Output: 4 (because unsigned takes 4 bytes on my system)

案例三:

struct B
{
    unsigned char c;  // +8 bits
    unsigned b: 1;    // +1 bit
} b;

sizeof(b); // Output: 8 

我不明白为什么案例 III 的输出为 8。我期待 1(char) + 4(unsigned) = 5。

4

6 回答 6

7

您可以使用 来检查结构的布局offsetof,但大致如下:

struct B
{
    unsigned char c;  // +8 bits
    unsigned char pad[3]; //padding
    unsigned int bint; //your b:1 will be the first byte of this one
} b;

现在,很明显(在 32 位架构中)sizeof(b)will be 8,不是吗?

问题是,为什么 3 个字节的填充,而不是更多或更少?

答案是字段在结构中的偏移量与字段本身的类型具有相同的对齐要求。在您的体系结构中,整数是 4 字节对齐的,因此offsetof(b, bint)必须是 4 的倍数。它不能为 0,因为有c前面,所以它将是 4。如果字段bint从偏移量 4 开始并且长度为 4 字节,那么结构的大小为 8。

另一种看待它的方式是,结构的对齐要求是其任何字段中最大的,因此这B将是 4 字节对齐的(因为它是您的位字段)。但是一个类型的大小必须是对齐的倍数,4是不够的,所以会是8。

于 2012-10-06T18:19:02.377 回答
3

我认为您在这里看到了对齐效果。

许多体系结构要求将整数存储在内存中字长倍数的地址处。

这就是为什么你的第三个结构中的 char 被填充了三个字节,以便下面的无符号整数从一个是字长倍数的地址开始。

于 2012-10-06T17:23:23.157 回答
2

Char 根据定义是一个字节。int 在 32 位系统上是 4 个字节。并且该结构被填充了额外的 4。

有关填充的一些解释,请参见http://en.wikipedia.org/wiki/Data_structure_alignment#Typical_alignment_of_C_structs_on_x86

于 2012-10-06T17:20:23.047 回答
1

为了保持对内存的访问对齐,如果您打包结构,编译器将添加填充,它不会添加填充。

于 2012-10-06T17:22:58.643 回答
1

结构的对齐方式和总大小是特定于平台和编译器的。您不能在这里期待直接且可预测的答案。编译器总能有一些特别的想法。例如:

struct B
{
    unsigned b0: 1;    // +1 bit
    unsigned char c;  // +8 bits
    unsigned b1: 1;    // +1 bit
};

编译器可以将字段 b0 和 b1 合并为一个整数,也可以不合并。这取决于编译器。一些编译器有控制这个的命令行键,一些编译器没有。其他示例:

struct B
{
    unsigned short c, d, e;
};

由编译器来打包/不打包这个结构的字段(假设是 32 位平台)。结构的布局在 DEBUG 和 RELEASE 版本之间可能不同。

我建议只使用以下模式:

struct B
{
    unsigned b0: 1;
    unsigned b1: 7;
    unsigned b2: 2;
};

当您具有共享相同类型的位字段序列时,编译器会将它们放入一个 int 中。否则,各个方面都会发挥作用。还要考虑到,在一个大项目中,您编写一段代码,而其他人将编写和重写 makefile;将您的代码从一个 dll 移动到另一个。此时编译器标志将被设置和更改。这些人有 99% 的机会不知道您的结构的对齐要求。他们甚至永远不会打开您的文件。

于 2012-10-06T17:56:05.690 回答
1

我又看了一遍,这就是我发现的。

  1. 来自 C 书,“关于字段的几乎所有内容都依赖于实现。”
  2. 在我的机器上:
 struct B {
    unsigned c: 8;
    unsigned b: 1;
}b;
printf("%lu\n", sizeof(b));

print 4 是一个短的;

您将位字段与常规结构元素混合在一起。

顺便说一句,位字段定义为:“单个实现定义的存储单元中的一组相邻”所以,我什至不确定 ':8' 是否符合您的要求。这似乎不符合位域的精神(因为它不再有点)

于 2012-10-06T17:59:44.167 回答