用C语言“指针”实现平台和应用程序的独立编译

2012-09-21 10:43王国斌
单片机与嵌入式系统应用 2012年6期
关键词:李四指针C语言

王国斌

(福州大观电子科技有限公司 研发部,福州 350003)

引 言

随着嵌入式产品应用环境和功能的日趋复杂,应用工程师都希望能够把更多的精力放在应用层次上应用功能的开发和稳定,而花更少的精力来关注底层驱动软件的实现和细节。基于此目的,一个比较好的实现方法就是在开发当中能够把已经稳定和充分测试的底层驱动软件的平台框架放在一个工程中编译当做“固化”,而把应用程序的功能实现部分放在另外一个工程中编译,从而减少驱动软件和应用软件的耦合度。这就有点像在编写PC软件时,我们编写一定的应用功能,而底层的驱动等则通过调用DLL的方式去实现。

1 预备知识

1.1 指针和函数指针

在了解怎样实现平台程序和应用程序的分开编译之前,先来学习一个重要的知识——指针和函数指针。

1.1.1 指 针

C语言的指针概念是最令C语言初学者头疼的问题,甚至一些有工作经验的程序员在平时的工作当中也是害怕它和规避它的。然而,如果没有指针,那C语言的效率和编程的乐趣将大打折扣。正由于此,C语言的指针问题也是很多公司面试时技术面试官必提的一个问题。指针,实际上应该叫做“地址”比较合理,这个地址表明了你所需要的数据或者程序段在整个地址空间的位置。通过这个地址你可以找到你所需要的东西的位置。它就像我们实际生活中的门牌号,比如说我们的实验楼在C区18号,那么只要找到了C区18号也就找到了我们的实验楼。

指针就类似于一个地址编码,那这个编码当然也是有规定长度的,或者说是有大小的。就像我们给仓库的储存格子来编码,那么这个编码的长度和内容就决定了最多能描述多少个格子。当然这个指针的长度越长,那么用它来描述的地址信息量就越多,也就是具有更大的选址空间。因此在16位MCU中指针的大小往往就是只有16位,而在32位MCU中指针的大小就往往是32位。一般来说在同一个地址空间内指针的大小是固定的,而这就是为什么在同一个MCU(特别是冯·诺伊曼结构)上,无论指针指向什么类型的数据,它总是占用固定的字节数。

(1)指针的类型

还是拿我们实验楼地址为例子:C区18号它既可以代表是一系列实验室的集合,也可以代表一堆房子的集合,而它具体可以代表什么样的含义就看你使用的时候需要怎样的信息。同样在程序中的一个地址,我们可以看作是一个字节数组的集合的开始,也可以看作是一个自定义的结构体的开始,当然还可以看作是一段程序段的开始。这就是我们在编程的时候可以把不同数据类型的指针相互转换的原因。

(2)指向指针的指针

很多人对指针已经很头痛了,对指向指针的指针可能就更加不知南北了。其实指向指针的指针在我们生活中运用得很多,比如说你要通过张三要找王五,但是他不知道王五在哪里,但是他知道李四知道王五在哪里,而他自己又知道李四在哪里,这样你就可以通过张三这个指针指向李四,而通过李四这个指针来指向王五来找到王五,而张三就是一个指向王五的指针的指针。相应的程序如下:

1.1.2 函数指针

如果看明白了上面这些描述,那也就应该知道函数指针的含义了。函数指针就是一个指向一段程序的地址,它和数据指针在本质上是没有区别的。

通过函数指针调用函数和直接调用函数的区别就在于我们事先是否知道这个函数的地址。比如我要把一封信送给李四,但是我不知道李四住在哪里,然而我知道张三知道李四在哪里,这样我就先把信转交给张三,信里面的内容就是我要传递给李四的信息(就是我们函数调用的参数),然后张三把信转交给了李四,李四根据信里面的内容进行相应的处理,并给我一个答案,而这个时候他已经知道了我的地址,因此他就可以直接绕过张三把答案回复给我。利用函数指针调用函数的过程就和上面所举的例子一样,而它与直接调用函数的区别就在于多了张三这一道手续而已。

