【C语言】结构体的内存对齐

在上一篇中我们已经掌握了结构体的基本使用。
现在我们深入讨论一个问题:计算结构体的大小。
我们来看下面一串代码,想一想它们的结果分别是什么?

1、练习

// 练习 1
struct S1
{
char c1 ;
int i ;
char c2 ;
};
printf ( "%d\n" , sizeof ( struct S1 ));
// 练习 2
struct S2
{
char c1 ;
char c2 ;
int i ;
};
printf ( "%d\n" , sizeof ( struct S2 ));
// 练习 3
struct S3
{
double d ;
char c ;
int i ;
};
printf ( "%d\n" , sizeof ( struct S3 ));
// 练习 4- 结构体嵌套问题
struct S4
{
char c1 ;
struct S3 s3 ;
double d ;
};
printf ( "%d\n" , sizeof ( struct S4 ));
答案:练习1:12
          练习2:8
          练习3:16
          练习4:32

结构体的大小为什么不是每个结构体成员大小相加?
比如S1为什么不是1+4+1=6,而是12

2、结构体对齐规则

   结构体的大小如何计算?

   首先得掌握结构体的对齐规则

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

 根据结构体的对齐规则回顾练习

练习1中

char c1;//c1在偏移量为0的位置

int i ;      // 根据规则2,int的大小为4,小于vs默认的4,所以偏移从4的整数倍开始,即i从4开
              始
char c2 ;// 现在偏移量为 1 (c1) +3 (因对其浪费的3个字节) +4 (int) =8, 根据规则2,c2的偏移量为9
因为结构体是最大对齐数的整数倍(在这里是int 4),所以c2后面又浪费3个字节,来确定对齐
结构体总大小是1 + 3 + 4 + 1 + 3 = 12

 同理,

练习2:

结构体总大小是1 + 1 + 2 + 4 = 8(最大对齐数为4)

练习3:

结构体总大小是8 + 1 + 3 + 4 = 16(最大对齐数为8)

练习4:

结构体总大小是1 + 7 + 16(8+1+3+4) + 8 = 32(最大对齐数为8)

//s1和s2存放的内容相同,但是占的字节数不同

3、为什么存在内存对齐?

大部分的参考资料都是如是说的:
1. 平台原因 ( 移植原因 )
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特
定类型的数据,否则抛出硬件异常。
2. 性能原因
数据结构 ( 尤其是栈 ) 应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访
问。
练习1中,
  • 32位的编译器一次内存访问读取4个字节,
  • 访问c1后,如果没有内存对齐,会从偏移量为0(没有偏移量,只是方便理解)的位置继续读取i,读取4个字节,
  • 但i的字节只读取了3个,还要再进行一次内存访问,访问4个字节,但是读取到i的字节只有一个
如果有内存对齐,则会在访问i时,直接从第四个字节开始访问,只需要访问一次

总体来说:
结构体的内存对齐是拿 空间 来换取 时间 的做法。
那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:
让占用空间小的成员尽量集中在一起。

例如前面的练习中S1S2类型的成员一模一样但是S1S2所占空间的大小有了一些区别 

4、修改默认对齐数 

//#pragma 预处理指令,可以改变默认对齐数

#include <stdio.h>
#pragma pack(8) // 设置默认对齐数为 8
struct S1
{
char c1 ;
int i ;
char c2 ;
};
#pragma pack() // 取消设置的默认对齐数,还原为默认
#pragma pack(1) // 设置默认对齐数为 1
struct S2
{
char c1 ;
int i ;
char c2 ;
};
#pragma pack() // 取消设置的默认对齐数,还原为默认
int main ()
{
    // 输出的结果是什么?
    printf ( "%d\n" , sizeof ( struct S1 ));
    printf ( "%d\n" , sizeof ( struct S2 ));
    return 0 ;
}

结论:
结构在对齐方式不合适的时候,我么可以自己更改默认对齐数。(一般是2的次方)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值