C结构体内存对齐
2016-05-24 12:41
232 查看
引言
结构体的内存对齐问题一直以来都受到很大的关注。平常使用时,我们又sizeof()操作符可以求出结构体类型的大小,也有offsetof()宏来求取结构体对应成员变量的偏移。但仅仅会使用这两者还是不够的,我们还需要掌握其成员变量在内存中的对齐方式,以在有必要的情况下,合理排布成员变量的顺序,减少结构体类型所占字节。所以今天再次走进结构体内存对齐这个话题,探索内存对齐的规则。在下面我们先借用外力来求结构体大小以及内存对齐,然后再来分析具体细节。
sizeof()
对sizeof()要注意的一点就是是个操作符而不是函数了。相信有关操作大家也都早已清楚,那么找一个特殊的例子吧:有一个空的结构体,对它用sizeof()求大小,结果是多少?0吗?不妨在你的机子上试一下。struct test {}; int main() { int size = sizeof(struct test); return 0; }
当你满怀疑惑的运行起这段代码的时候,发现结果似乎有些出乎意料,不是0,而是1(准确的说是大部分人是1。这样的测试时平台相关的)。 为什么呢?
分析一下就知道了,假设我用这个结构体类型创建了两个结构体变量,struct test t1,t2;如果这个结构体类型大小为0,那么t1和t2将会拥有同一个地址,显然编译器此时无法区分这两个逻辑上不同的变量。所以主流的编译器大都为其分配一个字节的大小,用以占位,表名这个地址是被占了的,若是再分配的多一点,显得有些浪费了,没必要这么做。当然还有少数编译器, 它们为其分配的字节数可能会多一点,这是编译器决定的。
offsetof()
这是一个宏,在stddef.h头文件中。用来求取结构体中成员变量的偏移,用法也很简单(如下求 i 的偏移):struct test { char c; int i; }; int main() { int offset = offsetof(struct test, i); return 0; }
这个例子运行后offset的值为4.
库函数中的offsetof实现方法:
#define offsetof(s,m) (size_t)&(((s *)0)->m)
结构体内存对齐规则
1、第一个成员在与结构体变量偏移量为0的地址处。2、其它成员变量要对齐到某个数(对齐数)的整数倍的地址处。
对齐数:编译器的 默认对齐数 与 该成员大小 中的较小值(对齐数不是默认对齐数)。
VS中默认对齐数为 8
linux中默认对齐数为4
3、结构体总大小为 最大对齐数 的整数倍。
4、如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数(结构体中的成员的最大对齐数)的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
举个栗子(在VS中):
struct D { double b; char c; int i; };
b的偏移为0,因为是第一个变量。b的默认对齐数为8,b的大小为8,对齐数(前两者中较小值)为8;
c的默认对齐数为8,c的大小为1,对齐数为1,偏移为8;
i的默认对齐数为8,i的大小为4,对齐数为4,偏移为12(对齐到对齐数的整数倍处)。
计算大小为16字节,然后整体大小要为最大对齐数(8,1,4中的最大值)的整数倍,已经满足,无需补齐。如下图所示:
特殊例子(注意下面这个结构体B并没有定义一个结构体变量):
struct A { struct B { char a; };//在C语言中相当于没有外边的大括号,sizeof(struct A)==2;在C++中相当于整个B都没有,sizeof(struct A)==1 char b; };
修改默认对齐数
默认对齐数也是可以修改的:#pragrma pack (4) //修改默认对齐数为4
//code
#pragram pack () //之间默认对齐数为4
相关文章推荐
- LeetCode 271. Encode and Decode Strings
- redis主从+sentinel主从自动切换
- [WinForm]WinForm跨线程UI操作常用控件类大全
- 《linux内核设计与实现》实践之模块及深入
- 重置CentOS 7的root密码
- mysql Threads_created 增长过快的解决
- 第十三周项目—阅读程序,并运行结果(虚函数)
- Java版的双色球买彩票程序
- Android(进度条)异步更新UI的三种方式
- 【leetcode】20. Valid Parentheses
- [mac]添加系统自带辞典或下载的词典包
- ASP.NET——初步了解
- 定制班第九课 Receiver在Driver的精妙实现全生命周期彻
- Davinci DM6446开发攻略-UBOOT-2009.03移植2 nand flash的烧写
- 凌乱
- Redis常用命令
- ath10 移植记录
- iOS 苹果官方Demo合集
- 将string转为同名类名,方法名。(c#反射)
- 《善圈第27期天天圈》5.23精彩回顾:善圈微商,干货鸡汤!