C语言嵌入式系统编程培训-内存操作原创文章21cnbao2005-10

 2024-03-20 03:04:27  阅读 0

如何练习C语言嵌入式系统编程——内存操作

作者:宋宝华-邮箱:[email][/email]

1. 数据指针

在嵌入式系统的编程中,经常需要对特定的内存单元中的内容进行读写,而汇编中有相应的MOV指令。 然而除了C/C++之外的其他编程语言基本上不具备直接访问绝对地址的能力。 在嵌入式系统的实际调试中,经常使用C语言指针来读写绝对地址单元的内容。 使用指针直接操作内存通常发生在以下情况:

(1)I/O芯片位于CPU的存储空间而不是I/O空间,寄存器对应特定的地址;

(2) 两个CPU通过双口RAM进行通信。 CPU需要向双口RAM的特定单元(称为邮箱)写入内容,以在另一个CPU中产生中断;

(3)读取ROM或FLASH特定单元中烧录的汉字和英文字库。

例如:

字符 *p = ( 字符 *);

*p=11;

上面程序的意思是在绝对地址+处写入11(80186使用16位段地址和16位偏移地址)。

使用绝对地址指针时,请注意指针递增和递减操作的结果取决于指针所指向的数据类型。 上面的例子中,p++后的结果是p=,如果p指向int,即:

int *p = (int *);

p++( 或 ++p) 的结果相当于:p = p+(int),p—( 或 —p) 的结果是 p = p-(int)。

同样,如果执行:

长整型 *p = (长整型 *);

那么p++(或++p)的结果相当于:p = p+(long int),p—(或-p)的结果是p = p-(long int)。

请记住:CPU 地址以字节为单位,C 语言指针根据它们指向的数据类型的长度递增和递减。 理解这一点对于使用指针直接操作内存非常重要。

2. 函数指针

首先,您需要了解以下三个问题:

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

(2)调用函数实际上相当于“传送指令+参数传送处理+返回位置到堆栈”。 本质上,核心操作就是将函数生成的目标代码首地址赋值给CPU的PC寄存器;

(3)因为函数调用的本质就是跳转到某个地址单元的代码去执行,所以你可以“调用”一个根本不存在的函数实体,晕? 请阅读以下内容:

请拿出你能找到的任何一本大学教科书《微型计算机原理》。 书上说,186 CPU启动后,跳转到绝对地址(对应C语言的指针,是段地址,是段内的偏移量)。 ,请看下面的代码:

(*) ( );/* 定义无参数、无返回类型 */

/* 函数指针类型 */

= ();/* 定义一个指向的函数指针 */

/* CPU启动后执行的第一条指令的位置*/

();/* 调用函数*/

在上面的程序中,我们根本没有看到任何函数实体,但是我们执行了这样一个函数调用:(),它实际上起到了“软重启”的作用,在CPU启动后跳转到第一行。 要执行的指令的位置。

请记住:函数只有一组指令; 您可以在没有函数体的情况下调用函数,这实际上只是更改地址并开始执行指令!

3. 数组与动态应用程序

嵌入式系统中的动态存储器应用比一般系统编程有更严格的要求。 这是因为嵌入式系统的内存空间往往非常有限,不经意的内存泄漏很快就会导致系统崩溃。

因此,请确保您的和免费的成对出现。 如果你写一个这样的程序:

字符*(空)

字符*p;

p = (char *)(…);

如果(p==NULL)

……;

…/* 对 p 的一系列操作*/

p;

在某处调用(),用完动态分配的内存后将其释放,如下:

字符 *q = ();

……

自由(q);

上面的代码显然是不合理的,因为它违反了与free成对出现的原则,即“谁申请谁就被释放”的原则。 不满足这个原则会导致代码的耦合性增加,因为用户在调用函数时需要知道其内部细节!

正确的做法是在调用处申请内存并传入函数,如下:

字符 *p=(…);

如果(p==NULL)

……;

(p);

……

免费(p);

p=空;

该函数接收参数p,如下:

无效(字符*p)

…/* 对 p 的一系列操作*/

基本上可以用更大的数组来代替动态内存分配方式。 对于编程新手来说,作者建议大家尽量使用数组! 嵌入式系统可以以宽广的胸怀接受缺陷,但不能“接受”错误。 毕竟,以最愚蠢的方式修炼神功的郭靖,比足智多谋却因政治错误而走上反革命道路的杨康要强。

给出原理:

(1)尽可能使用数组,数组不能越界访问(真理越界就是谬论,数组越界光荣地完成了一个混沌的嵌入式系统);

(2)如果使用动态申请,申请后一定要判断是否申请成功,而且要和free成对出现!

4.关键字常量

const 的意思是“只读”。 区分下面代码的功能非常重要,这也是常见的感叹。 如果你不知道其中的区别,并且已经在编程世界摸爬滚打了很多年,那么只能说是一场悲剧:

常量整型;

