我已经定义了这个结构:
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位的5个元素等于15位,而且小于2个字节。
在定义像这样的结构时是否存在“内部大小”?我只需要澄清一下,因为从我到目前为止的语言概念来看,我预计大小为2字节,而不是3字节。
答案 0 :(得分:95)
因为您使用char
作为字段的基础类型,编译器会尝试按字节对位进行分组,并且由于每个字节不能放置超过8位,因此每个字节只能存储两个字段。
你的struct使用的总和是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
的任何内容,结构将填充所使用类型的不超过一个元素,因此 - 对于同一平台 - 结构最终将为int
的大小为4,对于{{8}为8。 {1}}等等。
答案 1 :(得分:78)
因为你不能有一个跨越最小对齐边界(1个字节)的位包字段,所以它们可能会像
那样被打包byte 1
A : 3
B : 3
padding : 2
byte 2
C : 3
D : 3
padding : 2
byte 3
E : 3
padding : 5
(同一字节内的字段/填充顺序不是故意的,只是为了给你一个想法,因为编译器可以放下它喜欢的方式)
答案 2 :(得分:16)
前两位字段适合单个char
。第三个不能适合char
并且需要一个新的char
。 3 + 3 + 3 = 9,它不适合8位字符。
因此,第一对采用char
,第二对采用char
,最后一位采用第三对{{1}}。
答案 3 :(得分:15)
大多数编译器允许您控制填充e.g. using #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
}
请注意,编译器的默认行为是有原因的,可能会为您提供更好的性能。
答案 4 :(得分:9)
尽管ANSI C标准对于如何打包比特字段提供的任何明显优势都没有提供任何明显的优势,但是“编译器允许打包它们认为合适的位域”,但在许多情况下它禁止编译器以最有效的方式打包方式。
特别是,如果结构包含位域,则需要编译器将其存储为包含一个或多个某些“正常”存储类型的匿名字段的结构,然后在逻辑上将每个这样的字段细分为其组成的位域部分。因此,给出:
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位,编译器将需要分配该类型的四个字段,并为除一个之外的所有字段分配两个位域(这将位于其自己的char
字段中) 。如果所有char
声明都已替换为short
,则会有两个类型为short
的字段,其中一个字段将保留五个位域,另一个字段将保留其余两个字段。
在没有对齐限制的处理器上,通过对前五个字段使用unsigned short
并在后两个字段使用unsigned char
,可以更有效地布局数据,以三个字节存储七个三位字段。虽然应该可以在三个字节中存储八个三位字段,但是如果存在可以用作“外部字段”类型的三字节数字类型,则编译器只允许这样做。
就个人而言,我认为定义的位域基本上是无用的。如果代码需要使用二进制打包数据,则应明确定义实际类型的存储位置,然后使用宏或其他一些方法来访问其位。如果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的三个最低有效位中,但那些可能是存储在较低或较高的地址)。然而,如果没有这样的功能,宏可能是唯一可以随身携带的便携式方式。