转自: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语言的结构体:

  1. typedef struct MemAlign
  2. {
  3. int a;
  4. char b[3];
  5. int c;
  6. }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字节对齐方式:

  1. #pragma pack(4)
  2. typedef struct MemAlign
  3. {
  4. char a[18];
  5. double b;
  6. char c;
  7. int d;
  8. short e;
  9. }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个字节来填充结构体,如下图红色的就是补齐出来的空间:

到此为止,我们内存对齐与补齐就完毕了!接下来我们用实验来证明真理,程序如下:

  1. #include <stdio.h>
  2. #include <memory.h>
  3. // 由于VS2010默认是8字节对齐,我们
  4. // 通过预编译来通知编译器我们以4字节对齐
  5. #pragma pack(4)
  6. // 用于测试的结构体
  7. typedef struct MemAlign
  8. {
  9. char a[18]; // 18 bytes
  10. double b;   // 08 bytes
  11. char c;     // 01 bytes
  12. int d;      // 04 bytes
  13. short e;    // 02 bytes
  14. }MemAlign;
  15. int main()
  16. {
  17. // 定义一个结构体变量
  18. MemAlign m;
  19. // 定义个以指向结构体指针
  20. MemAlign *p = &m;
  21. // 依次对各个成员进行填充,这样我们可以
  22. // 动态观察内存变化情况
  23. memset( &m.a, 0x11, sizeof(m.a) );
  24. memset( &m.b, 0x22, sizeof(m.b) );
  25. memset( &m.c, 0x33, sizeof(m.c) );
  26. memset( &m.d, 0x44, sizeof(m.d) );
  27. memset( &m.e, 0x55, sizeof(m.e) );
  28. // 由于有补齐原因,所以我们需要对整个
  29. // 结构体进行填充,补齐对齐剩下的字节
  30. // 以便我们可以观察到变化
  31. memset( &m, 0x66, sizeof(m) );
  32. // 输出结构体大小
  33. printf( "sizeof(MemAlign) = %d", sizeof(m) );
  34. }

程序运行过程中,查看内存如下:

其中,各种颜色带下划线的代表各个成员变量,蓝色方框的代表为内存对齐时候填补的多余字节,由于这里看不到补齐效果,我们接下来看下图,下图篮框包围的字节就是与上图的交集以外的部分就是补齐所填充的字节。

在最后,我在谈一谈关于补齐的作用,补齐其实就是为了让这个结构体定义的数组变量时候,数组内部,也同样满足内存对齐的要求,为了更好的理解这点,我做了一个跟本例子相对照的图:

补充:

1)对于结构体中有数组的,将数组作为基本数据类型来处理对齐;

2)对于结构体内嵌套结构体成员的,结构体成员的对齐和补齐原则:已最大成员作为内结构体整体的大小去匹配;

---------------------

本文来自 漫步者2011 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/suifengpiao_2011/article/details/47260085?utm_source=copy

