时间:2021-05-20
Data Alignment
关于数据对齐问题,现在多多少少有了一些接触,简单地说下自己的看法。
1、对齐的背景
大端和小端的问题有必要在这里介绍一下,计算机里面每个地址单元对应着一个字节,一个字节为8bit,对于位数大于8位的处理器来说,寄存器的宽度是大于一个字节的,例如16bit的short型变量x,在内存中的地址是0x0010,x的值为0x1122,0x11为高字节,0x22为低字节,常用的X86结构是小端模式,很多ARM,DSP都是小端模式,而KEIL C51则为大端模式。内存空间是按照byte进行划分的,理论上对任何类型的变量的访问可以从任何地址开始,但实际上访问特定变量的时候经常在特定的内存地址访问,这就需要各类型的数据按照一定的规则在空间上排列,而不是顺序排列,这就是对齐。
2、对齐的原因
不同硬件平台对存储空间的处理是有很大不同的,一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况, 但是最常见的是如果不按照适合其平台的要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为 32位)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低 字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。这也是空间和时间的博弈。
3、对齐的实现
通常我们在写代码的时候是不需要考虑对齐的影响的,都是依赖编译器来为我们选择适合的对齐策略,我们也可以通过传递给编译器预编译指令来指定数据对齐的方法。
以struct数据结构的sizeof方法为例,环境是Mac OS X 64位内核,结构体的定义如下:
struct A {
int a;
char b;
short c;
};
struct B {
char b;
int a;
short c;
};
#pragma pack(2)
struct C {
char b;
int a;
short c;
};
#pragma pack()
#pragma pack(1)
struct D {
char b;
int a;
short c;
};
#pragma pack()
int main(int argc, char** argv)
{
printf("size of struct A : %lu \n", sizeof(struct A));
printf("size of struct B : %lu \n", sizeof(struct B));
printf("size of struct C : %lu \n", sizeof(struct C));
printf("size of struct D : %lu \n", sizeof(struct D));
return 0;
}
输出:
size of struct A : 8
size of struct B : 12
size of struct C : 8
size of struct D : 7
结构体中包含了4字节长度的int一个,1字节长度的char一个以及2字节长度的short一个。加起来所用到的内存空间为7个字节,但实际使用sizeof时发现,结构体之间占用的内存是不一样的。
关于对齐有几个需要说明的:
(1)数据类型自身的对齐值:基本数据类型的自身对齐值,char类型为1,short类型为2,int,float,double为4;
(2)指定对齐值:#pragma pack(value)时的指定对齐值value;
(3)结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值;
(4)数据成员、结构体和类的有效对齐值:自身对齐值或指定对齐值中较小值。
对于一个具体的数据结构的成员和其自身的对齐方式,有效对齐值N将最终决定数据存放地址的方式的值,对齐在N上就意味着数据“存放的起始地址%N=0”,
下面来针对上面的例子进行分析:
struct B {
char b;
int a;
short c;
};
假设B从地址空间0x0000开始,默认的对齐值是4(这里有个问题想请教大家,我的是64位的内核,但是测试我的默认对齐方式为4),第一个成员变量b的自身对齐值为1,比默认值小所以有效对齐值为1,存放地址0x0000%1=0,第二个成员变量a,自身对齐值为4,存放的起始地址为0x0004到0x0007这个4个连续的字节空间中,0x0004%4=0,第三个变量c,自身对齐值为2,存放的起始地址为0x0008到0x0009,地址同样符合要求。结构体B的自身对齐值为变量中的最大对齐值(b)4,(10+2)%4=0,所以0x000A到0x000B也是被结构体B占用。
内存中的示意图:
b - - -
a a a a
c c
#pragma pack(2)
struct C {
char b;
int a;
short c;
};
#pragma pack()
第一个变量b的自身对齐值为1,指定对齐值为2,有效对齐值为1,b存放在0x0000,a的自身对齐值为4,大于指定对齐值,所有有效的对齐值为2,a占有的字节为0x0002、0x0003、0x0004、0x0005四个连续字节中,c的自身对齐值为2,所以有效对齐值也是2,顺序存放在0x0006、0x0007。结构体C的自身对齐值为4,所以有效对齐值为2,8%2=0。
内存中的示意图:
b -
a a
a a
c c
其实想到内存中的示意图一切都会简单很多。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
本文详细叙述了C/C++的位操作,这是C/C++程序设计中非常重要的概念。本文实例对于C/C++初学者来说也具有很好的复习与参考价值。具体分析如下:C/C++对
C++中的内存对齐实例详解内存对齐在我们的程序中,数据结构还有变量等等都需要占有内存,在很多系统中,它都要求内存分配的时候要对齐,这样做的好处就是可以提高访问内
现在比较流行C#与C++融合:C#做GUI,开发效率高,C++做运算,运行效率高,二者兼得。但是C++与C#必然存在数据交互,C#与C++dll的数据交互从来都
原理python没有办法直接和c++共享内存交互,需要间接调用c++打包好的库来实现流程C++共享内存打包成库python调用C++库往共享内存存图像数据C++
本系列教程主要介绍如何在C/C++程序里面嵌入Lua脚本,我打算从以下几个方面来介绍:1.如何在C/C++里面嵌入Lua脚本2.Lua访问C/C++数据结构(这