【发布时间】:2020-05-12 20:48:19
【问题描述】:
我尝试在 ARM Cortex-M0 处理器的 GNU GCC C 编译器中声明一个指向字符串的短指针数组(16 位而不是默认的 32 位),以减少闪存消耗。我有两种语言的大约 200 个字符串,因此将指针的大小从 32 位减少到 16 位可以节省 800 字节的闪存。这应该是可能的,因为闪存大小小于 64 kB,所以指向闪存的指针的高位字(16 位)是常量,等于 0x0800:
const unsigned char str1[] ="First string";
const unsigned char str2[] ="Second string";
const unsigned short ptrs[] = {&str1, &str2}; //this line generate error
但我在第 3 行出现错误
“错误:初始化元素在加载时不可计算”
然后我尝试了:
const unsigned short ptr1 = (&str1 & 0xFFFF);
我得到了: “错误:二进制 & 的操作数无效(有 'const unsigned char (*)[11]' 和 'int')”
经过多次尝试,我最终完成了组装:
.section .rodata.strings
.align 2
ptr0:
ptr3: .short (str3-str0)
ptr4: .short (str4-str0)
str0:
str3: .asciz "3-th string"
str4: .asciz "4-th string"
编译顺利通过,但现在我在尝试引用指针时遇到问题:来自 C 代码的 ptr4 和 ptr0。尝试将“ptr4-ptr0”作为 8 位参数传递给 C 函数:
ptr = getStringFromTable (ptr4-ptr0)
声明为:
const unsigned char* getStringFromTable (unsigned char stringIndex)
我得到了这样的错误代码:
ldr r3, [pc, #28] ; (0x8000a78 <main+164>)
ldrb r1, [r3, #0]
ldr r3, [pc, #28] ; (0x8000a7c <main+168>)
ldrb r3, [r3, #0]
subs r1, r1, r3
uxtb r1, r1
bl 0x8000692 <getStringFromTable>
而不是这样的:
movs r0, #2
bl 0x8000692 <getStringFromTable>
如有任何建议,我将不胜感激。
.....几天后.....
按照@TonyK 和@old_timer 的建议,我终于通过以下方式解决了这个问题: 在汇编中我写道:
.global str0, ptr0
.section .rodata.strings
.align 2
ptr0: .short (str3-str0)
.short (str4-str0)
str0:
str3: .asciz "3-th string"
str4: .asciz "4-th string"
然后我在 C 中声明:
extern unsigned short ptr0[];
extern const unsigned char str0[] ;
enum ptrs {ptr3, ptr4}; //automatically: ptr3=0, ptr4=1
const unsigned char* getStringFromTable (enum ptrs index)
{
return &str0[ptr0[index]] ;
}
现在是这段文字:
ptr = getStringFromTable (ptr4)
被编译成正确的代码:
08000988: 0x00000120 movs r0, #1
0800098a: 0xfff745ff bl 0x8000818 <getStringFromTable>
我只需要记住保持enum ptrs 的顺序,每次我将向程序集添加一个字符串并将一个新项目添加到enum ptrs
【问题讨论】:
-
arm 地址是 32 位的,什么是 16 位指针,您将如何使用它? (好吧,有些是 24 岁)
-
如果你想要一个偏移量,然后你将在以后制作一个完整的地址,然后 const unsigned short ptr1 = (&str1)&0xFFFF;然后用一些base+ptr1的味道指向它。给这只猫换皮的各种方法,如果这都是 const 的东西,那么从高级语言的角度来看,所有的字符串都可以放在一个数组中,然后你只需要偏移量来访问不是完整地址而不是物理地址。即使不是 const 你也可以用二维数组做到这一点,只需要一个偏移量,不需要特殊的指针数学(也不需要链接时的物理地址)
-
可以创建一个临时工具,以某种形式预先准备数据,这样实际的二进制文件就不会携带太多的包袱,主程序也不必获取工具链来尝试减少编译时的使用时间。
-
您是否检查过您的地址实际上适合为 16 位?微控制器通常没有 MMU,而是固定的内存映射。您应该查阅您的微控制器数据表/架构手册,以验证简单的模数减少到 16 位对于地址是可逆的。如果不是,最好声明一个大的全局数组并在其中使用 16 位索引,如果您真的非常渴望更小的指针。
-
ptr3: .short (str3-str0) 这是一个相对偏移量而不是地址,所以不是指针不能用于直接处理任何有用的东西,需要一个(32 位)基地址才有用。 .base+ptr3 可以是地址,但 ptr3 不能。
标签: string pointers gcc arm 16-bit