String 如何在arm处理器的GCC C编译器中声明16位字符串指针

String 如何在arm处理器的GCC C编译器中声明16位字符串指针,string,pointers,gcc,arm,16-bit,String,Pointers,Gcc,Arm,16 Bit,我试图在ARM Cortex-M0处理器的GNU GCC编译器中声明一个指向字符串的短指针数组(16位而不是默认的32位),以减少闪存消耗。我有大约200个两种语言的字符串,因此将指针大小从32位减少到16位可以节省800字节的闪存。这应该是可能的,因为闪存大小小于64 kB,所以指向闪存的指针的高位字(16位)为常量,等于0x0800: const unsigned char str1[] ="First string"; const unsigned char str2[] ="Second

我试图在ARM Cortex-M0处理器的GNU GCC编译器中声明一个指向字符串的短指针数组(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
但我在第三行出错了

“错误:初始值设定项元素在加载时不可计算”

然后我试着:

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>
然后我在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>
08000988:0x00000120 movs r0,#1
080098A:0xfff745ff bl 0x8000818

我只需要记住每次都要保持
enum ptrs
的顺序,我将在汇编语言文件中向程序集添加一个字符串,并向
enum ptrs
添加一个新项,将
ptr0
str0
声明为
.global
。然后在
C
中:

extern unsigned short ptr0[] ;
extern const char str0[] ;
const char* getStringFromTable (unsigned char index)
  {
  return &str0[ptr0[index]] ;
  }

只要str0表的总大小小于64K,这种方法就有效。

指针是地址,arm中的地址不能是16位,而不是基于Acorn的arms(如果我没记错的话,是24位),地址是最小32位(对于arm),并且进入aarch64时,地址越大越好

这个

不生成地址(因此它不能是指针),它生成的偏移量仅在将其添加到基址str0时可用

您无法生成16位地址(在已调试/可用的arm编译器中),但由于这里的所有内容都是静态的(const/rodata),这使得它更容易求解,因此也可以在运行时求解,但根据迄今为止提供的信息进行更简单的预计算

const unsigned char str1[] ="First string";
const unsigned char str2[] ="Second string";
const unsigned char str3[] ="Third string";
BruteForce需要30行代码来生成下面的头文件,如果您尝试压缩头文件,则要少得多,尽管临时程序不需要很漂亮

此输出故意较长,以演示解决方案(并能够直观地检查工具),但编译器不关心(因此最好使其较长且详细,以便于可读性/验证):

mystrings.h

const unsigned char strs[39]=
{
  0x46, //  0 F
  0x69, //  1 i
  0x72, //  2 r
  0x73, //  3 s
  0x74, //  4 t
  0x20, //  5  
  0x73, //  6 s
  0x74, //  7 t
  0x72, //  8 r
  0x69, //  9 i
  0x6E, // 10 n
  0x67, // 11 g
  0x00, // 12 
  0x53, // 13 S
  0x65, // 14 e
  0x63, // 15 c
  0x6F, // 16 o
  0x6E, // 17 n
  0x64, // 18 d
  0x20, // 19  
  0x73, // 20 s
  0x74, // 21 t
  0x72, // 22 r
  0x69, // 23 i
  0x6E, // 24 n
  0x00, // 25 
  0x54, // 26 T
  0x68, // 27 h
  0x69, // 28 i
  0x72, // 29 r
  0x64, // 30 d
  0x20, // 31  
  0x73, // 32 s
  0x74, // 33 t
  0x72, // 34 r
  0x69, // 35 i
  0x6E, // 36 n
  0x67, // 37 g
  0x00, // 38 
};
const unsigned short ptrs[3]=
{
  0x0000 //  0   0
  0x000D //  1  13
  0x001A //  2  26
};
然后,当您使用它时,编译器将处理所有的地址生成

&strs[ptrs[n]]
根据您编写工具的方式,您的工具甚至可以包含以下内容

#define FIRST_STRING 0
#define SECOND_STRING 1
以此类推,这样您的代码就可以用

strs[ptrs[SECOND_STRING]]
使程序更具可读性。所有这些都是从为您执行此偏移工作的特殊工具自动生成的

该工具的main()部分可能如下所示

add_string(FIRST_STRING,"First string");
add_string(SECOND_STRING,"Second string");
add_string(THIRD_STRING,"Third string");
使用该函数和更多代码转储结果

然后您只需包含生成的输出并使用

strs[ptrs[THIRD_STRING]] 
在实际应用程序中键入语法

为了继续沿着您开始的路径前进,如果您喜欢的话(看起来需要做更多的工作,但仍然可以很快编写代码)

然后您需要导出str0和ptr3、ptr4(根据需要,具体取决于您的汇编语言),然后将它们作为指向str0+ptr3的指针进行访问

extern unsigned int str0;
extern unsigned short ptr3;
...
... *((unsigned char *)(str0+ptr3))
修复我有意或无意添加到伪代码中的任何语法错误

这也会起作用,你会有一个基址,然后是数百个16位的偏移量

甚至可以做一些

const unsigned short ptrs[]={ptr0,ptr1,ptr2,ptr3};
...
(unsigned char *)(str0+ptrs[n])
使用某种风格的C语法创建该数组,但可能不值得付出额外的努力

到目前为止,我们中的一些人提到的解决方案(上面演示的一个示例)(16位偏移量不是地址,这意味着不是指针)更易于编码、维护和使用,并且可能会根据您的实现进行读取。然而,实现它需要一个完整大小的基址和偏移量。在没有特别工具的情况下,用C编写代码是可能的,但是特别工具实际上只需要几分钟的编写时间

我几乎每天都编写程序来编写程序或压缩/操作数据,为什么不呢。压缩就是一个很好的例子。想在资源有限的mcu闪存中嵌入黑白图像吗?不要把所有的像素都放在二进制中,从运行长度编码开始,这意味着你是否编写了一个第三方工具,将真实数据转换成适合的结构,同样的,这里的第三方工具为应用程序准备/压缩数据。这个问题实际上只是另一个压缩算法,因为您试图在不丢失任何数据的情况下减少数据量

另请注意,根据这些字符串是什么,如果可能有重复或分数,该工具可能更智能:

const unsigned char str1[] ="First string";
const unsigned char str2[] ="Second string";
const unsigned char str3[] ="Third string";
const unsigned char str4[] ="string";
const unsigned char str5[] ="Third string";
创造

const unsigned char strs[39]=
{
  0x46, //  0 F
  0x69, //  1 i
  0x72, //  2 r
  0x73, //  3 s
  0x74, //  4 t
  0x20, //  5  
  0x73, //  6 s
  0x74, //  7 t
  0x72, //  8 r
  0x69, //  9 i
  0x6E, // 10 n
  0x67, // 11 g
  0x00, // 12 
  0x53, // 13 S
  0x65, // 14 e
  0x63, // 15 c
  0x6F, // 16 o
  0x6E, // 17 n
  0x64, // 18 d
  0x20, // 19  
  0x73, // 20 s
  0x74, // 21 t
  0x72, // 22 r
  0x69, // 23 i
  0x6E, // 24 n
  0x00, // 25 
  0x54, // 26 T
  0x68, // 27 h
  0x69, // 28 i
  0x72, // 29 r
  0x64, // 30 d
  0x20, // 31  
  0x73, // 32 s
  0x74, // 33 t
  0x72, // 34 r
  0x69, // 35 i
  0x6E, // 36 n
  0x67, // 37 g
  0x00, // 38 
};
const unsigned short ptrs[5]=
{
  0x0000 //  0   0
  0x000D //  1  13
  0x001A //  2  26
  0x0006 //  3   6
  0x001A //  4  26
};

arm地址是32位,什么是16位指针,您将如何使用它?(好的,有些是24)如果你想要一个偏移量,那么你将从后面的const unsigned short ptr1=(&str1)&0xFFFF;然后用碱+ptr1的味道来点它。剥皮这只猫的各种方法,如果这都是常量,那么从高级语言的角度来看,所有字符串都可以放在一个数组中,然后只需要偏移量来访问非完整地址而非物理地址。即使不是常量,你也可以使用二维数组,只需要一个偏移量。没有特殊的指针数学(或链接时的物理地址)可以创建一个临时工具来预先准备某种形式的数据,这样实际的二进制文件就不会有那么多负担,主程序也不必让工具链来尝试
const unsigned short ptrs[]={ptr0,ptr1,ptr2,ptr3};
...
(unsigned char *)(str0+ptrs[n])
const unsigned char str1[] ="First string";
const unsigned char str2[] ="Second string";
const unsigned char str3[] ="Third string";
const unsigned char str4[] ="string";
const unsigned char str5[] ="Third string";
const unsigned char strs[39]=
{
  0x46, //  0 F
  0x69, //  1 i
  0x72, //  2 r
  0x73, //  3 s
  0x74, //  4 t
  0x20, //  5  
  0x73, //  6 s
  0x74, //  7 t
  0x72, //  8 r
  0x69, //  9 i
  0x6E, // 10 n
  0x67, // 11 g
  0x00, // 12 
  0x53, // 13 S
  0x65, // 14 e
  0x63, // 15 c
  0x6F, // 16 o
  0x6E, // 17 n
  0x64, // 18 d
  0x20, // 19  
  0x73, // 20 s
  0x74, // 21 t
  0x72, // 22 r
  0x69, // 23 i
  0x6E, // 24 n
  0x00, // 25 
  0x54, // 26 T
  0x68, // 27 h
  0x69, // 28 i
  0x72, // 29 r
  0x64, // 30 d
  0x20, // 31  
  0x73, // 32 s
  0x74, // 33 t
  0x72, // 34 r
  0x69, // 35 i
  0x6E, // 36 n
  0x67, // 37 g
  0x00, // 38 
};
const unsigned short ptrs[5]=
{
  0x0000 //  0   0
  0x000D //  1  13
  0x001A //  2  26
  0x0006 //  3   6
  0x001A //  4  26
};