A004-数据对齐的原因
来源:互联网 发布:node path resolve 编辑:程序博客网 时间:2024/04/19 11:01
今天读到一篇数据对齐的文章,明白了为什么需要数据对齐(地址对齐)。
不过作者的语言是属于作者自己的,我并不能立即理解他的全部表达。
只有我自己去思考推导之后才明白他的一些表达,因此我有必要将自己对这篇文章的解读记录下来。
原文:http://blog.csdn.net/tigerscorpio/article/details/5933807
.
地址对齐
数据对齐:数据A的存储地址的地址值 必须能整出 数据A的数据长度。
例如:
2Byte 的数据、就应该被存放在地址值为2的倍数的内存上。
4Byte的数据、就应该被存放在地址值为4的倍数的内存上。
所以,我们通常说 1字节的数据无论如何都是对齐的,2字节的数据需要2字节对齐,4字节的数据需要4字节对齐。
.
对齐的原因
(以32位机器为例、不考虑大小端)
数据需要对齐,是因为计算机在设计时,将32bit的数据分成4个1字节的数据,分别存放在4个内存芯片上,4个内存芯片共同构成一块最终的芯片。
4个芯片共用一条地址总线,每个内存单元的地址如下:
假如有数据A = 0x12345678,存放的地址是 0x00,是4字节对齐的。
那么这个4个字节会分别被存放在4块芯片的Offset=0处:
CPU读取数据A时,会向地址总线发出 addr = 0x00 的地址值,由于4块芯片共用地址总线,所以4块芯片收到的地址值都是0x00,结果是都寻址到4块芯片的Offset=0处,一次寻址就读出32bit 的数据A。
.
假如数据A 不是4字节对齐 存放 的,比如存放的地址是 0x01。
那么这4个字节会分别被存放在4块芯片的如下位置:
读取数据A时,CPU向地址总线发出 addr = 0x01 的地址值,由于4块芯片共用地址总线,所以4块芯片收到的地址值都是0x01,结果是都寻址到4块芯片的Offset=1处。
而芯片数据A 的第4个字节0x78 是在Chip-0的Offset=1(地址4)处,而其他3个字节都不在这3块芯片的Offset=1处、而是在它们的Offset=0 处,所以这次只正确地读出数据A的第4个字节,这显然不是我们期待的结果。
要正确的取得数据A 的四个字节,CPU可以采取读两次的方式:
然后再将第一次读取出来的数据的第1个字节、替换成第二次读取出来的第1个字节。
这样读取的效率会很低,要读两次,最后还要组合一下。
.
当然,还可以让4块芯片不共用一条地址总线,而是使用独立的4条地址总线。
一次性的发送4路地址值,直接定位到4块芯片的4个字节去,这样CPU一次就可以读出4字节的数据A了。
但是地址总线的数量会增加到原来的4倍,也就是地址线IO和配套的寄存器都增加到原来的4倍,还得修改地址译码的控制器。
这个成本是很高的,所以设计CPU的厂家也不这样干,就要设计成地址对齐。
因此地址对齐的要求就是这样来的,都是CPU设计成了这样,所以就变成了这样,目的是为了让CPU的结构更简洁。
如果我们的数据不对齐,会出现以下三种结果:
我之前用的一款cortex-M0的芯片、一读到没对齐的数据,CPU就跳转到hardfault中断,用户程序只能调用CHIP-RESET指令重启芯片或者关机。这个芯片就是这样直接,强制罢工,强制用户将数据对齐。
.
对齐规则
(假设4字节对齐,测试环境:Visual Studio 2012)
每个数据对象都有一个对齐限制,这个概念称为数据对象的对齐模数。
基本数据类型的对齐模数如下:
当编译器指定的对齐模数和这些基本类型本身的对齐模数不一致时,选择较小的一个模数。
.
比如编译器要求4字节对齐,那么:
.
假如编译器要求2字节对齐,那么:
.
结构体的对齐
对于结构体/联合体/数组类型,它的对齐模数是其内部各成员的对齐模数的最大值。
对于结构体/联合体,还要求他占据的空间是对齐模数的倍数。
例如:
struct student{ char data; int number; char addr;}stu;
它的3个成员的对齐模数各自为:
所以student 的对齐模数是4。
student在内存中的存储情况如下:
data 存放在Offset=0。
接着空余3字节,因为这3个字节的地址都不是4对齐的。
而number 的存储需要4字节对齐,所以number只能存放在Offset=4处。
addr 存放在Offset=8处。
接着空余3字节,因为student 的对齐模数是4,他占据的空间被要求是4的倍数。
所以空余3个字节后,student 的总大小从9字节增加到12字节,是4的倍数了。
因此student 一共占据了12字节的空间。
.
显然,将addr 放置在data 的后面,将number 放在addr 的后面,这样student占据的空间就降低到8字节。
因为addr 是1字节对齐,可以被存放在Offset=1处:
.
位域的对齐
对于结构体中的位域的对齐,还要看其内部成员的类型和大小。
例如:
struct flame_data{ char a : 3; char b : 3; int c : 2; char d : 4; char e : 5;}IR_data;
变量a 和 b 的类型相同,而且他们一共才6bit,可以存放在同1个字节中,因此a 和 b 共同占据1个字节。
变量c 才2bit,和前面的a、b 一共刚好是8bit,但是他们类型的长度不同,所以不能合并存储到同1个字节中,同样c 也不能和d 合并,只能单独存储。
同时c 要对4字节对齐,所以在a 和 b 占据的那1个字节之后,还需要空出3个字节,让c 存储到到地址对齐后的第4个字节。
变量d 和 e 类型相同,但是他们加起来的长度超过了8bit,因此也不能合并到同1个字节中,因此d 和 e 各自占据1个字节。
这里一共占据10个字节。
最后、flame_data 作为结构体,它占据的空间长度还需要是其对齐模数的倍数。
而flame_data 的对齐模数是4,所以在上面的10字节后面还需要增加2字节,以整除4。
因此flame_data 最终一共占据12个字节。
.
特殊的位域
位域还有2个特殊的地方:
(1). 未命名的域
例如:
struct flame_k{ char a : 3; char : 2; char b : 3;}k_data;
这3个域可以被合并存储到同1个字节(类型一致,总大小没超过8bit),但未命名的第2个域不能被引用。
(2). 大小为0的域
例如:
struct flame_j{ char a : 3; char : 0; char b : 3;}j_data;
大小为0的域用来将它的前后两个域隔开,禁止他们合并到一起。
所以这个位域一共占据2个字节。
.
pragma pack( n )
32位机器默认是4字节对齐,但我们可以使用#pragma pack(n) 来指定n字节对齐。
如#pragma pack(4) 将对齐模数人为指定为4字节对齐。
而#pragma pack() 用来取消刚才设置的自定义对齐模数、采用编译器默认的对齐模数。
- A004-数据对齐的原因
- A004:Linux和Windows的传输
- 内存对齐的原因调查
- 深究“字节对齐”的原因
- 数据的对齐
- 数据对齐的解释
- 眼中的数据对齐
- 能对齐的数据
- A004: Python中的module,library,package之间的区别
- 我眼中的数据对齐
- 对数据对齐的探索
- 关于数据对齐的摘要
- 经典的数据对齐解说
- Microsoft Windows数据的对齐
- 结构体的数据对齐
- 数据的对齐(alignment)
- 内存对齐原因
- 数据对齐 字节对齐
- 学习笔记之——自定义Dialog仿ios弹出底部菜单
- 单击AutoCompleteTextView后自动提示,无需输入字符,提示信息通过SQLite查询获得
- 多尔衮是清初开国头号美男
- java.lang.IllegalStateException: Fragment already active,Fragment.setArguments()问题
- SystemBarTintManager.java
- A004-数据对齐的原因
- iOS - 工程引入doubango (idoubs编译)
- 线性插值和双线性插值
- IOS 自定义返回按钮 滑动返回假死解决方法
- mac gradle遇到问题
- Android OkHttp(一)初识
- POJ 5690 All X
- json循环依赖导致生成json失败
- Struts2-校验器优先校验配置