c语言 __align(); #pragma pack(8) 数据对齐_c语言的数据对其align-程序员宅基地

技术标签: STM32  

1. 什么是字节对齐?

 

在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构、联合等)的数据单元。在结构中,编译器为结构的每个成员按其自然边界(alignment)分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。

 

为了使CPU能够对变量进行快速的访问,变量的起始地址应该具有某些特性,即所谓的”对齐”. 比如4字节的int型,其起始地址应该位于4字节的边界上,即起始地址能够被4整除.

 

2. 字节对齐有什么作用?

 

字节对齐的作用不仅是便于cpu快速访问,同时合理的利用字节对齐可以有效地节省存储空间。

 

对于32位机来说,4字节对齐能够使cpu访问速度提高,比如说一个long类型的变量,如果跨越了4字节边界存储,那么cpu要读取两次,这样效率就低了。但是在32位机中使用1字节或者2字节对齐,反而会使变量访问速度降低。所以这要考虑处理器类型,另外还得考虑编译器的类型。在vc中默认是4字节对齐的,GNU gcc 也是默认4字节对齐。

 

3. 更改C编译器的缺省字节对齐方式

 

在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件:

·使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。

·使用伪指令#pragma pack (),取消自定义字节对齐方式。

 

另外,还有如下的一种方式:

· __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。

· __attribute__ ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。

 

4. 举例说明

 

例1

 

struct test

{

char x1;

short x2;

float x3;

char x4;

};

 

由于编译器默认情况下会对这个struct作自然边界(有人说“自然对界”我觉得边界更顺口)对齐,结构的第一个成员x1,其偏移地址为0,占据了第1个字节。第二个成员x2为short类型,其起始地址必须2字节对界,因此,编译器在x2和x1之间填充了一个空字节。结构的第三个成员x3和第四个成员x4恰好落在其自然边界地址上,在它们前面不需要额外的填充字节。在test结构中,成员x3要求4字节对界,是该结构所有成员中要求的最大边界单元,因而test结构的自然对界条件为4字节,编译器在成员x4后面填充了3个空字节。整个结构所占据空间为12字节。

 

例2

 

#pragma pack(1) //让编译器对这个结构作1字节对齐

struct test

{

char x1;

short x2;

float x3;

char x4;

};

#pragma pack() //取消1字节对齐,恢复为默认4字节对齐

 

这时候sizeof(struct test)的值为8。

 

例3

 

#define GNUC_PACKED __attribute__((packed))

struct PACKED test

{

char x1;

short x2;

float x3;

char x4;

}GNUC_PACKED;

 

这时候sizeof(struct test)的值仍为8。


对齐方式

程序编译器对结构的存储的特殊处理确实提高CPU存储 变量的速度,但是有时候也带来了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式。
编译器中提供了 #pragma pack(n)来设定 变量以n 字节对齐方式。n 字节对齐就是说 变量存放的起始地址的 偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有 成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。
下面举例说明其用法。
#pragma pack(push) //保存对齐状态
#pragma pack(4)//设定为4 字节对齐
struct test
{
char m1;
double m4;
int m3;
};
#pragma pack(pop)//恢复对齐状态
以上 结构体的大小为16,下面分析其存储情况,首先为m1分配空间,其 偏移量为0,满足我们自己设定的对齐方式(4 字节对齐),m1大小为1个字节。接着开始为m4分配空间,这时其 偏移量为4,需要补足3个字节,这样使偏移量满足为n=4的倍数(因为sizeof(double)大于4),m4占用8个字节。接着为m3分配空间,这时其 偏移量为12,满足为4的倍数,m3占用4个字节。这时已经为所有 成员变量分配了空间,共分配了16个字节,满足为n的倍数。如果把上面的 #pragma pack(4)改为 #pragma pack(8),那么我们可以得到结构的大小为24。

对齐用法详解编辑

