第三章 TinyOS编程语言nesC
第一节 C与nesC的比较
在具体介绍nesC之前,先以C及C++作为参照物,讨论C(C++)与nesC之间的不同,这将有利于读者尤其是nesC的初学者更好地理解nesC的语法、用法。
nesC(Network Embedded Systems C)采用了特有的基于组件(Component)的编程模型,即通过连接组件来创建应用。实际上,可以将nesC程序看做若干组件的集合。组件是一个个单独的代码块,为输入和(/或)输出定义了明确的接口(interface)。接口是一组被称为命令(command)和(/或)事件(event)的函数集合,是组件的访问点。接口是双向的,是组件间相互联系的通道。组件实现的功能或函数必须在其声明的接口中说明,否则不能被其他组件使用。组件可以分为两种类型:模块(Module)和配件(Configuration)。模块负责定义状态以及可执行逻辑,配件通过接口将组件连接起来,形成一个新的抽象(也可以称为服务)或者一个完整的顶层应用。
本章按照从简单到复杂的顺序,依次介绍组件、接口、模块实现、配件实现、参数化接口与通用化组件等编程知识。
3.1 Android平台简介
nesC与C之间的本质区别在于程序的连接(linking)模型。C程序由变量、函数和类型等程序元素组成。这些程序元素在文件中定义,分别被编译并连接到一起构建成应用。nesC程序由组件组成,这些组件通过连接语句显式地组织起来。nesC编译器将这些组件编译并连接成为一个整体(即一个应用)。站在编程者的角度来看,nesC编程的困难与复杂性不在于如何写组件,而在于如何把组件组织成一个可以工作的应用。
本节试图从编程者的角度出发,先介绍C(C++)程序如何组织,再进一步讨论nesC程序如何组织及原因。
3.1.1 C与C++
C程序元素包括变量、类型和函数。为了叙述方便,这里大多时候将函数与变量统称为变量。在一些具体情况下,再明确指出是变量还是函数。
C程序命名变量有三种方式:声明(declaration),定义(definition)和引用(reference)。
声明变量是告知编译器变量的名称和类型。例如,声明函数是把函数名、函数参数的个数和参数类型等信息通知编译系统,以便在调用函数时,编译系统能够正确识别函数并检查调用是否合适。只要保持一致,可以多次声明变量。声明变量不是实现,仅表明变量存在,其他变量可以使用它。
引用变量包括调用函数、赋值及取地址等。通常情况下,C编译器要求声明变量之后才能引用它。使用未声明的变量是不良的C编程习惯。
定义变量是最后一种变量命名方式。声明变量表明变量是存在的,引用变量是使用变量,而定义变量实际上是创建变量。具体地说,定义函数是实现函数,定义变量是为变量分配存储区域。变量可以被声明多次,引用多次,但只能定义一次。
定义变量将变量名字引入到了程序的命名范围。C程序有多个命名范围,可以将其看成一个范围树,在最顶层的根是全局范围。任何代码都可以引用全局范围内的变量。全局变量和非静态(non-static)函数都在全局范围内。大括号{ }定义了子范围,该范围内的代码只能引用树中该范围以上范围内命名的变量。另外,还有一个变量范围级别是文件范围,由static关键字来声明,也就是说,带有关键字static标识的变量只能被同文件内的代码引用,不能在全局范围内被引用。
如果一个C程序由多个C文件构成,为了引用别的文件中的变量,需要使用声明变量的方式,将所要使用的变量名字引到全局范围。在下面的例3.1中,文件f2.c定义了函数g(),在文件f1.c中要使用这个函数,需要使用语句extern void g()先声明它。
例3.1:
f1.c包含的C代码如下:
extern void g( )
int main () {
g();g();
}
f2.c包含的代码如下:
void g() {
printf(“hello world!”);
}
在全局范围内组织变量要采用诸如头文件和命名约定等技巧。头文件也是一种C源文件。它将变量声明组织在一起,这些声明与相应实现文件或目标文件的定义相匹配。使用头文件可以避免大量重复输入可能带来的差错。命名约定可以在一定程度上避免使用不同的符号为相同的变量命名。例如,类型通常有_t的前缀,保证类型和函数不会有相同的名字。有些库在其变量前使用相同的前缀,如gkt_用在GKT+图形工具的库中。这些前缀提醒用户避免与其他库中的符号名字冲突。但是这样的做法使得编程显得繁冗。