C 对于外部指针声明,ARM代码显然无法正常工作
我正在编译一段ARM代码,生成的程序集不是我所期望的 以下代码:C 对于外部指针声明,ARM代码显然无法正常工作,c,gcc,assembly,arm,extern,C,Gcc,Assembly,Arm,Extern,我正在编译一段ARM代码,生成的程序集不是我所期望的 以下代码: #include <stdint.h> extern uint8_t* a; extern uint8_t b[]; void teste(void) { *a = b[1]; b[2] = *a; } 并生成以下asm: 00000000 <teste>: 0: 4a04 ldr r2, [pc, #16] ; (14 <teste+
#include <stdint.h>
extern uint8_t* a;
extern uint8_t b[];
void teste(void)
{
*a = b[1];
b[2] = *a;
}
并生成以下asm:
00000000 <teste>:
0: 4a04 ldr r2, [pc, #16] ; (14 <teste+0x14>)
2: 4b05 ldr r3, [pc, #20] ; (18 <teste+0x18>)
4: 6811 ldr r1, [r2, #0]
6: 7858 ldrb r0, [r3, #1]
8: 7008 strb r0, [r1, #0]
a: 6812 ldr r2, [r2, #0]
c: 7812 ldrb r2, [r2, #0]
e: 709a strb r2, [r3, #2]
10: 4770 bx lr
12: bf00 nop
...
00000000 <teste>:
0: 4a02 ldr r2, [pc, #8] ; (c <teste+0xc>)
2: 4903 ldr r1, [pc, #12] ; (10 <teste+0x10>)
4: 7853 ldrb r3, [r2, #1]
6: 7093 strb r3, [r2, #2]
8: 700b strb r3, [r1, #0]
a: 4770 bx lr
...
有人知道为什么第一种方法不能正常工作吗
“a”是内存中分配的字节变量的地址,因此将其声明为向量没有意义
谢谢您的帮助。想想数组和指针之间的区别:
- 数组是没有l值的符号。因此,它不能在运行时更改
- 指针是一种符号,不具有l值。因此,它可以在运行时改变
a
在运行时可能会更改,那么它必须添加一个代码,以便从内存加载其值,然后再尝试从它所指向的内存地址加载该值
如果编译器“知道”变量a
在运行时从不更改,那么它可以添加一个代码,直接从它所指向的(常量)内存地址加载该值
顺便说一句,虽然您的代码可以编译和链接而不会出错,但我不确定它在运行时不会因为变量
a
的声明不明确而崩溃,因此我建议您只需声明uint8\u t a[1]
只要考虑数组和指针之间的区别:
- 数组是没有l值的符号。因此,它不能在运行时更改
- 指针是一种符号,不具有l值。因此,它可以在运行时改变
a
在运行时可能会更改,那么它必须添加一个代码,以便从内存加载其值,然后再尝试从它所指向的内存地址加载该值
如果编译器“知道”变量a
在运行时从不更改,那么它可以添加一个代码,直接从它所指向的(常量)内存地址加载该值
顺便说一句,尽管您的代码可以编译和链接而不会出错,但我不确定它在运行时不会因为变量
a
的声明不明确而崩溃,因此我建议您只需声明uint8\u t a[1]
以下是a
和b
在内存中的样子:
+------------------------------------------+
a | uint8_t* >----------------------+
+------------------------------------------+ |
| +---------+
+---------+ +->| uint8_t | a[0] (or *a)
b[0] | uint8_t | +---------+
+---------+ | uint8_t | a[1] (or *(a+1))
b[1] | uint8_t | +---------+
+---------+ | ... |
b[2] | uint8_t |
+---------+
| ... |
请注意,b[1]
和b[2]
(可能还有b[0]
)显示为逻辑上驻留的位置,即使没有分配实际存储。另外,a
未初始化,因此它可能未指向有效的内存位置
链接/加载后,将知道a
和b
的地址。这些地址必须加载到寄存器中,以便访问变量所在的内存。在ARM中,地址作为数据值存储,并通过pc相对寻址进行访问:
+------------------------------------------+
teste+0 | |
| I n s t r u c t i o n s |
| |
+------------------------------------------+ +------------
teste+14 | uint8_t** >--------------------------> a | uint8_t * ...
+------------------------------------------+ +------------
teste+18 | uint8_t* >------------------------+
+------------------------------------------+ | +---------+
+-> b[0] | uint8_t |
+---------+
b[1] | uint8_t |
+---------+
b[2] | uint8_t |
+---------+
| ... |
通过将这些恒定地址存储在指令的固定(小)偏移量处,代码可以使用16位THUMB操作码将它们加载到寄存器中。相反,MIPS代码通常使用2 32位指令序列来完成相同的任务,方法是将嵌入指令中的16位直接指令加载到目标寄存器的上半部分和下半部分
现在让我们一步一步地看一下说明
0: 4a04 ldr r2, [pc, #16] ; (14 <teste+0x14>)
此行正在将b[0]
的地址(存储在子例程之后的代码中)加载到r3
4: 6811 ldr r1, [r2, #0]
此行正在将存储在a
中的指针加载到r1
中。因此,r1
现在指向一些uint8\t
(图中右侧浮动的一个)
此行将一个字节从地址r3+1
(又称b[1]
)加载到r0
8: 7008 strb r0, [r1, #0]
这将存储我们刚刚加载到地址r1+0
(又称*a
)中的字节
此行将a
重新加载到r2
中。这是不必要的,因为我们在r1
中已经有了这个值;但是,我猜您已经禁用了优化
c: 7812 ldrb r2, [r2, #0]
此行将一个字节从地址r2+0
(又称*a
)加载到r2
2: 4b05 ldr r3, [pc, #20] ; (18 <teste+0x18>)
e: 709a strb r2, [r3, #2]
此行存储我们刚刚加载到地址r3+2
(akab[2]
)中的字节
最后,我们从子程序返回。下面是内存中
a
和b
的样子:
+------------------------------------------+
a | uint8_t* >----------------------+
+------------------------------------------+ |
| +---------+
+---------+ +->| uint8_t | a[0] (or *a)
b[0] | uint8_t | +---------+
+---------+ | uint8_t | a[1] (or *(a+1))
b[1] | uint8_t | +---------+
+---------+ | ... |
b[2] | uint8_t |
+---------+
| ... |
请注意,b[1]
和b[2]
(可能还有b[0]
)显示为逻辑上驻留的位置,即使没有分配实际存储。另外,a
未初始化,因此它可能未指向有效的内存位置
链接/加载后,将知道a
和b
的地址。这些地址必须加载到寄存器中,以便访问变量所在的内存。在ARM中,地址作为数据值存储,并通过pc相对寻址进行访问:
+------------------------------------------+
teste+0 | |
| I n s t r u c t i o n s |
| |
+------------------------------------------+ +------------
teste+14 | uint8_t** >--------------------------> a | uint8_t * ...
+------------------------------------------+ +------------
teste+18 | uint8_t* >------------------------+
+------------------------------------------+ | +---------+
+-> b[0] | uint8_t |
+---------+
b[1] | uint8_t |
+---------+
b[2] | uint8_t |
+---------+
| ... |
通过将这些恒定地址存储在指令的固定(小)偏移量处,代码可以使用16位THUMB操作码将它们加载到寄存器中。相反,MIPS代码通常使用2 32位指令序列来完成相同的任务,方法是将嵌入指令中的16位直接指令加载到目标寄存器的上半部分和下半部分
现在让我们一步一步地看一下说明
0: 4a04 ldr r2, [pc, #16] ; (14 <teste+0x14>)
此行正在将b[0]
的地址(存储在子例程之后的代码中)加载到r3
4: 6811 ldr r1, [r2, #0]
此行正在将存储在a
中的指针加载到r1
中。因此,r1
现在指向一些uint8\t
(图中右侧浮动的一个)
此行将一个字节从地址r3+1
(又称b[1]
)加载到r0
8: 7008 strb r0, [r1, #0]
本街