C语言-结构体内存对齐
C语言结构体对齐也是老生常谈的话题了。基本上是面试题的必考题。内容虽然很基础,但一不小心就会弄错。写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢?
开始学的时候,也被此类问题困扰很久。其实相关的文章很多,感觉说清楚的不多。结构体到底怎样对齐?
有人给对齐原则做过总结,具体在哪里看到现在已记不起来,这里引用一下前人的经验(在没有#pragma pack宏的情况下):
原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。
例1:
struct {short a1;short a2;short a3; } A;struct{long a1;short a2; } B;
sizeof(A) = 6; 这个很好理解,三个short都为2。
sizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。
例2:
struct A{int a;char b;short c; };struct B{char b;int a;short c; };
sizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。
sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。
深究一下,为什么是这样,我们可以看看内存里的布局情况。
a b c
A的内存布局:1111, 1*, 11
b a c
B的内存布局:1***, 1111, 11**
其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。
B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。
再看一个结构中含有结构成员的例子:
例3:
struct A{int a;double b;float c; };struct B{char e[2];int f;double g;short h;struct A i; };
sizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。
sizeof(B) = 48; 看看B的内存布局。
e f g h i
B的内存布局:11* *, 1111, 11111111, 11 * * * * * *, 1111* * * *, 11111111, 1111 * * * *
i其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。把B的内存布局弄清楚,有关结构体的对齐方式基本就算掌握了。
以上讲的都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) = 16; sizeof(B) = 32;
有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。
a b c
A的内存布局:1111, 11111111, 1111
e f g h i
B的内存布局:11, 1111, 11111111, 11 , 1111, 11111111, 1111
那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。
还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。
使用位域的主要目的是压缩存储,其大致规则为:
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
还是让我们来看看例子。
例4:
struct A{char f1 : 3;char f2 : 4;char f3 : 5; };
a b c
A的内存布局:111, 1111 *, 11111 * * *
位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。
例5:
struct B{char f1 : 3;short f2 : 4;char f3 : 5; };
由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。
例6:
struct C{char f1 : 3;char f2;char f3 : 5; };
非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。
考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体 系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据 的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。
最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间。
转载于:https://www.cnblogs.com/JohnABC/p/4704789.html
C语言-结构体内存对齐相关推荐
- C语言结构体内存对齐问题
参考博文:C语言结构体内存对齐问题 成员对齐: 以4字节对齐为例,如果自身类型小于4字节,则该成员的首地址是自身类型大小的整数倍:如果自身类型大于等于4字节,则该成员的首地址是4的整数倍.若内嵌结 ...
- 室友利用一把王者的时间就学会了【C语言结构体内存对齐】
文章目录 一.什么是结构体内存对齐? 二.结构体的对齐规则 结构体内存对齐规则的具体应用 三.为什么会存在内存对齐 总结 提示:以下是本篇文章正文内容,下面案例可供参考 一.什么是结构体内存对齐? 从 ...
- c语言 char转int_图文并茂,一文讲透C语言结构体内存对齐
↑点击上方蓝色字体,关注"嵌入式软件实战派"获得更多精品干货. (以下有约5000字内容,建议收藏再读,推荐下载源码自行测试以加深理解.) 面试官:你知道C语言的结构体对齐吗? 应 ...
- c语言结构体的对齐方式,C语言结构体内存的对齐知识详解
前言 在前面的章节中,我们谈到了C语言中整数以及浮点数的储存 今天,我们来谈一谈一些关于结构体内存的知识. 我们先来看一个例子: struct S1 { char c1; int i; char c2 ...
- printf打印结构体_工程师:这道题80%初学者都没做对!你确定搞懂结构体内存对齐了?...
这是工程师面试后的实际经历-- 这道经典.易错的关于C语言结构体内存对齐的题目,你真的会吗: 求32bit环境下以下结构体所占的字节数:typedef struct test_struct{ char ...
- 关于C语言中的结构体内存对齐与位段问题
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 结构体的内存对齐与位段 一.结构体的内存对齐 1.为什么会有内存对齐? 2.如何内存对齐 3. 如何在设计结构体的时候,既要 ...
- 语言结构体在内存的分布_结构体内存对齐,这篇文章给你彻底搞会!(干货收藏)...
脚本之家 你与百万开发者在一起 公众号:C语言编程 作者:薛定谔的coding猫 一.内存对齐的原因 1.平台原因(移植原因):一些资料上是这样说的,"不是所有的硬件平台都能访问任意地址上 ...
- 细讲C语言结构体(结构体内存对齐你懂了吗?)
结构体 结构体类型的声明 结构体自引用 结构体变量的定义和初始化 结构体内存对齐 结构体传参 结构体 结构体的声明 结构体是一些值集合的,里面可以包括char,int,double等等的各种类型构成的 ...
- 【C语言】自定义类型——计算结构体内存对齐
文章目录 前言 一.结构体 结构体类型的声明 结构的自引用 结构体变量的定义和初始化 *结构体内存对齐 修改默认对齐数 结构体传参 结构体实现位段(位段的填充&可移植性) 二.枚举 枚举类型的 ...
最新文章
- 英特尔发布全新CPU架构 称摩尔定律未死 中国研究院迎20周年
- 从SEO效果看谷歌百度360搜狗有道bing技术现状
- 安卓音频输出采样率_只有AirPods配有姓名吗?安卓的这些无线耳机也不错
- Adapter模式的好处
- 怎么删除结构体数组中的一组数据_数据结构-栈
- Python爬虫 搜索并下载图片
- ExtJs radiogroup form.loadRecord方法无法赋值正确解决办法
- Android学习之在Eclipse看源代码的技巧
- 【176天】黑马程序员27天视频学习笔记【Day11-上】
- 理解张量(tensor)和numpy
- java实例分析宠物商店_java实例分析:宠物商店.ppt
- Redis学习手册(List数据类型)(转)
- PHP字体向右移动,CSS3如何实现文字向右循环闪过效果以及可在移动端使用的实例代码分享...
- MYSQL向日期添加指定的时间间隔
- 2021-06-12 lock 锁 与synchronized 锁
- 简单解释bp神经网络_神经网络的最简单,最直观的解释
- 微信公众号管理欧宁泰php,米拓微信公众号管理应用-MetInfo帮助中心
- 干货来袭!腾讯T4大佬,十分钟教你用svg做出精美的动画
- Linux下的按键精灵xdotool
- Arp病毒(motou.exe,smss.com,smss.exe)解决方案
热门文章
- vscode python第三方库检测_VSCode中使用Pylint检查python代码
- 切换oracle用户impdp,Oracle 12c pdb使用expdp/impdp导入导出
- php的异常处理方式,php异常处理基本方法
- 操作系统上机作业-- 使用信号量解决生产者、计算者、消费者问题(多线程)
- 《MySQL——约束》
- json/ 发送形式_24/7的完整形式是什么?
- HttpServletRequest(request的一些API)
- yii mysql gii_Yii Gii使用
- 流畅的python目录_流畅的python python 序列
- golang plugin模块的使用