> 文档中心 > C语言结构体的大小计算和位段

C语言结构体的大小计算和位段

在计算结构体的大小的时候,我们需要考虑内存对齐问题,这也是比较热门的考点之一

结构体内存对齐的规则(本篇文章全部以VS编译器来做示例)

我们在编译器中可以使用#pragma pack(8)来自行设定默认对齐数,#pragma pack()还可以用这段代码来取消自行设定默认对齐数

1. 第一个成员在与结构体变量偏移量为0的地址处。 2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值 VS中默认的值为8 3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。 4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

1.结构体的大小计算

1.1普通结构体的大小计算

这里我们可以用一段代码来做示例

struct S1{ char c1; int i; char c2;};

这是C语言中定义的一个结构体,那他的大小怎么来计算呢?

首先我们用一个图来解释

首先我们遵从第一条,让第一个放在偏移量为0的位置

然后c1占用一个字节,所以i要从偏移量为1的位置开始找

然后根据第二条,取最小偏移量,默认的偏移量为8,而int的偏移量为4,所以int应该放到偏移量为4的位置,然后int占用4个字节,依次给他4个空间

最后看c2,取最小偏移量,默认值为8,而char的偏移量为1,所以char应该放到偏移量为8的位置,然后char占用1个字节,依次开1个空间

然后看0-8一共占用了9个字节,在根据第三条规则这个结构体中的最大偏移量是int的4,所以结构体的大小应该为4的倍数,应该是12,所以这个结构体所占用的字节数是12个字节

但是如果我们对代码进行改进,变成这样

struct S2{ char c1; char c2; int i;};

 根据上面的方法可以得到,结构体的大小变成了8,也就节省了4个字节的空间,所以我们定义结构体的时候,最好把占用空间较小的变量放到前面,这样可以提高内存的利用率

1.2嵌套结构体的大小计算

看一段代码

struct S3{ double d; char c; int i;};struct S4{char c1;struct S3 s3;double d;};printf("%d\n", sizeof(struct S4));

这里我们可以看到,S4进行了结构体的嵌套使用,那么这种情况下应该怎么计算呢?

首先我们用前面的方法可以计算出S3的最大对齐数位8,占用的空间16个字节

然后嵌套之后,看上面的第四条规则:

如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。 也就是说S4里面嵌套的S3的对齐数应该是8,大小是16个字节,我们依旧用一个图来理解

 这里我们套用上面的几条规则之后,得出的结果应该是32个字节,如果你不相信我就去测试一下

可以看到,我们的结果完全正确

1.3为什么会存在内存对齐

 

大部分的参考资料都是如是说的: 1. 平台原因 ( 移植原因 ) 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特 定类型的数据,否则抛出硬件异常。 2. 性能原因 数据结构 ( 尤其是栈 ) 应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。 总体来说: 结构体的内存对齐是拿 空间 来换取 时间 的做法。

2.位段

位段的基本要求

1. 位段的成员必须是 int unsigned int signed int 或者char 2. 位段的成员名后边有一个冒号和一个数字。 比如:

struct A { int _a:2; int _b:5; int _c:10; int _d:30;};

位段的基本大小是以比特位单位的

1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型 2. 位段的空间上是按照需要以 4 个字节( int )或者 1 个字节( char )的方式来开辟的。 3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。这个代码的大小应该是8个字节,因为一次开4个,用完再开4个,所以应该是4的整数倍