
2.2 C语言中的构造类型及编译相关问题
在实时操作系统内核代码中,大量使用C语言中的构造类型、宏定义、条件编译等,简要概述这些知识,有助于内核代码分析。
2.2.1 C语言中的构造类型
C语言提供了许多种基本的数据类型(如int、float、double、char等)供用户使用,但是由于程序需要处理的问题往往比较复杂,而且多样化,已有的数据类型显然不能满足使用要求。因此,C语言允许用户根据需要自己声明一些类型,用户声明的类型有结构体类型(Structure)、共用体类型(Union)、枚举类型(Enumeration)等,这些类型将不同类型的数据组成一个有机整体,这些数据之间是相互联系的。用户声明的类型也称为构造类型。本书涉及的构造类型主要为结构体类型和枚举类型两种,下面对这两种类型进行介绍。
1.结构体类型
1)结构体的基本概念
C语言允许用户将一些不同类型(当然也可以是相同类型)的元素组合在一起定义成一个新的类型,这种新类型就是结构体。其中的元素称为结构体的成员或者域,且这些成员可以为不同的类型,成员一般用名字访问。结构体可以被声明为变量、指针或数组等,用以实现较复杂的数据结构。
声明一个结构体类型的一般形式如下:

例如,可以通过下面的声明来建立结构体类型:


结构体类型名用作结构体类型的标志,上面声明中的Date就是结构体类型名,大括号内是该结构体中的全部成员,由它们组成一个特定的结构体。上例中的year、month、day等都是结构体中的成员,结构体类型的大小是其成员大小之和。在声明一个结构体类型时必须对各成员都进行类型声明,每一个成员也称为结构体中的一个域。结构体的成员类型可以是另一个结构体类型,也就是说结构体可以嵌套定义。例如:

这样就声明了一个新的结构体类型Student,它向编译系统声明:这是一种结构体类型,包括num、name、sex、age、score、birthday和addr等不同类型的数据项。应当说明,Student是一个类型名,它和系统提供的标准类型(如int、char、float、double)一样,都可以用来定义变量,只不过结构体类型需要事先由用户自己声明而已。实际使用中,根据需要还可以通过typedef关键字将已定义的结构体类型命名为其他各种别名。
2)结构体变量的引用
结构体变量成员引用格式为:

例如:

“.”是成员运算符,它在所有运算符中优先级最高,因此可以把stu1.num和stu1.age当作一个整体来看待,相当于一个变量。如果成员本身又属于一个结构体类型,那么要用若干个“.”运算符,一级一级找到最低一级的成员,只能对最低级的成员进行赋值或存取及运算。例如:

结构体变量成员和结构体变量本身都具有地址,且都可以被引用。例如:

注意:结构体变量的地址主要用作函数参数,传递结构体变量的地址。
3)结构体指针
结构体指针是指存储一个结构体变量起始地址的指针变量。一旦一个结构体指针变量指向了某个结构体变量,就可以通过结构体指针对该结构体变量进行操作。例如,上例中结构体变量stu1,也可以通过指针变量来进行操作:

代码中定义了一个struct Student类型的指针变量p,并将变量stu1的首地址赋值给指针变量p,然后通过指针操作符“->”引用其成员进行赋值。(*p)表示p指向的结构体变量,因此(*p).age也就等价于stu1.age。在本书中,可以看到结构体指针是构建链式存储结构的基础。
4)应用举例
在操作系统中,使用了大量的结构体来存储和描述相关信息。例如,线程控制块是描述线程的基本信息的数据结构,是Mbed OS进行线程调度的基础,其结构体类型声明如下:


可以看到,线程控制块结构体osRtxThread_s成员较多,包括整数类型成员、字符类型成员、osRtxThread_s结构体指针类型成员、osRtxMutex_s结构体指针类型成员、void指针类型成员,并且通过typedef关键字定义了该类型的一个别名osRtxThread_t。
2.枚举类型
1)枚举类型基本概念
枚举类型是C语言另一种构造数据类型,它用于声明一组命名的常数,当一个变量有几种可能的取值时,可以将它定义为枚举类型。所谓“枚举”是指将变量的可能值一一列举出来,这些值也称为枚举元素或枚举常量。变量的值只限于列举出来的值的范围,有效地防止用户提供无效值,该变量可使代码更加清晰,因为它可以描述特定的值。
枚举的声明基本格式如下。

例如:

在C语言程序的编译过程中,枚举元素是作为常量来处理的,它们不是变量,因此不能对它们进行直接赋值,但可以通过强制类型转换来赋值。枚举元素的值按定义的顺序从0开始,如red为0,green为1,blue为2,yellow为3,white为4。枚举元素可以用作判断比较,比较是按其在定义时的顺序号进行比较的。
2)应用举例
本书在描述操作系统内核状态时,将内核状态值定义为枚举类型。例如:

不同的值表示不同状态,枚举类型的成员名清晰地描述了系统内核的当前状态。在后面的章节中,还可以看到其他操作系统一些常用的枚举类型,包括线程状态、线程优先级、系统状态等。
2.2.2 编译相关问题
C语言提供编译预处理的功能,允许在程序中使用几种特殊的命令(它们不是一般的C语句),在C语言编译系统对程序进行常规编译(包括语法分析、代码生成、优化等)之前,先对程序中的这些特殊命令进行预处理,然后将预处理的结果和源程序一起进行常规的编译处理,以得到目标代码。C语言提供的预处理功能主要有宏定义、条件编译和文件包含。
1.宏定义

表达式既可以是数字、字符,也可以是若干条语句。在编译时,所有引用该宏的地方,都将自动被替换成宏所代表的表达式。例如:

2.撤销宏定义

3.条件编译

若表达式成立,则编译#if下的程序,否则编译#else下的程序。#endif为条件编译的结束标志。

条件编译通常用来调试、保留程序(但不编译),或者在需要对两种状况做不同处理时使用。
4.文件包含处理
所谓文件包含是指一个源文件将另一个源文件的全部内容包含进来,其一般形式如下:
