linux中结构体对齐【转】
转自:https://blog.csdn.net/suifengpiao_2011/article/details/47260085
linux中定义对齐字节
typedef struct sdk_handler {
char comm_ver[10];
char name[20];
char reserve[20];
}PACKED sdk_handler_t;
#define PACKED //__attribute__((aligned(1),packed)) // 一字节对齐
首先我们先看看下面的C语言的结构体:
- typedef struct MemAlign
- {
- int a;
- char b[3];
- int c;
- }MemAlign;
以上这个结构体占用内存多少空间呢?也许你会说,这个简单,计算每个类型的大小,将它们相加就行了,以32为平台为例,int类型占4字节,char占用1字节,所以:4 + 3 + 4 = 11,那么这个结构体一共占用11字节空间。好吧,那么我们就用实践来证明是否正确,我们用sizeof运算符来求出这个结构体占用内存空间大小,sizeof(MemAlign),出乎意料的是,结果居然为12?看来我们错了?当然不是,而是这个结构体被优化了,这个优化有个另外一个名字叫“对齐”,那么这个对齐到底做了什么样的优化呢,听我慢慢解释,再解释之前我们先看一个图,图如下:
相信学过汇编的朋友都很熟悉这张图,这张图就是CPU与内存如何进行数据交换的模型,其中,左边蓝色的方框是CPU,右边绿色的方框是内存,内存上面的0~3是内存地址。这里我们这张图是以32位CPU作为代表,我们都知道,32位CPU是以双字(DWORD)为单位进行数据传输的,也正因为这点,造成了另外一个问题,那么这个问题是什么呢?这个问题就是,既然32位CPU以双字进行数据传输,那么,如果我们的数据只有8位或16位数据的时候,是不是CPU就按照我们数据的位数来进行数据传输呢?其答案是否定的,如果这样会使得CPU硬件变的更复杂,所以32位CPU传输数据无论是8位或16位都是以双字进行数据传输。那么也罢,8位或16位一样可以传输,但是,事情并非像我们想象的那么简单,比如,一个int类型4字节的数据如果放在上图内存地址1开始的位置,那么这个数据占用的内存地址为1~4,那么这个数据就被分为了2个部分,一个部分在地址0~3中,另外一部分在地址4~7中,又由于32位CPU以双字进行传输,所以,CPU会分2次进行读取,一次先读取地址0~3中内容,再一次读取地址4~7中数据,最后CPU提取并组合出正确的int类型数据,舍弃掉无关数据。那么反过来,如果我们把这个int类型4字节的数据放在上图从地址0开始的位置会怎样呢?读到这里,也许你明白了,CPU只要进行一次读取就可以得到这个int类型数据了。没错,就是这样,这次CPU只用了一个周期就得到了数据,由此可见,对内存数据的摆放是多么重要啊,摆放正确位置可以减少CPU的使用资源。
那么,内存对齐有哪些原则呢?我总结了一下大致分为三条:
第一条:第一个成员的首地址为0
第二条:每个成员的首地址是自身大小的整数倍
第二条补充:以4字节对齐为例,如果自身大小大于4字节,都以4字节整数倍为基准对齐。
若内嵌结构体,则内嵌结构体的首地址也要符合此条,只不过自身大小用内嵌结构体的最大成员来表示。补齐时,内嵌结构体和外部结构体都要满足补齐,内嵌机构体中的补齐以其内部最大成员表示;外部结构体以自身基础成员和内嵌结构体的最大成员之间的较大者表示。
第三条:最后以结构总体对齐。
第三条补充:以4字节对齐为例,取结构体中最大成员类型倍数,如果超过4字节,都以4字节整数倍为基准对齐。(其中这一条还有个名字叫:“补齐”,补齐的目的就是多个结构变量挨着摆放的时候也满足对齐的要求。)
上述的三原则听起来还是比较抽象,那么接下来我们通过一个例子来加深对内存对齐概念的理解,下面是一个结构体,我们动手算出下面结构体一共占用多少内存?假设我们以32位平台并且以4字节对齐方式:
- #pragma pack(4)
- typedef struct MemAlign
- {
- char a[18];
- double b;
- char c;
- int d;
- short e;
- }MemAlign;
下图为对齐后结构如下:
我们就以这个图来讲解是如何对齐的:
第一个成员(char a[18]):首先,假设我们把它放到内存开始地址为0的位置,由于第一个成员占18个字节,所以第一个成员占用内存地址范围为0~18。
第二个成员(double b):由于double类型占8字节,又因为8字节大于4字节,所以就以4字节对齐为基准。由于第一个成员结束地址为18,那么地址18并不是4的整数倍,我们需要再加2个字节,也就是从地址20开始摆放第二个成员。
第三个成员(char c):由于char类型占1字节,任意地址是1字节的整数倍,所以我们就直接将其摆放到紧接第二个成员之后即可。
第四个成员(int d):由于int类型占4字节,但是地址29并不是4的整数倍,所以我们需要再加3个字节,也就是从地址32开始摆放这个成员。
第五个成员(short e):由于short类型占2字节,地址36正好是2的整数倍,这样我们就可以直接摆放,无需填充字节,紧跟其后即可。
这样我们内存对齐就完成了。但是离成功还差那么一步,那是什么呢?对,是对整个结构体补齐,接下来我们就补齐整个结构体。那么,先让我们回顾一下补齐的原则:“以4字节对齐为例,取结构体中最大成员类型倍数,如果超过4字节,都以4字节整数倍为基准对齐。”在这个结构体中最大类型为double类型(占8字节),又由于8字节大于4字 节,所以我们还是以4字节补齐为基准,整个结构体结束地址为38,而地址38并不是4的整数倍,所以我们还需要加额外2个字节来填充结构体,如下图红色的就是补齐出来的空间:
到此为止,我们内存对齐与补齐就完毕了!接下来我们用实验来证明真理,程序如下:
- #include <stdio.h>
- #include <memory.h>
- // 由于VS2010默认是8字节对齐,我们
- // 通过预编译来通知编译器我们以4字节对齐
- #pragma pack(4)
- // 用于测试的结构体
- typedef struct MemAlign
- {
- char a[18]; // 18 bytes
- double b; // 08 bytes
- char c; // 01 bytes
- int d; // 04 bytes
- short e; // 02 bytes
- }MemAlign;
- int main()
- {
- // 定义一个结构体变量
- MemAlign m;
- // 定义个以指向结构体指针
- MemAlign *p = &m;
- // 依次对各个成员进行填充,这样我们可以
- // 动态观察内存变化情况
- memset( &m.a, 0x11, sizeof(m.a) );
- memset( &m.b, 0x22, sizeof(m.b) );
- memset( &m.c, 0x33, sizeof(m.c) );
- memset( &m.d, 0x44, sizeof(m.d) );
- memset( &m.e, 0x55, sizeof(m.e) );
- // 由于有补齐原因,所以我们需要对整个
- // 结构体进行填充,补齐对齐剩下的字节
- // 以便我们可以观察到变化
- memset( &m, 0x66, sizeof(m) );
- // 输出结构体大小
- printf( "sizeof(MemAlign) = %d", sizeof(m) );
- }
程序运行过程中,查看内存如下:
其中,各种颜色带下划线的代表各个成员变量,蓝色方框的代表为内存对齐时候填补的多余字节,由于这里看不到补齐效果,我们接下来看下图,下图篮框包围的字节就是与上图的交集以外的部分就是补齐所填充的字节。
在最后,我在谈一谈关于补齐的作用,补齐其实就是为了让这个结构体定义的数组变量时候,数组内部,也同样满足内存对齐的要求,为了更好的理解这点,我做了一个跟本例子相对照的图:
补充:
1)对于结构体中有数组的,将数组作为基本数据类型来处理对齐;
2)对于结构体内嵌套结构体成员的,结构体成员的对齐和补齐原则:已最大成员作为内结构体整体的大小去匹配;
---------------------
本文来自 漫步者2011 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/suifengpiao_2011/article/details/47260085?utm_source=copy
linux中结构体对齐【转】的更多相关文章
- C语言中结构体对齐问题
C语言中结构体对齐问题 收藏 关于C语言中的结构体对齐问题 1,比如: struct{short a1;short a2;short a3;}A;struct{long a1;short a2;}B; ...
- C 中结构体对齐
参考 百度百科内存对齐 对齐作用 可以使得以最少的次数将操作数加载到寄存器中,如果数据没有对齐,则当CPU以最小读取数据大小从内存读入数据时可能只取到了一部分数据,而对齐情况下可以一次读入. 对齐修改 ...
- 函数定义从零开始学C++之从C到C++(一):const与#define、结构体对齐、函数重载name mangling、new/delete 等
今天一直在学习函数定义之类的问题,下午正好有机会和大家共享一下. 一.bool 类型 逻辑型也称布尔型,其取值为true(逻辑真)和false(逻辑假),存储字节数在不同编译系统中可能有所不同,VC+ ...
- C语言基础--结构体对齐,位域,联合体
结构体对齐 1--结构体对齐的原因与意义 许多计算机系统对基本数据类型的可允许地址做出了一些限制,要求某种类型的对象的地址必须是某个值K(通常是2,4,8)的倍数,而这个k则被称为该数据类型的对齐模数 ...
- 结构体对齐及#pragma详细解释
在linux下c语言结构体对齐: 1.自然对齐 struct 是一种复合数据类型,其构成元素既可以是基本数据类型(如int.long.float 等)的变量,也可以是一些复合数据类型(如array.s ...
- const与#define、结构体对齐、函数重载name mangling、new/delete 等
一.bool 类型 逻辑型也称布尔型,其取值为true(逻辑真)和false(逻辑假),存储字节数在不同编译系统中可能有所不同,VC++中为1个字节. 声明方式:bool result; result ...
- C语言结构体对齐
1.结构体变量中的元素如何访问? (1)数组中元素的访问方式:表面上有2种方式(数组下标方式和指针方式):实质上都是指针方式访问.(2)结构体变量中的元素访问方式:只有一种,用.或者->的方式来 ...
- C语言中结构体赋值问题的讨论
今天帮师姐调一个程序的BUG,师姐的程序中有个结构体直接赋值的语句,在我印象中结构体好像是不能直接赋值的,正如数组不能直接赋值那样,我怀疑这个地方有问题,但最后证明并不是这个问题.那么就总结一下C语言 ...
- 解析C语言结构体对齐(内存对齐问题)
C语言结构体对齐也是老生常谈的话题了.基本上是面试题的必考题.内容虽然很基础,但一不小心就会弄错.写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的 ...
随机推荐
- 在Java Web程序中使用监听器可以通过以下两种方法
之前学习了很多涉及servlet的内容,本小结我们说一下监听器,说起监听器,编过桌面程序和手机App的都不陌生,常见的套路都是拖一个控件,然后给它绑定一个监听器,即可以对该对象的事件进行监听以便发生响 ...
- 自学Zabbix4.1 zabbix监控web服务器访问性能
自学Zabbix4.1 zabbix监控web服务器访问性能 使用Zabbix实现对web性能的监控,通过它可以了解web站点的可用性以及性能.最终将各项指标绘制到图形中,这样我们可以了解到一个站点的 ...
- [luogu4868]Preprefix sum
https://www.luogu.org/problemnew/show/P4868 题目大意 单点修改,查询前缀前缀和. 分析 遇到了单点修改,前缀和,很明显是要树状数组维护解决问题. 请看以下我 ...
- window.open打开页面居中显示
<script type="text/javascript"> function openwindow(url,name,iWidth,iHeight) { var u ...
- 收藏:SQL重复记录查询 .
来自:http://blog.csdn.net/chinmo/article/details/2184020 1.查找表中多余的重复记录,重复记录是根据单个字段(peopleId)来判断select ...
- 关于移动端及flex
我们知道写pc页面的时候,ui设计图是多少px,我们写网页的时候,就会写多少px,这个其实就是由pc端屏幕的物理像素,和我们设计图的css逻辑像素决定的,由于屏幕的物理像素和css逻辑像素比,刚好是1 ...
- 一起使用mock数据动态创建表格
在ant-design中,我们创建一个基础table会怎么实现呢? 如下代码可视,我们会自己创建一些数据,在表格中渲染出来,如下 <Card title="基础表格"> ...
- 2018.10.2浪在ACM 集训队第三次测试赛
2018.10.26 浪在ACM 集训队第三次测试赛 今天是暴力场吗???????可怕 题目一览表 来源 考察知识点 完成时间 A 1275 珠心算测试 NOIP 普及组 2014 暴力??? 201 ...
- Window配置环境变量
拿Java为例,讲一下怎么配置环境变量使得javac可以在全局被调用: 1.添加环境变量 例如我们的Java下载在了:D:\Java\v1.8 我们就到环境变量那添加一个例如叫“Java_path”的 ...
- python中的位运算符
按位运算符是把数字看作二进制来进行计算的.Python中的按位运算法则如下,下表中变量 a 为 60,b 为 13,二进制格式如下: a = 0011 1100 b = 0000 1101 ----- ...