什么是对齐,以及为什么要对齐: 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的 变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的 内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。 对齐的作用和原因:各个硬件平台对 存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低 字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。这也是空间和时间的博弈。
对齐的实现:通常,我们写程序的时候,不需要考虑对齐问题。 编译器会替我们选择适合目标平台的对齐策略。当然,我们也可以通知给 编译器传递 预编译指令而改变对指定数据的对齐方法。 但是,正因为我们一般不需要关心这个问题,所以因为 编辑器对数据存放做了对齐,而我们不了解的话,常常会对一些问题感到迷惑。最常见的就是struct 数据结构的sizeof结果,出乎意料。为此,我们需要对对齐算法所了解。
作用:指定 结构体、联合以及类成员的packing alignment;
语法: #pragma pack( [show] | [push | pop] [, identifier], n )
说明:1,pack提供数据声明级别的控制,对定义不起作用;2,调用pack时不指定参数,n将被设成默认值;3,一旦改变 数据类型的alignment,直接效果就是占用memory的减少,但是performance会下降;
语法具体分析:1,show:可选参数;显示当前packing aligment的 字节数,以warning message的形式被显示;2,push:可选参数;将当前指定的packing alignment数值进行压栈操作,这里的栈是the internal compiler stack,同时设置当前的packing alignment为n;如果n没有指定,则将当前的packing alignment数值压栈;3,pop:可选参数;从internal compiler stack中删除最顶端的record;如果没有指定n,则当前栈顶record即为新的packing alignment数值;如果指定了n,则n将成为新的packing aligment数值;如果指定了identifier,则internal compiler stack中的record都将被pop直到identifier被找到,然后pop出identitier,同时设置packing alignment数值为当前栈顶的record;如果指定的identifier并不存在于internal compiler stack,则pop操作被忽略;4,identifier:可选参数;当同push一起使用时,赋予当前被压入栈中的record一个名称;当同pop一起使用时,从internal compiler stack中pop出所有的record直到identifier被pop出,如果identifier没有被找到,则忽略pop操作;5,n:可选参数;指定packing的数值,以字节为单位;缺省数值是8,合法的数值分别是1、2、4、8、16。
重要规则:1,复杂类型中各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个类型的地址相同;2,每个成员分别对齐,即每个成员按自己的方式对齐,并最小化长度;规则就是每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数中较小的一个对齐;3,结构、联合或者类的 数据成员,第一个放在偏移为0的地方;以后每个数据成员的对齐,按照 #pragma pack指定的数值和这个数据成员自身长度两个中比较小的那个进行;也就是说,当#pragma pack指定的值等于或者超过所有数据成员长度的时候,这个指定值的大小将不产生任何效果;4,复杂类型(如结构)整体的对齐<注意是“整体”>是按照结构体中长度最大的数据成员和#pragma pack指定值之间较小的那个值进行;这样在成员是复杂类型时,可以最小化长度;5,结构整体长度的计算必须取所用过的所有对齐参数的整数倍,不够补空字节;也就是取所用过的所有对齐参数中最大的那个值的整数倍,因为对齐参数都是2的n次方;这样在处理 数组时可以保证每一项都 边界对齐
对齐的算法: 由于各个平台和 编译器的不同,现以本人使用的gcc version 3.2.2 编译器(32位x86平台)为例子,来讨论编译器对struct 数据结构中的各成员如何进行对齐的。
在相同的对齐方式下, 结构体内部数据定义的顺序不同, 结构体整体占据内存空间也不同,如下: 设结构体如下定义: struct A { int a; char b; short c; }; 结构体A中包含了4 字节长度的int一个,1 字节长度的char一个和2字节长度的short型数据一个。所以A用到的空间应该是7 字节。但是因为编译器要对 数据成员在空间上进行对齐。所以使用sizeof(strcut A)值为8。 现在把该结构体调整 成员变量的顺序。 struct B { char b; int a; short c; }; 这时候同样是总共7个字节的 变量,但是sizeof(struct B)的值却是12。 下面我们使用 预编译指令 #pragma pack (value)来告诉 编译器,使用我们指定的对齐值来取代缺省的。 #pragma pack (2) /*指定按2 字节对齐,等价于#pragma pack(push,2)*/ struct C { char b; int a; short c; }; #pragma pack () /*取消指定对齐,恢复缺省对齐,等价于#pragma pack(pop)*/ sizeof(struct C)值是8。 修改对齐值为1: #pragma pack (1) /*指定按1 字节对齐*/ struct D { char b; int a; short c; }; #pragma pack () /*取消指定对齐,恢复缺省对齐*/ sizeof(struct D)值为7。 对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位 字节
这里面有四个概念值: 1. 数据类型自身的对齐值:就是上面交代的基本数据类型的自身对齐值。 2.指定对齐值: #pragma pack (value)时的指定对齐值value。 3. 结构体或者类的自身对齐值:其 数据成员中自身对齐值最大的那个值。 4. 数据成员结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。 有了这些值,我们就可以很方便的来讨论具体 数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".而数据结构中的数据 变量都是按定义的先后顺序来排放的。第一个数据 变量的起始地址就是 数据结构的起始地址。 结构体成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍,结合下面例子理解)。这样就不能理解上面的几个例子的值了。 例子分析: 分析例子B; struct B { char b; int a; short c; }; 假设B从 地址空间0x0000开始排放。该例子中没有定义指定对齐值,在笔者环境下,该值默认为4。
第一个 成员变量b的自身对齐值是1,比指定或者默认指定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.
第二个 成员变量a,其自身对齐值为4,所以有效对齐值也为4,所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,符合0x0004%4=0, 且紧靠第一个变量。
第三个 变量c,自身对齐值为2,所以有效对齐值也是2,可以存放在0x0008到0x0009这两个字节空间中,符合0x0008%2=0。所以从0x0000到0x0009存放的都是B内容。
再看 数据结构B的自身对齐值为其 变量中最大对齐值(这里是a)和指定对齐值(这里是4)中较小的那个,所以就是4,所以 结构体的有效对齐值也是4。根据 结构体圆整的要求,0x0009到0x0000=10 字节,(10+2)%4=0。所以0x0000A到0x000B也为 结构体B所占用。故B从0x0000到0x000B共有12个 字节,sizeof(struct B)=12; 同理,分析上面例子C: #pragma pack (2) /*指定按2 字节对齐*/ struct C { char b; int a; short c; }; #pragma pack () /*取消指定对齐,恢复缺省对齐*/ 第一个 变量b的自身对齐值为1,指定对齐值为2,所以,其有效对齐值为1,假设C从0x0000开始,那么b存放在0x0000,符合0x0000%1=0;
第二个 变量,自身对齐值为4,指定对齐值为2,所以有效对齐值为2,所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连续字节中,符合0x0002%2=0。
第三个 变量c的自身对齐值为2,所以有效对齐值为2,顺序存放在0x0006、0x0007中,符合0x0006%2=0。所以从0x0000到0x00007共八 字节存放的是C的 变量
又C的自身对齐值为4,所以C的有效对齐值为2。又8%2=0,C只占用0x0000到0x0007的八个字节。所以sizeof(struct C)=8.


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/mish84/article/details/38756331