linux中结构体对齐【转】的更多相关文章

  1. C语言中结构体对齐问题

    C语言中结构体对齐问题 收藏 关于C语言中的结构体对齐问题 1,比如: struct{short a1;short a2;short a3;}A;struct{long a1;short a2;}B; ...

  2. C 中结构体对齐

    参考 百度百科内存对齐 对齐作用 可以使得以最少的次数将操作数加载到寄存器中,如果数据没有对齐,则当CPU以最小读取数据大小从内存读入数据时可能只取到了一部分数据,而对齐情况下可以一次读入. 对齐修改 ...

  3. 函数定义从零开始学C++之从C到C++(一):const与#define、结构体对齐、函数重载name mangling、new/delete 等

    今天一直在学习函数定义之类的问题,下午正好有机会和大家共享一下. 一.bool 类型 逻辑型也称布尔型,其取值为true(逻辑真)和false(逻辑假),存储字节数在不同编译系统中可能有所不同,VC+ ...

  4. C语言基础--结构体对齐,位域,联合体

    结构体对齐 1--结构体对齐的原因与意义 许多计算机系统对基本数据类型的可允许地址做出了一些限制,要求某种类型的对象的地址必须是某个值K(通常是2,4,8)的倍数,而这个k则被称为该数据类型的对齐模数 ...

  5. 结构体对齐及#pragma详细解释

    在linux下c语言结构体对齐: 1.自然对齐 struct 是一种复合数据类型,其构成元素既可以是基本数据类型(如int.long.float 等)的变量,也可以是一些复合数据类型(如array.s ...

  6. const与#define、结构体对齐、函数重载name mangling、new/delete 等

    一.bool 类型 逻辑型也称布尔型,其取值为true(逻辑真)和false(逻辑假),存储字节数在不同编译系统中可能有所不同,VC++中为1个字节. 声明方式:bool result; result ...

  7. C语言结构体对齐

    1.结构体变量中的元素如何访问? (1)数组中元素的访问方式:表面上有2种方式(数组下标方式和指针方式):实质上都是指针方式访问.(2)结构体变量中的元素访问方式:只有一种,用.或者->的方式来 ...

  8. C语言中结构体赋值问题的讨论

    今天帮师姐调一个程序的BUG,师姐的程序中有个结构体直接赋值的语句,在我印象中结构体好像是不能直接赋值的,正如数组不能直接赋值那样,我怀疑这个地方有问题,但最后证明并不是这个问题.那么就总结一下C语言 ...

  9. 解析C语言结构体对齐(内存对齐问题)

    C语言结构体对齐也是老生常谈的话题了.基本上是面试题的必考题.内容虽然很基础,但一不小心就会弄错.写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结果往往都比你声明的 ...

随机推荐

  1. 自学Zabbix3.12.4-动作Action-Operation配置

    点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 3.12.4 自学Zabbix3.12.4-动作Action-Operation配置 1. 概述 ...

  2. json序列化 & 反序列化

    json序列化: json的dumps方法可以将json格式数据序列为python的相关数据类型,比如str,常用于打印,另外,在序列化时,中文汉字被转换为unicode编码,在dumps函数中添加参 ...

  3. poj1236/luogu2746 Network of Schools (tarjan)

    tarjan缩点后,第一问答案显然是入度为零的点得个数第二问:考虑到 没有入度或出度为0的点 的图强连通, 所以答案就是max{入度为零的个数,出度为零的个数} (把出度为零的连到入度为零的点,然后剩 ...

  4. 【洛谷P1072】Hankson 的趣味题

    题目大意:给定四个数字 a,b,c,d,求满足 \(gcd(a,x)=b,lcm(c,x)=d\) 的 x 的个数. 题解: 解法1:根据 lcm 的性质,x 一定为 d 的约数.因此,直接枚举 d ...

  5. 获取EasyUI的treegrid的checkbox所有已勾选的数据

    EasyUI为TreeGrid的已勾选节点,未勾选节点,只勾选部分子节点的父节点分别添加了三个不同的样式,如下:样式一:tree-checkbox2 有子节点被选中样式二:tree-checkbox1 ...

  6. echarts 取消图例上的点击事件和图表上鼠标滑过点击事件

    备注:标黄的代码是起作用的代码,其他的不需要借鉴!!! //取消 鼠标滑过的提示框 tooltip : {         trigger: 'item',      show:false,      ...

  7. ansible 循环与条件判断when

    普通循环 with_itemes 变量组 { item } 循环变量 示例 - name: 覆盖配置文件 copy: src=/root/{{ item }} dest=/root/test/{{ i ...

  8. 学习windows编程 day6 之模拟记事本

    //短的函数最好定义为宏 #define BUFFER(x,y) *(y*cxBuffer+x+pBuffer)//取出一个字符 //字符消息 //WM_CHAR,WM_DEADCHAR,WM_SYS ...

  9. JAVA记录-Spring两大特性

    1.IOC控制反转 Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象 ...

  10. 【推荐】Nginx基础知识之————多模块(非覆盖安装、RTMP在线人数实例安装测试)

    说明:已经安装好的nginx,需要添加一个未被编译安装的模块,需要怎么弄呢? 具体:这里以安装第三方nginx-rtmp-module和nginx-accesskey-2.0.3模块为例,nginx的 ...