ARM C C++内存对齐
ARM 系列处理器是 RISC (Reducded Instruction Set Computing)处理器。很多基于ARM的高效代码的程序设计策略都源于RISC 处理器。和很多 RISC 处理器一样,ARM 系列处理器的内存访问,也要求数据对齐,即存取“字(Word)”数据时要求四字节对齐,地址的bits[1:0]==0b00;存取“半字(Halfwords)”时要求两字节对齐,地址的bit[0]==0b0;存取“字节(Byte)”数据时要求该数据按其自然尺寸边界(Natural Size Boundary)定位。
ARM 编译程序通常将全局变量对齐到自然尺寸边界上,以便通过使用 LDR和 STR 指令有效地存取这些变量。这种内存访问方式与多数 CISC (Complex Instruction Set Computing)体系结构不同,在CISC体系结构下,指令直接存取未对齐的数据。因而,当需要将代码从CISC 体系结构向 ARM 处理器移植时,内存访问的地址对齐问题必须予以注意。在RISC体系结构下,存取未对齐数据无论在代码尺寸或是程序执行效率上,都将付出非常大的代价。
本文将从以下几个方面讨论在ARM体系结构下的程序设计问题。
未对齐的数据指针
C和C++编程标准规定,指向某一数据类型的指针,必须和该类型的数据地址对齐方式一致,所以ARM 编译器期望程序中的 C 指针指向存储器中字对齐地址,因为这可使编译器生成更高效的代码。
比如,如果定义一个指向 int 数据类型的指针,用该指针读取一个字,ARM 编译器将使用LDR 指令来完成此操作。如果读取的地址为四的倍数(即在一个字的边界)即能正确读取。但是,如果该地址不是四的倍数,那么,一条 LDR 指令返回一个循环移位结果,而不是执行真正的未对齐字载入。循环移位结果取决于该地址向对于字的边界的偏移量和系统所使用的端序(Endianness)。例如,如果代码要求从指针指向的地址 0x8006 载入数据,即要载入 0x8006、0x8007、0x8008 和 0x8009 四字节的内容。但是,在 ARM 处理器上,这个存取操作载入了0x8004、0x8005、0x8006 和 0x8007 字节的内容。这就是在未对齐的地址上使用指针存取所得到的循环移位结果。
因而,如果想将指针定义到一个指定地址(即该地址为非自然边界对齐),那么在定义该指针时,必须使用 __packed 限定符来定义指针: 例如,
__packed int *pi; // 指针指向一个非字对其内存地址
使用了_packed限定符限定之后,ARM 编译器将产生字节存取命令(LDRB或STRB指令)来存取内存,这样就不必考虑指针对齐问题。所生成的代码是字节存取的一个序列,或者取决于编译选项、跟变量对齐相关的移位和屏蔽。但这会导致系统性能和代码密度的损失。
值得注意的是,不能使用 __packed 限定的指针来存取存储器映射的外围寄存器,因为 ARM 编译程序可使用多个存储器存取来获取数据。因而,可能对实际存取地址附近的位置进行存取,而这些附近的位置可能对应于其它外部寄存器。当使用了位字段(Bitfield)时, ARM 程序将访问整个结构体,而非指定字段。
在ARM中,通常希望字单元的地址是字对齐的(地址的低两位为0b00),半字单元的地址是半字对齐的(地址的最低为0b0).在存储访问操作中,如果存储单元的地址没有遵守上述的对齐规则,则称为非对齐(unaligned)的存储访问操作.
代码中关于对齐的隐患,很多是隐式的。比如在强制类型转换的时候。例如:
unsigned int i = 0×12345678;
unsigned char *p=NULL;
unsigned short *p1=NULL;
p=&i;
*p=0×00;
p1=(unsigned short *)(p+1);
*p1=0×0000;
最后两句代码,从奇数边界去访问unsignedshort型变量,显然不符合对齐的规定。
在x86上,类似的操作只会影响效率,但是在MIPS或者sparc上,可能就是一个error,因为它们要求必须字节对齐.
有部分摘自ARM编译器文档对齐部分
对齐的使用:
1.__align(num)
这个用于修改最高级别对象的字节边界。在汇编中使用LDRD或者STRD时
就要用到此命令__align(8)进行修饰限制。来保证数据对象是相应对齐。
这个修饰对象的命令最大是8个字节限制,可以让2字节的对象进行4字节
对齐,但是不能让4字节的对象2字节对齐。
__align是存储类修改,他只修饰最高级类型对象不能用于结构或者函数对象。
2.__packed
__packed是进行一字节对齐
1.不能对packed的对象进行对齐
2.所有对象的读写访问都进行非对齐访问
3.float及包含float的结构联合及未用__packed的对象将不能字节对齐
4.__packed对局部整形变量无影响
5.强制由unpacked对象向packed对象转化是未定义,整形指针可以合法定
义为packed。
__packed int* p; //__packed int 则没有意义
6.对齐或非对齐读写访问带来问题
__packed struct STRUCT_TEST
{
char a;
int b;
char c;
} ; //定义如下结构此时b的起始地址一定是不对齐的
//在栈中访问b可能有问题,因为栈上数据肯定是对齐访问[from CL]
//将下面变量定义成全局静态不在栈上
static char* p;
static struct STRUCT_TEST a;
void Main()
{
__packed int* q; //此时定义成__packed来修饰当前q指向为非对齐的数据地址下面的访问则可以
p = (char*)&a;
q = (int*)(p+1);
*q = 0×87654321;
/*
得到赋值的汇编指令很清楚
ldr r5,0×20001590 ; = #0×12345678
[0xe1a00005] mov r0,r5
[0xeb0000b0] bl __rt_uwrite4 //在此处调用一个写4byte的操作函数
[0xe5c10000] strb r0,[r1,#0] //函数进行4次strb操作然后返回保证了数据正确的访问
[0xe1a02420] mov r2,r0,lsr #8
[0xe5c12001] strb r2,[r1,#1]
[0xe1a02820] mov r2,r0,lsr #16
[0xe5c12002] strb r2,[r1,#2]
[0xe1a02c20] mov r2,r0,lsr #24
[0xe5c12003] strb r2,[r1,#3]
[0xe1a0f00e] mov pc,r14
*/
/*
如果q没有加__packed修饰则汇编出来指令是这样直接会导致奇地址处访问失败
[0xe59f2018] ldr r2,0×20001594 ; = #0×87654321
[0xe5812000] str r2,[r1,#0]
*/
from:http://www.360doc.com/content/14/0527/14/7647352_381424711.shtml
ARM C C++内存对齐的更多相关文章
- C/C++中的内存对齐 C/C++中的内存对齐
一.什么是内存对齐.为什么需要内存对齐? 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址 ...
- 从硬件到语言,详解C++的内存对齐(memory alignment)
转载请保留以下声明 作者:赵宗晟 出处:https://www.cnblogs.com/zhao-zongsheng/p/9099603.html 很多写C/C++的人都知道“内存对齐”的概念以及规则 ...
- crash:EXC_ARM_DA_ALIGN(关于内存对齐,memcpy)
crash:EXC_ARM_DA_ALIGN(关于内存对齐,memcpy) 问题描述 在iOS game开发时做内存拷贝时出现了 crash:EXC_ARM_DA_ALIGN,debug版本不会出现, ...
- 深入理解c/c++ 内存对齐
内存对齐,memory alignment.为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐.原因在于,为了访问未对齐的内存,处理器需要作两次内存访问:然而,对齐的内存访问仅需要一 ...
- 有关于__align(n) ,内存对齐
__align __align 关键字指示编译器在 n 字节边界上对齐变量. __align 是一个存储类修饰符.它不影响函数的类型. 语法 __align(n) 其中: n 是对齐边界. 对于局部变 ...
- 从硬件到语言,详解C++的内存对齐(memory alignment)(一)
作者:赵宗晟 出处:https://www.cnblogs.com/zhao-zongsheng/p/9099603.html 很多写C/C++的人都知道“内存对齐”的概念以及规则,但不一定对他有很深 ...
- Go中由WaitGroup引发对内存对齐思考
转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 本文使用的go的源码时14.4 WaitGroup使用大家都会,但是其中是怎么实现的我们 ...
- golang内存对齐分析(转载)
问题 type Part1 struct { a bool b int32 c int8 d int64 e byte } 在开始之前,希望你计算一下 Part1 共占用的大小是多少呢? func m ...
- C++内存对齐总结
大家都知道,C++空类的内存大小为1字节,为了保证其对象拥有彼此独立的内存地址.非空类的大小与类中非静态成员变量和虚函数表的多少有关. 而值得注意的是,类中非静态成员变量的大小与编译器内存对齐的设置有 ...
随机推荐
- jdk1.7中的常量池
在探究jdk1.7中的常量池,我们可以先看看以下的这段代码 public static void main(String[] args) throws Throwable { List<Stri ...
- cogs 558 奇怪的函数
提交地址:http://cojs.tk/cogs/problem/problem.php?pid=558 558. 奇怪的函数 ★☆ 输入文件:xx.in 输出文件:xx.out 简单对比 ...
- [POJ 3243]Clever Y
Description Little Y finds there is a very interesting formula in mathematics: XY mod Z = K Given X, ...
- codeforces 809E Surprise me!
Tired of boring dates, Leha and Noora decided to play a game. Leha found a tree with n vertices numb ...
- NOIP 2009 最优贸易
题目描述 C 国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市.任意两个 城市之间最多只有一条道路直接相连.这 m 条道路中有一部分为单向通行的道路,一部分 为双向通行的道路 ...
- ●POJ 2983 Is the Information Reliable?
题链: http://poj.org/problem?id=2983 题解: 差分约束. 1).对于条件(P u v w),不难发现反映到图上就是: $dis[u]-dis[v]=w$,所以添加两条边 ...
- hdu4729 树链剖分+二分
An Easy Problem for Elfness Time Limit: 5000/2500 MS (Java/Others) Memory Limit: 65535/65535 K (J ...
- [BZOJ]1005 明明的烦恼(HNOI2008)
BZOJ的第一页果然还是很多裸题啊,小C陆续划水屯些板子. Description 自从明明学了树的结构,就对奇怪的树产生了兴趣......给出标号为1到N的点,以及某些点最终的度数,允许在任意两点间 ...
- Unix系统的文件打开机构
当打开一个文件时,建立用户与该文件的联系.其实质是将该文件在辅存中的有关目录信息.辅存i节点及相应的文件地址索引表拷贝到主存中.文件系统中管理这一方面的机构成为打开文件管理机构,简称打开文件机构. 打 ...
- Spring利用反射调用接口
首先在项目中使用java反射调用 //根据实例及参数名获得方法A Method method = event.getObj().getClass() .getMethod(event.getMetho ...