智能推荐

fatal error LNK1120: 2 unresolved externals解决办法-程序员宅基地

文章浏览阅读4.7k次。典型的错误提示有: libcmtd.lib(crt0.obj) : error LNK2001: unresolved external symbol _main LIBCD.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16 msvcrtd.lib(crtexew.obj) _fatal error lnk1120: 2 unresolved externals

3D游戏建模:吹爆“寡姐”的神仙颜值,你也能轻松上手 | 欣赏-程序员宅基地

文章浏览阅读237次。就问问各位《复仇者联盟》《惊奇队长》《黑寡妇》《超体》这些耳熟能详的电影绝对是部部必看!“寡姐”斯嘉丽·约翰逊是咱们最熟悉的漫威女演员之一“寡姐”的神仙颜值绝对征服了不少的人包括奶茶在内也是爱的不要不要的今天就给大家分享一个斯嘉丽·约翰逊的模型▼▼▼毛发是 XGEN2.0,头发造型虽然只做了一层但看起来越来越像神奇女侠有木有皮肤贴图是Mari4.2v1,Arnold渲染器细节之处处理的非常到位尤其是眼神和皮肤的处理非常细致其实头发在处理上

PO、BO、VO、POJO、DTO(TO)、DAO的区别_bo和vo和to的区别-程序员宅基地

文章浏览阅读768次。PO(persistent object 持久对象)有时也被称为Data对象,对应数据库中的entity,可以简单认为一个PO对应数据库中的一条记录。在hibernate持久化框架中与insert/delet操作密切相关。PO中不应该包含任何对数据库的操作。BO(business object 业务对象)业务对象主要作用是把业务逻辑封装为一个对象。这个对象可以包括一个或多个其它的对象。比如一个简历,有教育经历、工作经历、社会关系等等。我们可以把教育经历对应一个PO,工作经历对应一_bo和vo和to的区别

2024年短剧项目怎么做?教你搭建自己的短剧及cps分销平台app小程序v_快手短剧分销平台怎么搭建的呢-程序员宅基地

文章浏览阅读924次,点赞29次,收藏8次。功能:1.对接流量主变现2.影视作品观看3.支持创作者入驻4.PC独立后台管理5.壁纸,表情包下载6.内容管理分类,专题分类7.可单次付费或月会员8.会员开通与支付功能9.微信端使用10.后台数据清晰明11.可定制搭建。介绍:短剧小程序,是自己在后台上传片源,用户充值和看剧一些操作都是在你自己的小程序上进行。以上就是三款短剧系统的介绍,如果想在2024年入局短剧行业 ,可联系:丸子图社 找客服联系搭建即可。前言:短剧系统分为三种:短剧系统、短剧cps分销平台、海外短剧系统。功能:1、支持短剧,小说挂载。_快手短剧分销平台怎么搭建的呢

