-
2018-10-23 16:16:30
原始链接源自 https://www.cnblogs.com/clover-toeic/p/3853132.html , 从上面博客中学习总结得到下面的文章。
不同硬件平台,对存储空间的处理不一样,比如不能放奇数地址,不能任意存放等,为了适应不同的架构,在C语言层面上,就可以执行对齐从而独立于硬件平台。 此外,是由于对内存的存取效率问题,如果存放的地址不对齐,取一个4字节的数据,可能会需要两个时钟信号才能取完。为了CPU能够对数据进行快速的访问,也要求数据的起始地址具有对齐特性。 比如4字节数据的起始地址应该在4字节的边界上,也就是数据存放的起始地址应该被4整除。这就是为什么要字节对齐, 和什么是字节对齐。
对齐的方式,又区分 结构体对齐、 栈内存对齐、位域对齐, 位域本质上是结构体。
对于Intel X86平台,每次分配内存应该是从4的整数倍地址开始分配,无论是对结构体变量还是简单类型的变量。
一、结构体对齐
编译器为结构体的每个成员按照其自然边界(alignment)分配空间。各成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。对齐规则:
1) 数据类型自身的对齐值:char型数据自身对齐值为1字节,short型数据为2字节,int/float型为4字节,double型为8字节。
2) 结构体的自身对齐值:其成员中自身对齐值最大的那个值。
3) 指定对齐值:#pragma pack (value)时的指定对齐值value。默认是4。
4) 数据成员、结构体的有效对齐值:自身对齐值和指定对齐值中较小者,即有效对齐值=min{自身对齐值,当前指定的pack值}。
使用pragma指定对齐值,其实是指定了数据结构的最大对齐值, 如果本身的对齐值,并不超过设定的值,还是会按照自身的对齐值来。
有效对齐值N是最终用来决定数据存放地址方式的值。有效对齐N表示“对齐在N上”,即该数据的“存放起始地址%N=0”。结构体的成员变量要对齐存放,结构体本身也要根据自身的有效对齐值圆整(即结构体成员变量占用总长度为结构体有效对齐值的整数倍)。
struct A{ int a; char b; short c; }; struct B{ char b; int a; short c; };
sizeof(A) = 8, sizeof(B) = 12;
A中,最初的int是4字节对齐,char是1字节对齐,所以前5个字节不需要填充,后面short长度为2个字节,和2对齐,所以char后面补一个字节,总共是8个字节。整个数组的有效对齐值是成员的最大值4, 8个字节是4的整数倍, 所以结构体总长度为8。
B中,最初char是1字节,随后int长度是4字节,和4字节对齐,char后面补3个字节,最后short是第9和10字节,也是对齐的。整个结构体的有效对齐值是4,结构体长度需要是4的整数倍,所以short后面还需要补2个字节,总长度为12。
之所以编译器在后面补充2个字节,是为了实现结构数组的存取效率。试想如果定义一个结构B的数组,那么第一个结构起始地址是0没有问题,但是第二个结构呢?按照数组的定义,数组中所有元素都紧挨着。如果我们不把结构体大小补充为4的整数倍,那么下一个结构的起始地址将是0x0000A,这显然不能满足结构的地址对齐。因此要把结构体补充成有效对齐大小的整数倍。
更改对齐方式:
在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件:
使用伪指令#pragma pack(n):C编译器将按照n个字节对齐;
使用伪指令#pragma pack(): 取消自定义字节对齐方式。在编码时,可用#pragma pack动态修改对齐值。自定义对齐值后要用#pragma pack()来还原,否则会对后面的结构造成影响。
#pragma pack(2) //指定按2字节对齐 struct C{ char b; int a; short c; }; #pragma pack() //取消指定对齐,恢复缺省对齐
char自身对齐值是1,指定对齐值是2,所以有效对齐值是1。 int自身对齐值是4,指定对齐值是2,所有有效对齐值是2,放在2的倍数的地址空间上,char后面只会补1个字节。所以到这里长度是6,后面short两个字节,有效对齐值也是2,结构体总长度是8。
需要注意,pragma pack指定的对齐值,是数据类型的最大对齐值,可以小,但是不能大。
因为对齐,会产生的问题:
1,数据类型强转可能会因为对齐,出错。 short类型应该放在2的倍数的地址上,但是这里p1却指向了奇数地址。需要注意。
int main(void){ unsigned int i = 0x12345678; unsigned char *p = (unsigned char *)&i; *p = 0x00; unsigned short *p1 = (unsigned short *)(p+1); *p1 = 0x0000; return 0; }
2,不同的处理器之间传递数据时,因为两个处理器可能采用的填充方式不一致,会导致数据出错。这时,可以在定义数据结构时,自己把需要填充的部分用char类型的数据填上,这样就不会不一致了。或者使用pragma pack 1, 让数据都按照1字节对齐。
二、栈内存对齐
在VC/C++中,栈的对齐方式不受结构体成员对齐选项的影响。总是保持对齐且对齐在4字节边界上。(直接看博客原文吧,他里面说的char和short没有凑到4个字节,我认为是已经凑到一起了)。
三、位域的对齐方式
有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1两种状态,用一位二进位即可。为了节省存储空间和处理简便,C语言提供了一种数据结构,称为“位域”或“位段”。
1,为了节省内存,对于大量的结构体数组来讲。 2,需要访问字节内的bit成员。两种情况会使用位域。
位域成员,除了指定所占用的bit位外,还有一个类型。位域成员不能单独被取sizeof值。下面主要讨论含有位域的结构体的sizeof。
其对齐规则大致为:
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++和GCC采取压缩方式;
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍,而位域则按照其最宽类型字节数对齐。
位域可以无位域名,只用作填充或调整位置,占位大小取决于该类型。例如,char :0表示整个位域向后推一个字节,即该无名位域后的下一个位域从下一个字节开始存放,同理short :0和int :0分别表示整个位域向后推两个和四个字节。
当空位域的长度为具体数值N时(如int :2),该变量仅用来占位N位。
原文还有位域的例子和字节大小端的例子,在这里就不写了。
更多相关内容 -
C语言 字节对齐
2022-03-29 17:05:33结构体变量的首地址能够被其最宽基本类型成员的大小所整除; 结构体每个成员相对结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要...// char[*]多少都是按4字节对齐 struct stChar{ char a[5]; int b; .字节对齐 、pragma 、位域长度问题整理
- 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
- 结构体每个成员相对结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;
- 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
#include <stdio.h> // 按结构体最宽数据类型int对齐 // char[*]多少都是按4字节对齐 struct stChar{ char a[5]; int b; char c; }; // 按结构体最宽数据类型short对齐 struct stShort{ char a; short b; }; // 按结构体最宽数据类型long对齐 struct stLong{ char a; long b; }; // 按结构体最宽数据类型int对齐 // 变量c的偏移量 要为自己大小的倍数 #pragma pack(8) //为2时,sizeof为10.为8时sizeof为12 struct AA { int a; char b; short c; //长度2 偏移量要提升到2的倍数6;存放位置区间[6,7] char d; }; struct AAA { // 类型说明符 位域名: 位域长度 int a:2; //变量a,4位长度,只使用两位长度 sizeof(AAA)为4 char b:1; }; int main() { struct stChar stChar1; struct stShort stShort1; struct stLong stLong1; struct AA stAA; struct AAA stAAA; printf("stChar = [%ld]\n", sizeof(stChar1)); printf("stShort = [%ld]\n", sizeof(stShort1)); printf("stLong = [%ld]\n", sizeof(stLong1)); printf("stAA = [%ld]\n", sizeof(stAA)); printf("stAAA = [%ld]\n", sizeof(stAAA)); return 0; }
打印结果
stChar = [16] stShort = [4] stLong = [16] stAA = [12] stAAA = [4]
-
c语言字节对齐
2021-11-26 11:15:16c语言驱动变成,网络通讯内核模块编程c语言结构体字节对齐详解
正在上传…重新上传取消cpp加油站发布于 4 月 22 日
1.什么是字节对齐
在c语言的结构体里面一般会按照某种规则去进行字节对齐。
我们先看一段代码:
struct st1 { char name; double age; char sex; }; //32位下 sizeof(struct st1) = 16 //64位下 sizeof(struct st1) = 24 struct st2 { char a; char b; char c; }; //32位和64位下, sizeof(struct st2)都是3个字节
从以上结果可以看出,结构体st1在32位下是按照4个字节来对齐的,在64位下则是按照8个字节来对齐的,结构体st2则不管32位还是64位则都是按照1个字节对齐的。
那么我们可以总结出对齐规则如下:
- 在所有结构体成员的字节长度都没有超出操作系统基本字节单位(32位操作系统是4,64位操作系统是8)的情况下,按照结构体中字节最大的变量长度来对齐;
- 若结构体中某个变量字节超出操作系统基本字节单位,那么就按照系统字节单位来对齐。
注意:并不是32位就直接按照4个字节对齐,64位按照8个字节对齐。
2.为什么要有字节对齐
首先普及一点小知识,cpu一次能读取多少内存要看数据总线是多少位,如果是16位,则一次只能读取2个字节,如果是32位,则可以读取4个字节,并且cpu不能跨内存区间访问。
假设有这样一个结构体如下:
struct st3 { char a; int b; }; //那么根据我们第1节所说的规则,在32位系统下,它就应该是8个字节的。
地址空间是类似下面这样的:
heigh 0x00000008 0x00000007 0x00000006 0x00000005 0x00000004 0x00000003 0x00000002 low 0x00000001 (栈顶) 在没有字节对齐的情况下,变量a就是占用了0x00000001这一个字节,而变量b则是占用了0x00000002~0x000000005这四个字节,那么cpu如果想从内存中读取变量b,首先要从变量b的开始地址0x00000002读到0x0000004,然后再读取一次0x00000005这个字节,相当于读一个int,cpu从内存读取了两次。
而如果进行字节对齐的话,变量a还是占用了0x00000001这一个字节,而变量b则是占用了0x00000005~0x00000008这四个字节,那么cpu要读取变量b的话,就直接一次性从0x00000005读到0x00000008,就一次全部读取出来了。
所以说,字节对齐的根本原因其实在于cpu读取内存的效率问题,对齐以后,cpu读取内存的效率会更快。但是这里有个问题,就是对齐的时候0x00000002~0x00000004这三个字节是浪费的,所以字节对齐实际上也有那么点以空间换时间的意思,具体写代码的时候怎么选择,其实是看个人的。
3.手动设置对齐
什么情况下需要手动设置对齐:
- 设计不同CPU下的通信协议,比如两台服务器之间进行网络通信,共用一个结构体时,需要手动设置对齐规则,确保两边结构体长度一直;
- 编写硬件驱动程序时寄存器的结构;
手动设置对齐方式有两种:
- 代码里添加预编译标识:
//用法如下 #pragma pack(n)//表示它后面的代码都按照n个字节对齐 struct st3 { char a; int b; }; #pragma pack()//取消按照n个字节对齐,是对#pragma pack(n)的一个反向操作 //这里计算sizeof(st3)=5
上面这两行其实就类似于开车的时候,走到某一段路的时候,发现一个限速60公里的指示牌,过了那一段路以后,又会有解除限速60公里的指示牌。
- 定义结构体时:
//用法如下 struct bbb { char a; int b; }__attribute__((packed));//直接按照实际占用字节来对齐,其实就是相当于按照1个字节对齐了 //这里计算sizeof(st3)=5
4.结构体比较方法
可以使用内存比较函数memcpy进行结构体比较,但因为结构体对齐可能会有填充位不一致的情况,此时需要注意:
- 设置为1个字节对齐,使它没有空位;
- 事先对结构体进行初始化;
memcpy(char *dest, const char* src, int len); //头文件#include<string.h>
-
C语言字节对齐问题详解
2020-07-29 12:45:35本文就C语言中字节对齐的问题进行详细的解析,感性趣的朋友可以看看。 -
C语言字节对齐讲解及实例
2020-07-26 21:37:15在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构、联合等)的数据单元。 -
C语言字节对齐详解
2021-05-19 09:23:36先了解4个基本概念:1、数据类型自身对齐值:即数据类型的大小(数组取数组成员类型的自身对齐值),如char的自身对齐值是1,short是2,int、float、double都是4,单位字节2、结构体的自身对齐值:结构体成员中自身...先了解4个基本概念:
1、数据类型自身对齐值:即数据类型的大小(数组取数组成员类型的自身对齐值),如char的自身对齐值是1,short是2,int、float、double都是4,单位字节
2、结构体的自身对齐值:结构体成员中自身对齐值最大的那个值
3、指定对齐值:使用#pragma pack (value)时指定的对齐值alue
4、有效对齐值:自身对齐值和指定对齐值中较小的那个值
其次,数据存放必须满足以下两个法则:
法则1:有效对齐值为N的数据在内存中存放的起始地址必须是N的整数倍,即满足“存放起始地址 # N = 0”
法则2:结构体的变量占用的总长度必须是结构体有效对齐值的整数倍
有了以上基础,就能判断一个结构体占用内存的空间大小,下面举几个例子分析:
struct B
{
char b;
int a;
short c;
}
假设B从0x0000开始存放,并且默认编译环境默认的指定对齐值是4。第一个成员变量b的自身对齐值是1,比指定对齐值4小,所以有效对齐值是1,所以存放在0x0000空间。第二个成员a的自身对齐值是4,指定对齐值亦是4,所以有效对齐值是4,其存放起始气质必须是4的整数倍,所以必须存放在0x0004~0x0007空间。第三个成员c的自身对齐值是2,比指定对齐值4小,所以有效对齐值是2,紧接a占用空间的下一个起始地址0x0008满足2的整数倍,所以存放在0x0008~0x0009空间。最后,B的自身对齐值是具有最大自身对齐值的成员变量a的自身对齐值4,等于指定对齐值4,所以B总共占用0x0000~0x0011空间。
#pragma pack (2);
struct C
{
char b:
int a;
short c:
};
虽然结构体C的成员组成和结构体B的成员完全一致,但由于定义结构体前指定了对齐值为2,所以所有自身对齐值大于2的成员变量的有效对齐值都变成了2,而且结构体的最终有效对齐值也变成了2,所以占用空间比B小,如下图:
对于联合union,遵循的原则也是一样的,只不过union最终的长度是仅按照占用空间最大的那个成员来计算的,如
union D{
char a; //自身对齐值1
int b[5]; //自身对齐值4,占用空间最大为5×4 = 20
double c; //自身对齐值8
int d[3]; //自身对齐值4
};
按占用空间最大的成员长度计算,该union初步的长度的20字节,而所有成员中自身对齐值最大的是c,为8,所以该union长度要圆整到8的整数倍,即最终长度是24字节
在编程的时候,为减少中间的填补空间,基本原则是把结构体中的变量按照自身对齐值从小到大排列
-
C语言字节对齐
2021-05-21 10:24:06什么是字节对齐?现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据按照... -
c语言字节对齐的计算方式
2020-06-09 17:02:39结构体的字节对齐,在笔试中经常考到,这里从下面三段代码简单的总结一下结构体字节对齐的计算方式。 计算的时候需要遵循一个原则,即每个变量的其实地址应该为它长度的整数倍。 struct Data{ int a; char b; ... -
C语言字节对齐4
2021-05-19 07:53:56非字节对齐类型的字节对齐规则我们可以使用“__packed”、“__attribute__((packed))”、“#pragma”等方式控制结构体的字节对齐,这些结构体的内部结构在定义时就已经确定了,当它们被包含在其它结构体内部时它们被... -
C语言字节对齐 #pragma pack() __attribute((aligned (n)))
2021-03-19 17:20:26C语言字节对齐 对于标准数据类型,它的地址只要是它的长度的整数倍就行了,而非标准数据类型按下面的原则对齐: 数组 :按照基本数据类型对齐,第一个对齐了后面的自然也就对齐了。 联合 :按其包含的长度最大的数据... -
C语言 字节对齐问题 详解
2019-06-20 19:06:53一 什么是字节对齐 现代计算机中,内存空间按照字节划分,理论上可以从任何起始地址访问任意类型的变量。但实际中在访问特定类型变量时经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列... -
C语言字节对齐[归类].pdf
2021-10-11 01:42:25C语言字节对齐[归类].pdf -
理一理C语言字节对齐的那些事
2021-05-21 06:01:58前言字节对齐是我们初学 C语言 就会接触到的一个概念,但是到底什么是字节对齐?对齐准则又是什么?为什么要字节对齐呢?字节对齐对我们编程有什么启示?本文将简单理一理字节对齐的那些事。什么是字节对齐计算机中... -
C语言字节对齐__align()讲解[整理].pdf
2021-10-11 01:42:27C语言字节对齐__align()讲解[整理].pdf -
C语言字节对齐、结构体对齐最详细的解释
2018-09-13 22:52:16一、概念 对齐跟数据在内存中的位置有关。... 需要字节对齐的根本原因在于CPU访问数据的效率问题。假设上面整型变量的地址不是自然对齐,比如为0x00000002,则CPU如果取它的值的话需要访问两次内存,第... -
C语言字节对齐笔记
2019-05-09 15:29:49** 一、字节对齐的意义**: 通过合理的内存对齐可以提高访问效率,有效地节省存储空间,为使CPU能够对数据进行快速访问,数据的起始地址应具有“对齐”特性。比如4字节数据的起始地址应位于4字节边界上,即起始地址... -
C-一文搞懂c语言字节对齐
2020-09-24 12:26:20一文搞懂c语言字节对齐 如果你对c语言的字节对齐总感觉模糊,不能从理论上推导出一个复杂结构体实际占用的内存大小,那么你必须要看看本博文,硬干货!!! 测试环境是ubutun 64位 文章目录基本概念自身对齐默认对齐... -
C语言字节对齐及设置编译对齐方式方法
2018-07-17 15:25:23一、概念 对齐跟数据在内存中的位置有关。如果一个变量的内存地址... 需要字节对齐的根本原因在于CPU访问数据的效率问题。假设上面整型变量的地址不是自然对齐,比如为0x00000002,则CPU如果取它的值的话... -
C语言字节对齐64位和32位
2018-07-05 10:05:21(第一次写博客:有不对的地方还望指出)借前辈们的话再详细补充linux64位下字节对齐: 对齐:在GNU GCC 编译器中,遵循的准则:根据最宽的基本数据类型来定:对齐模数最大只能是4,也就是说,即使结构体中有double... -
C语言的字节对齐
2021-01-19 09:37:29一、基本概念 1. 什么是自然对齐 对于一个存储在内存中的变量,如果它的内存...例子:以32位的CPU为例,32位的CPU,默认是4字节[32 / 8 = 4]对齐。 当整型数据A的存储起始地址为0x2,则它在内存中的数据占据了4.. -
C语言字节对齐,看这篇就够了
2021-08-31 13:09:08默认32位OS对齐字节是4,64位对齐字节是8。'N'有可能影响结构体内部成员的对齐位置,以及结构体整体大小。 gcc 中,N不能大于默认的对齐字节数,否则不生效。 对齐规则 规则一.:每个成员变量在其结构体内的...