嵌入式程序设计之玩转内存_嵌入式设计

 

数据指针

在嵌入式系统编程中,经常会对特定的内存单元进行读写操作。在汇编中有对应的MOV指令,而在C/C++以及其他高级语言中基本没有直接操作绝对地址的能力。而在嵌入式系统的实际开发调试过程中,大多情况会借助C语言指针多具备的对绝对地址单元内容的读写能力。
直接使用指针操作内存,主要分为以下几种情况:

  • 某I/O芯片被定位在CPU的存储空间而非I/O空间,而且寄存器对应的是特定的地址

  • 两个CPU之间以双端口RAM进行通信,CPU需要在双端口RAM的特定单元(mail box)书写内容以在对方CPU产生中断。

  • 读写ROM或FLASH的特定单元所烧录的汉字或英文字模

 

////< 在绝对地址0x01写入10
unsigned char *p = (unsigned char *)0x01;
*p = 10;

使用绝对地址时,需要注意指针自增自减的结果取决于指针指向的数据类型。

int *p = (int *)0x01;

p++的结果为:p = p + sizeof(int)

p--的结果为:p = p - sizeof(int)

cpu以字节为单位编址,而C语言指针以指向的数据类型长度作为自增自减

函数指针

  • C 语言中函数名直接对应于函数生成的指令代码在内存中的地址,因此函数名可以直接赋给指向函数的指针

  • 调用函数实际上等同于"调转指令+参数传递处理+回归位置入栈",本质上最核心的操作是将函数生成的目标代码的首地址赋给CPU 的PC 寄存器

  • 因为函数调用的本质是跳转到某一个地址单元的code 去执行,所以可以"调用"一个根本就不存在的函数实体

 

typedef void (*lpFunction) ( ); ////< 定义一个无参数、无返回类型的函数指针类型
////< 定义一个函数指针,指向CPU 启动后所执行第一条指令的位置
lpFunction lpReset = (lpFunction)0xF000FFF0;
lpReset();   ////< 函数调用

《微型计算机原理》中讲到,186 CPU 启动后跳转至绝对地址0xFFFF0,该地址对应到C语言中为上述例子中的0xF000FFF0,其中0xF000为段地址,0xFFF0为段内偏移。

在上述例子中,根本没有定义函数,但是却能够执行lpReset()函数的调用,它实际的作用为"软重启",使程序跳转到CPU启动后第一条要执行的执行位置。

动态申请内存

在嵌入式系统中动态内存申请存在比一般系统编程时更严格的要求,这是因为嵌入式系统的内存空间往往是十分有限的,不经意的内存泄露会很快导致系统的崩溃。

因此,maloc和free一般要保证成对出现。

char *Function(void) {
  char *p = NULL;
  p = (char *)malloc(sizeof(char));
  ...
  return p;
}

void main() {
  char *q = Function();
  ...
  free(q);
}

上面这个例子明显是不合理的,因为它违反了"谁申请,就由谁释放"的原则。若程序不满足这个原则的话,会导致代码的耦合度增大,使用者需要在调用的时候知道Function()函数中的具体实现。

正确做法应该如下:

void Function(char*p) {
  ...
}

void main() {
  char *q = malloc(sizeof(char));
  ...
  Function(q);
  ...
  free(q);
}

 

  •