函数指针的举例描述:

①uint8(*)(uint8,uint8)

表示一个函数指针。这个函数有两个参数,参数类型均为uint8,函数的返回类型是uint8。

②void(*const)(uint8,uint8(*)(void))

表示一个常量函数指针。这个函数有两个参数,第一个参数的类型是uint8,第二个参数的类型是一个函数指针,而这个函数指针的参数没有,返回类型则是uint8,最外层的函数指针的返回类型则为void。

1.2 地址空间

前面讲述了指针,下面我们讲述指针的前提,也就是地址空间。在冯·诺依曼结构的CPU中只有一个地址空间,无论是程序还是数据都可以在这个空间内被找到,因此在编程的时候从严格意义上来讲就只存在一种类型的指针,那就是void*。而对于哈佛结构的CPU来说则要显得复杂一些,程序空间和数据空间是隔离的,因此就存在指向程序空间和数据空间的两种指针。51单片机这种哈佛结构的增强则要显得更加复杂,不但程序空间和数据空间不统一,而且数据空间还要分成几种类型。因此从C语言编程的复杂度来讲,冯·诺依曼结构的CPU要简单一些。

1.3 程 序

简单地说,程序就是一段二进制码0和1的有序组合。而这种有序组合构成了目标CPU的一条条机器指令,这一系列的机器指令的有机组合则构成了程序员为之骄傲的程序。当我们的程序被目标CPU开始执行之后,它就按照预先安排的步骤去完成各种各样的事情。而函数就是程序中的一个片断,它在程序中的某一个位置完成一件或者几件我们所需要完成的事情。

然而如果你调用的函数并不在你所编写的程序段中,而已经在目标CPU的地址空间的某个地方了,那该怎么办呢?这个时候我们就需要用函数指针来完成这件事情了,首先需要通过一种方式来获得这个函数指针,然后通过函数指针来调用你所需要的函数。

2 实现平台程序和应用程序的分开编译

程序的编译就是把C语言文件编译成机器码,能让目标CPU通过这种方式和你交流。而编译完的程序段放在目标CPU地址空间的什么位置就需要由链接来决定。目前,绝大多数的编译器都允许用户自定义链接文件,因此通过这种方式我们就能按照意愿来安排代码的位置了。如果某些代码已经很稳定了,就可以事先把它固定在目标CPU的地址空间的某些位置,从而减少后期的工作量。

当平台程序和应用程序分离之后它们之间的接口就只能通过函数指针的方式来实现了。这里应用程序需要把它的入口函数的地址放在固定的位置,而平台程序则需要把它的API函数的地址放在固定的位置。

比如我们把应用程序的函数入口地址放在0x10000这个位置,那么平台程序运行的时候就从0x10000地址取出一个指针(地址),然后调用这个指针所指向的程序段来运行应用程序。应用程序通过同样的方式能够调用平台程序的API函数来完成预先定义的功能。

结 语

本文主要是根据日常的工程项目应用的实际需求,整理出了一套可以在设计嵌入式系统软件时对平台程序和应用程序分开编译的一种方法,希望对相关技术人员有所帮助。

[1] 陈建辉.C语言指针探讨[J].莆田高等专科学校学报,2001,8(4).

[2] 赵森,李卓民.C程序设计[M].北京:冶金工业出版社,2005.

猜你喜欢
李四指针C语言
基于Visual Studio Code的C语言程序设计实践教学探索
你追我赶
基于C语言的计算机软件编程
熟人就是这样变成陌生人的
李四超前
为什么表的指针都按照顺时针方向转动
高职高专院校C语言程序设计教学改革探索
基于改进Hough变换和BP网络的指针仪表识别
论子函数在C语言数据格式输出中的应用
ARM Cortex—MO/MO+单片机的指针变量替换方法