int 常量 a;

常量 int *a;

int * 常量 a;

int const * 一个常量;

(1)关键字const的作用是向阅读你代码的人传达非常有用的信息。 例如,在函数的形参前添加const关键字,表示该参数在函数体中不会被修改,是“输入参数”。 当有多个形式参数时,函数的调用者可以通过参数前是否有const关键字来清楚地识别哪些是输入参数,哪些是可能的输出参数。

(2)合理使用关键字const可以让编译器自然地保护那些不希望被改变的参数,防止它们被无意的代码修改,这样可以减少bug的发生。

const在C++语言中有着更丰富的含义,但是在C语言中它的意思只是:“一个只能读的普通变量”,可以称之为“不可改变的变量”(这句话看似拗口,其实是C语言中const本质最准确的表达),编译阶段需要的常量仍然只能用#宏来定义! 因此,下面的C语言程序是非法的:

常量 int 大小 = 10;

char a[SIZE];/* 非法:编译时不能使用变量*/

5.关键词

C语言编译器会对用户编写的代码进行优化,比如下面的代码:

整数a、b、c;

a = (0x100);/* 读取I/O空间中端口0x100的内容并存储到变量中*/

b = a;

a = (0x100);/* 再次读取I/O空间0x100端口的内容,存入a变量中*/

c = a;

它可能被编译器优化为:

整数a、b、c;

a = (0x100);/* 读取I/O空间中端口0x100的内容并存储到变量中*/

b = a;

c = a;

然而,这样的优化结果可能会导致错误。 如果在第一次读操作后,I/O空间中端口0x100的内容被另一个程序写入了新的值,那么第二次读操作读出的内容实际上与第一次不同。 ,b和c的值应该不同。 在变量 a 的定义之前添加关键字可以防止编译器进行类似的优化。 正确的做法是:

整数a;

变量可以在以下情况下使用:

(1)并行设备的硬件寄存器(比如状态寄存器,例子中的代码就属于此类);

(2) 将在中断服务程序中访问的非自动变量(即全局变量);

(3) 多线程应用程序中多个任务共享的变量。

6.处理CPU字长和内存位宽不一致的问题

正如背景部分提到的,本文特意选择了字长与CPU字长不一致的存储芯片,只是为了进行本节的讨论,解决CPU字长与内存位宽不一致的问题。 80186的字长为16,NVRAM的位宽为8。此时,我们需要提供一个对NVRAM读写字节和字的接口,如下:

字符字节;

;

/* 功能:读取NVRAM中的字节

* 参数: ,读取位置相对于NVRAM基地址的偏移

* 返回:读取到的字节值

*/

字节(字)

= (字节*)(NVRAM + * 2); /* 为什么偏移量是×2? */

*;

/* 功能:读取NVRAM中文字幕

* 参数: ,读取位置相对于NVRAM基地址的偏移

* 返回:读到的单词

*/

字(字)

字 wTmp = 0;

;

/* 读取高字节 */

= (字节*)(NVRAM + * 2); /* 为什么偏移量是×2? */

wTmp +=(*)*256;

/* 读取低字节 */

= (字节*)(NVRAM + (+1) * 2); /* 为什么偏移量是×2? */

wTmp +=*;

温度;

/* 功能:向NVRAM写入一个字节

* 参数: ,写入位置相对于NVRAM基地址的偏移

*,要写入的字节

*/

无效(字、字节)

……

/* 功能:向NVRAM写入一个字*/

* 参数: ,写入位置相对于NVRAM基地址的偏移

*wData,要写入的字

*/

无效(WORD,WORD wData)

……

子贡问:为什么偏移量要乘2?

子说:请看图1,16位80186和8位NVRAM之间的互连只能连接到地址线A1到A0。 CPU本身的A0不与NVRAM连接。 所以NVRAM的地址只能是偶地址,所以每次都是以2为单位前进!

图1 CPU与NVRAM地址线连接

子贡又问:那么为什么80186的地址线A0没有连接到NVRAM的A0呢?

孔子说:请读一下《论语》中的《微机原理》,里面描述了圣人的计算机构成之道。

总结

本文主要讲述嵌入式系统C语言编程中内存操作的相关技巧。 掌握并深入理解数据指针、函数指针、动态分配内存、const和关键字等相关知识是一个优秀的C语言程序员的基本要求。 当我们牢牢掌握了以上技能后,我们就已经学会了C语言的99%,因为C语言最本质的内涵就体现在内存操作上。

我们在嵌入式系统中之所以使用C语言进行编程,99%的原因是因为它强大的内存操作能力!

如果你热爱编程,请热爱C语言;

如果你热爱C语言,请热爱指针;

如果你喜欢指针,请喜欢指针!

标签: 指针 函数 操作

如本站内容信息有侵犯到您的权益请联系我们删除,谢谢!!


Copyright © 2020 All Rights Reserved 京ICP5741267-1号 统计代码