【毕业设计】基于JAVA的springboot家政服务管理平台(源代码+论文)_java springboot 毕设-程序员宅基地

文章浏览阅读584次。家政服务管理平台采用了B/S结构,JAVA作为开发语言,数据库采用了B/S结构,Mysql数据库进行开发。该系统包括前台操作和后台管理两个部分,一方面,为用户提供首页、服务信息、公告信息、留言反馈、个人中心、后台管理等功能;另一方面,为管理员提供首页、个人中心、用户管理、服务人员管理、服务信息管理、服务类型管理、服务预约管理、服务取消管理、服务分配管理、服务进度管理、评价信息管理、留言反馈、系统管理等功能。_java springboot 毕设

HDU-5877 Weak Pair(dfs序+归并树/dfs序+离线归并+树状数组/dfs栈+树状数组/dfs作差+树状数组)-程序员宅基地

文章浏览阅读215次。题意在一棵有 nnn 个节点的树上,求出有多少个点对 (u,v)(u,v)(u,v) ,满足 uuu 是 vvv 的祖先且 a[u]∗a[v]≤ka[u]∗a[v]≤ka[u]*a[v] ≤k 。 1≤n≤1051≤n≤105 1 \leq n \leq 10^5 0≤ai≤1090≤ai≤109 0 \leq a_i \leq 10^9 0≤k≤10180≤k≤1018 0 \..._dfs作差

随便推点

什么是水平分表,垂直分表_水平分表和垂直分表-程序员宅基地

文章浏览阅读412次。水平分表把一个大表,拆分成多个小表,大表和小表的字段完全一致,只是小表的数据加一起才是大表的数据(如大表的资料有三个月的人员刷卡资料。小表分别会存一个月的刷卡资料)垂直分表把一个大表,拆分成多个小表。小表的字段总和(除id)等同于大表字段例如:大表字段为id,empno,chname,sex,age,dept则小表1的字段为id,empno,chname,dept小表2的字段为id,empno,sex,age..._水平分表和垂直分表

jmeter函数助手对话框 随机数、字符和时间戳,判断变量是否存在_jmeter获取随机时间撮-程序员宅基地

文章浏览阅读1.9w次,点赞6次,收藏13次。函数助手对话框打开位置2处默认界面点击帮助左边的倒钩三角形,选择所需要的函数Random函数创建${__Random(9000,9999,data)}Random函数使用和csv文件正则提取一样结果Random String函数使用Random string length:随机字符串长度Chars to use for random string generation:用于随机生成字符串的字符Random String函数使用${__RandomString(7,abc1_jmeter获取随机时间撮

如何通过CRM实现客户关系管理?-程序员宅基地

文章浏览阅读879次,点赞19次,收藏20次。数字化转型已经成为了企业发展的重要方向,然而数字化转型不仅仅是把线下搬到线上,更重要的是,真正的数字化转型应该以客户的连接和客户的数字化作为突破口。从“以产品为中心”到“以客户为中心”的转变,让客户成为了企业最重要的战略资源之一。CRM通过整合企业营销、销售、服务等业务为一体的企业商业经营策略,可以实现:有效组织企业资源培养以客户为中心的经营行为实施以客户为中心的业务流程……

双目视觉算法研究(二)相机模型和直接线性法(DLT)_dlt模型-程序员宅基地

文章浏览阅读1.5w次,点赞18次,收藏99次。一、相机数学模型 相机模型为以后一切标定算法的关键,只有这边有相当透彻的理解,对以后的标定算法才能有更好的理解。本人研究了好长时间,几乎每天都重复看几遍,最终才会明白其推导过程。 我觉得首先我们要理解相机模型中的四个平面坐标系的关系:像素平面坐标系(u,v)、像平面坐标系(图像物理坐标第(x,y)、相机坐标系(Xc,Yc,Zc)和世界坐标系(Xw,Yw,Zw),在每一篇介绍相_dlt模型

sed 第n行后加入_'sed':如何在字符串匹配2行后添加新行-程序员宅基地

文章浏览阅读757次。我想在字符串匹配2行之后添加一个新行 .这是我的档案:allow-hotplug eth0auto eth0iface eth0 inet staticaddress 172.16.2.245netmask 255.255.254.0gateway 192.168.1.1allow-hotplug eth1#auto eth1iface eth1 inet staticaddress 192.16..._sed在匹配行后2行插入

Docker方式部署redis-cluster-程序员宅基地

文章浏览阅读950次,点赞19次,收藏13次。-userports:redis3:ports:redis4:ports:redis5:ports:redis6:ports:创建文件后,直接启动服务窗口模式后台进程查看启动的进程状态为Up,说明服务均已启动,镜像无问题。