2

我确实有一个包含位字段的结构。根据我的说法,它是 2 个字节,但结果是 4 个。我已经在 stackoverflow 上阅读了一些与此相关的问题,但无法与我的问题相关。这是我有的结构

struct s{
char b;
int c:8;
};
int main()
{
printf("sizeof struct s = %d bytes \n",sizeof(struct s));
return 0;
}

如果 int 类型必须在其内存边界上,那么输出应该是 8 个字节,但它显示 4 个字节?

4

5 回答 5

1

int c:8;表示您正在声明一个bit-field大小为 8 位的 a。由于 32 位系统上的 alignemt 通常为 4 个字节(=32 位),因此您的对象将显示为 4 个字节而不是 2 个字节(char + 8 位)。

于 2011-08-19T12:18:51.130 回答
1

在此处输入图像描述

资料来源:http ://geeksforgeeks.org/?p=9705

总之:它在不影响对齐的情况下尽可能地优化位的打包(这就是位字段的含义)。

变量的数据对齐处理数据存储在这些银行中的方式。例如 int 在 32 位机器上的自然对齐是 4 个字节。当数据类型自然对齐时,CPU 会在最短的读取周期内获取它。

类似地,自然对齐short int是 2 个字节。这意味着,一个短整数可以存储在 bank 0 – bank 1 pair 或 bank 2 – bank 3 pair 中。Adouble需要 8 个字节,并且在存储体中占据两行。double 的任何未对齐都会强制两个以上的读取周期来获取 double 数据。

请注意,双变量将在 32 位机器上的 8 字节边界上分配,并且需要两个内存读取周期。在 64 位机器上,根据存储体的数量,双变量将分配在 8 字节边界上,并且只需要一个内存读取周期。

所以编译器会为每个结构引入对齐要求。它将是该结构中最大的成员。如果你char从你的 中删除struct,你仍然会得到4 个字节

在您的struct,char中是 1 个字节对齐的。它后面是一个int位域,它是 4 字节对齐的整数,但您定义了一个位域。

8 位 = 1 个字节。Char可以是任何字节边界。所以Char+ Int:8= 2 个字节。嗯,这是一个奇数字节边界,因此编译器添加了额外的 2 个字节来维持 4 字节边界。

对于 8 个字节,您必须声明一个实际的int(4 个字节)和一个char(1 个字节)。那是5个字节。嗯,这是另一个奇数字节边界,所以struct填充到8 bytes

我过去通常做的控制填充是在我之间放置填充符struct以始终保持 4 字节边界。所以如果我有struct这样的:

struct s {
    int id;
    char b;
};

我将插入分配如下:

struct d {
    int id;
    char b;
    char temp[3];
}

这将给我一个struct大小为 4 字节 + 1 字节 + 3 字节 = 8 字节!通过这种方式,我可以确保以struct我想要的方式填充我的内容,特别是如果我通过网络将它传输到某个地方。此外,如果我改变了我的实现(例如,如果我可能将它保存struct到一个二进制文件中,填充物从一开始就在那里,所以只要我保持我的初始结构,一切都很好!)

最后,您可以阅读这篇关于C Structure size with bit-fields的文章以获得更多解释。

于 2011-08-19T16:49:07.623 回答
0

但是如果你指定c应该占用 8 位,它就不是一个真正的 int,是吗?c+的大小b为 2 个字节,但您的编译器将结构填充为 4 个字节。

于 2011-08-19T12:17:49.617 回答
0

它们在结构中的字段对齐方式取决于编译器/平台。也许您的编译器对长度小于或等于 16 位的位域使用 16 位整数,也许它永远不会在小于 4 字节边界的任何东西上对齐结构。

底线:如果您想知道结构字段是如何对齐的,您应该阅读您正在使用的编译器和平台的文档。

于 2011-08-19T12:22:47.153 回答
0

在通用的、独立于平台的 C 语言中,您永远无法知道结构/联合的大小,也无法知道位域的大小。编译器可以在结构/联合/位域内的任何地方随意添加任意数量的填充字节,除了第一个内存位置。

此外,编译器还可以自由地将任意数量的填充添加到位域,并且可以将它们放在它喜欢的任何位置,因为哪个位是 msb,而 lsb 不是由 C 定义的。

当涉及到位域时,C 语言会冷落你,它们没有标准。您必须详细阅读编译器文档以了解它们在您的特定平台上的行为方式,并且它们是完全不可移植的。

明智的解决方案是永远不要使用位域,它们是 C 的冗余特性。相反,使用位运算符。按位运算符和深入记录的位域将生成相同的机器代码(未记录的位域可以自由地产生相当任意的代码)。但是按位运算符可以保证在世界上的任何编译器/系统上都能正常工作。

于 2011-08-19T14:17:21.453 回答