为什么这个结构大小3而不是2?
我已经定义了这个结构:
typedef struct { char A:3; char B:3; char C:3; char D:3; char E:3; } col;
sizeof(col)
给我3的输出,但是不应该是2? 如果我只评论一个元素,那么sizeof
是2.我不明白为什么:3位的五个元素等于15位,并且小于2个字节。
定义这样一个结构是否有“内部大小”? 我只是需要澄清,因为从我迄今为止的语言概念来看,我预计的是2字节的大小,而不是3字节。
由于您使用char
作为字段的基础types,编译器会尝试按字节对位进行分组,并且由于每个字节中不能放置超过八位,因此每个字节只能存储两个字段。
你的结构使用的位数的总和是15,所以适合这么多数据的理想大小将是一个short
。
#include <stdio.h> typedef struct { char A:3; char B:3; char C:3; char D:3; char E:3; } col; typedef struct { short A:3; short B:3; short C:3; short D:3; short E:3; } col2; int main(){ printf("size of col: %lu\n", sizeof(col)); printf("size of col2: %lu\n", sizeof(col2)); }
上面的代码(对于像我这样的64位平台)确实会为第二个结构产生2
。 对于大于short
任何内容,结构体将只填充所使用types的一个元素,因此 – 对于同一个平台 – 结构体的大小为int
,长度为8,等等。
因为你不能有一个跨越最小的alignment边界(这是1个字节)的数据包字段,所以他们可能会打包
byte 1 A : 3 B : 3 padding : 2 byte 2 C : 3 D : 3 padding : 2 byte 3 E : 3 padding : 5
(在同一个字节内的字段/填充的顺序不是有意的,只是给你的想法,因为编译器可以把它们放在更喜欢的方式)
前两个位字段适合一个char
。 第三个不适合那个char
,需要一个新的char
。 3 + 3 + 3 = 9不适合8位字符。
所以第一对需要一个char
,第二对需要一个char
,最后一个字段得到第三个char
。
大多数编译器允许您控制填充, 例如使用#pragma
s 。 以下是GCC 4.8.1的一个例子:
#include <stdio.h> typedef struct { char A:3; char B:3; char C:3; char D:3; char E:3; } col; #pragma pack(push, 1) typedef struct { char A:3; char B:3; char C:3; char D:3; char E:3; } col2; #pragma pack(pop) int main(){ printf("size of col: %lu\n", sizeof(col)); // 3 printf("size of col2: %lu\n", sizeof(col2)); // 2 }
请注意,编译器的默认行为是有原因的,可能会给你更好的性能。
尽pipeANSI C标准没有详细说明位域是如何打包的,但是提供了“编译器允许打包位域但是看起来合适”的任何显着优点,但是在许多情况下,它仍然禁止编译器以最有效的方式打包文件。
特别是,如果一个结构包含位字段,编译器需要将其存储为包含一个或多个“正常”存储types的匿名字段的结构,然后将每个这样的字段逻辑地细分为其组成的位字段部分。 因此,给定:
unsigned char foo1: 3; unsigned char foo2: 3; unsigned char foo3: 3; unsigned char foo4: 3; unsigned char foo5: 3; unsigned char foo6: 3; unsigned char foo7: 3;
如果unsigned char
是8位,则编译器将被要求分配该types的四个字段,并将两个位域分配给除一个之外的所有字段(其将在它自己的char
字段中)。 如果所有的char
声明都被replace为short
,那么将会有两个short
types的字段,其中一个将保存五个位字段,另一个将保留剩下的两个字段。
在没有alignment限制的处理器上,可以通过对前五个字段使用unsigned short
和对后两个unsigned char
使用unsigned char
来更有效地布局数据,以三个字节存储七个三位字段。 虽然应该可以用三个字节存储八个三位字段,但编译器只能允许存在一个可以用作“外部字段”types的三字节数字types。
就我个人而言,我认为所定义的位域基本上是无用的。 如果代码需要使用二进制打包的数据,则应该明确定义实际types的存储位置,然后使用macros或其他一些手段访问其中的位。 如果C支持如下语法,这将是有帮助的:
unsigned short f1; unsigned char f2; union foo1 = f1:0.3; union foo2 = f1:3.3; union foo3 = f1:6.3; union foo4 = f1:9.3; union foo5 = f1:12.3; union foo6 = f2:0.3; union foo7 = f2:3.3;
如果允许的话,这样的语法将使得代码能够以便携的方式使用位字段,而不考虑字的大小或字节顺序(foo0将在f1的三个最低有效位中,但是这些可以存储在更低或更高的地址)。 然而,如果没有这样的function,macros可能是唯一可以随便操作的方式。