![嵌入式实时操作系统:基于ARM Mbed OS的应用实践](https://wfqqreader-1252317822.image.myqcloud.com/cover/578/47379578/b_47379578.jpg)
2.2 C语言中的构造类型及编译相关问题
在实时操作系统内核代码中,大量使用C语言中的构造类型、宏定义、条件编译等,简要概述这些知识,有助于内核代码分析。
2.2.1 C语言中的构造类型
C语言提供了许多种基本的数据类型(如int、float、double、char等)供用户使用,但是由于程序需要处理的问题往往比较复杂,而且多样化,已有的数据类型显然不能满足使用要求。因此,C语言允许用户根据需要自己声明一些类型,用户声明的类型有结构体类型(Structure)、共用体类型(Union)、枚举类型(Enumeration)等,这些类型将不同类型的数据组成一个有机整体,这些数据之间是相互联系的。用户声明的类型也称为构造类型。本书涉及的构造类型主要为结构体类型和枚举类型两种,下面对这两种类型进行介绍。
1.结构体类型
1)结构体的基本概念
C语言允许用户将一些不同类型(当然也可以是相同类型)的元素组合在一起定义成一个新的类型,这种新类型就是结构体。其中的元素称为结构体的成员或者域,且这些成员可以为不同的类型,成员一般用名字访问。结构体可以被声明为变量、指针或数组等,用以实现较复杂的数据结构。
声明一个结构体类型的一般形式如下:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_3.jpg?sign=1739350685-Jd3uYwrSK7NMrQpwk36bFyLoD9sikNLJ-0-35fecef0ba4f8871a445b4ded53b4830)
例如,可以通过下面的声明来建立结构体类型:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_4.jpg?sign=1739350685-RmkKnWfsQSgsYsXmdFl9vPW8Iz06QvCv-0-e80a7c24fb4919f6c7ce73aa19e821d9)
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_5.jpg?sign=1739350685-WPY5uXMDwnHCyqLbHRGoKaJN0KfqowAm-0-9023397604b1e2ff81908cdd9b070a6c)
结构体类型名用作结构体类型的标志,上面声明中的Date就是结构体类型名,大括号内是该结构体中的全部成员,由它们组成一个特定的结构体。上例中的year、month、day等都是结构体中的成员,结构体类型的大小是其成员大小之和。在声明一个结构体类型时必须对各成员都进行类型声明,每一个成员也称为结构体中的一个域。结构体的成员类型可以是另一个结构体类型,也就是说结构体可以嵌套定义。例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_6.jpg?sign=1739350685-xkNSikW1ezF2Ri8XIk8oB4B5sKKYRmli-0-da148c85ce42a06f3af8a10da026c87e)
这样就声明了一个新的结构体类型Student,它向编译系统声明:这是一种结构体类型,包括num、name、sex、age、score、birthday和addr等不同类型的数据项。应当说明,Student是一个类型名,它和系统提供的标准类型(如int、char、float、double)一样,都可以用来定义变量,只不过结构体类型需要事先由用户自己声明而已。实际使用中,根据需要还可以通过typedef关键字将已定义的结构体类型命名为其他各种别名。
2)结构体变量的引用
结构体变量成员引用格式为:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_7.jpg?sign=1739350685-mDLQG6iPQRIUcPg95d9ZtAvEsrc2HQDR-0-1bff7b65480bd3a8b563c3c9bd83da90)
例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_8.jpg?sign=1739350685-EFLDsfFJTTI9AsIpXs68H12TwmuOrufN-0-293690d253f66c968dee6a4488da2871)
“.”是成员运算符,它在所有运算符中优先级最高,因此可以把stu1.num和stu1.age当作一个整体来看待,相当于一个变量。如果成员本身又属于一个结构体类型,那么要用若干个“.”运算符,一级一级找到最低一级的成员,只能对最低级的成员进行赋值或存取及运算。例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_9.jpg?sign=1739350685-clMNHtbqhHyYciVhm0WbfDyUWtQhFPRs-0-08e0c6c9bd9d440986c69d30aa008bb1)
结构体变量成员和结构体变量本身都具有地址,且都可以被引用。例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_10.jpg?sign=1739350685-JQLP2bGMz5fJqlG7YOmpSVQ5rvpofFYW-0-84c3822b70eb4f6c43c97ad53e3916c8)
注意:结构体变量的地址主要用作函数参数,传递结构体变量的地址。
3)结构体指针
结构体指针是指存储一个结构体变量起始地址的指针变量。一旦一个结构体指针变量指向了某个结构体变量,就可以通过结构体指针对该结构体变量进行操作。例如,上例中结构体变量stu1,也可以通过指针变量来进行操作:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_11.jpg?sign=1739350685-ayNTfcAMpvnjfgxmQ2E8EnCehxFjvqFV-0-cbe1c1acfce43dbf10a34c8ec7be67f5)
代码中定义了一个struct Student类型的指针变量p,并将变量stu1的首地址赋值给指针变量p,然后通过指针操作符“->”引用其成员进行赋值。(*p)表示p指向的结构体变量,因此(*p).age也就等价于stu1.age。在本书中,可以看到结构体指针是构建链式存储结构的基础。
4)应用举例
在操作系统中,使用了大量的结构体来存储和描述相关信息。例如,线程控制块是描述线程的基本信息的数据结构,是Mbed OS进行线程调度的基础,其结构体类型声明如下:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_12.jpg?sign=1739350685-u1AecO7Q26176a8bY1JlVj1AhUmGqSFo-0-d061e91750300159f5f896c262da27e0)
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_13.jpg?sign=1739350685-zxmhILRyNPHaaT4cCcz61Uy8tCLNFeUP-0-1cd0c5a36e399aea0e6119bd4372a034)
可以看到,线程控制块结构体osRtxThread_s成员较多,包括整数类型成员、字符类型成员、osRtxThread_s结构体指针类型成员、osRtxMutex_s结构体指针类型成员、void指针类型成员,并且通过typedef关键字定义了该类型的一个别名osRtxThread_t。
2.枚举类型
1)枚举类型基本概念
枚举类型是C语言另一种构造数据类型,它用于声明一组命名的常数,当一个变量有几种可能的取值时,可以将它定义为枚举类型。所谓“枚举”是指将变量的可能值一一列举出来,这些值也称为枚举元素或枚举常量。变量的值只限于列举出来的值的范围,有效地防止用户提供无效值,该变量可使代码更加清晰,因为它可以描述特定的值。
枚举的声明基本格式如下。
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_14.jpg?sign=1739350685-qnIwZU3szN1Mif8EAEXVgvOBCxd4gGJs-0-99677f7ff5ea5f4f59c74a9bcef459c0)
例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_15.jpg?sign=1739350685-Si3ETW3XrhU0Ou8ZX7GYWZDWBsbEZzJF-0-59665d03d3e44247417b7e20e3708f48)
在C语言程序的编译过程中,枚举元素是作为常量来处理的,它们不是变量,因此不能对它们进行直接赋值,但可以通过强制类型转换来赋值。枚举元素的值按定义的顺序从0开始,如red为0,green为1,blue为2,yellow为3,white为4。枚举元素可以用作判断比较,比较是按其在定义时的顺序号进行比较的。
2)应用举例
本书在描述操作系统内核状态时,将内核状态值定义为枚举类型。例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_16.jpg?sign=1739350685-WOoTSwd6j5cptofQ6m7i6ZH5visfgUjp-0-edd8779051aaad44851d889956dbf92a)
不同的值表示不同状态,枚举类型的成员名清晰地描述了系统内核的当前状态。在后面的章节中,还可以看到其他操作系统一些常用的枚举类型,包括线程状态、线程优先级、系统状态等。
2.2.2 编译相关问题
C语言提供编译预处理的功能,允许在程序中使用几种特殊的命令(它们不是一般的C语句),在C语言编译系统对程序进行常规编译(包括语法分析、代码生成、优化等)之前,先对程序中的这些特殊命令进行预处理,然后将预处理的结果和源程序一起进行常规的编译处理,以得到目标代码。C语言提供的预处理功能主要有宏定义、条件编译和文件包含。
1.宏定义
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_17.jpg?sign=1739350685-MOLRElNibm4q5PzNsN7p866CHaaZfE1g-0-cb863d3ac2eebe8e45e8906d66a9de37)
表达式既可以是数字、字符,也可以是若干条语句。在编译时,所有引用该宏的地方,都将自动被替换成宏所代表的表达式。例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_18.jpg?sign=1739350685-jkBiv9TcxAOHJAjmPoS7qji7WpfkKvWY-0-76fc07fad8b45bded151c6c0acb06661)
2.撤销宏定义
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_19.jpg?sign=1739350685-TzEFw7tNg0GczjYZXahr4E24oRz2CtZp-0-bf943f08bf3bea98c0651d2dafd8dc4f)
3.条件编译
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_20.jpg?sign=1739350685-xWYgxpmRXWpwK3kV4Jt2kOYsJQ199Qwz-0-763a26ce41f0ce4ee53d403a187cf997)
若表达式成立,则编译#if下的程序,否则编译#else下的程序。#endif为条件编译的结束标志。
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_21.jpg?sign=1739350685-F2hTYkG9pdVgBTDcwBrPIeKBxJ2hw1HL-0-0c83f4156d9b1784fcc018f2e2395f6e)
条件编译通常用来调试、保留程序(但不编译),或者在需要对两种状况做不同处理时使用。
4.文件包含处理
所谓文件包含是指一个源文件将另一个源文件的全部内容包含进来,其一般形式如下:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_22.jpg?sign=1739350685-pZNFGQwmfUONVFHMBkgANhEoD4ofMifF-0-a7b2fed784313d62a85a239b10c97d2d)