![嵌入式实时操作系统:基于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=1739329254-B7LWXEWHLXz83CG5CVUsOuVLkGXEWFTu-0-076fc40e24bae29afaa4a34095d34e13)
例如,可以通过下面的声明来建立结构体类型:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_4.jpg?sign=1739329254-MB9oU6ovmI726XKDg3MjMHbs9YRpXnqB-0-548472b2eb6125b8a66ad6345af63351)
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_5.jpg?sign=1739329254-fPNxGCK4zc7nzMJ6Lz3hlyFOpGRInmlS-0-e1371fd17009ccaddd84aaaeadd5d4b2)
结构体类型名用作结构体类型的标志,上面声明中的Date就是结构体类型名,大括号内是该结构体中的全部成员,由它们组成一个特定的结构体。上例中的year、month、day等都是结构体中的成员,结构体类型的大小是其成员大小之和。在声明一个结构体类型时必须对各成员都进行类型声明,每一个成员也称为结构体中的一个域。结构体的成员类型可以是另一个结构体类型,也就是说结构体可以嵌套定义。例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_6.jpg?sign=1739329254-g5aTj9VyS3aBRpenyk6rVDZHkvzt3erH-0-46ecbc8ee264fc5e48371768b7c126ce)
这样就声明了一个新的结构体类型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=1739329254-gXiqSLCx77GUrdIpDbVlf7UR3NLaVJUU-0-1aa1e41cbba41dce5fadc55b356b2948)
例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_8.jpg?sign=1739329254-8r6a6Zi6U4NsMO61BIMTDpuVIjacTh3e-0-efab9bdf3d60baa95d587177fd2f9085)
“.”是成员运算符,它在所有运算符中优先级最高,因此可以把stu1.num和stu1.age当作一个整体来看待,相当于一个变量。如果成员本身又属于一个结构体类型,那么要用若干个“.”运算符,一级一级找到最低一级的成员,只能对最低级的成员进行赋值或存取及运算。例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_9.jpg?sign=1739329254-EPJNfrCEHYlca4NYiscDb5MYqcgZxPha-0-682f337d2870dbf6c7b1f233aad127f5)
结构体变量成员和结构体变量本身都具有地址,且都可以被引用。例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_10.jpg?sign=1739329254-jpIclfAj4FjMSNSNojlOg0KbGHLH28sP-0-3a2d55be7dde68e8fa1428885bb15050)
注意:结构体变量的地址主要用作函数参数,传递结构体变量的地址。
3)结构体指针
结构体指针是指存储一个结构体变量起始地址的指针变量。一旦一个结构体指针变量指向了某个结构体变量,就可以通过结构体指针对该结构体变量进行操作。例如,上例中结构体变量stu1,也可以通过指针变量来进行操作:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_11.jpg?sign=1739329254-HiG2miW64SzTHjgDAygZWAqJKEaiTG8T-0-ebbb729dd2d427dc927a52f3444c14ce)
代码中定义了一个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=1739329254-5xyrYAHdgzYHSrKstSGURLi3H7O04MtQ-0-888fb6981af4ecc095bcf27f1c73b0a5)
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_13.jpg?sign=1739329254-QgLRyp5OKbluJi4QooJIw0p1INFMlZ8j-0-fe65879dd99620dd41d5fde8d8569c88)
可以看到,线程控制块结构体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=1739329254-Uz5IigblfJgTlIyHpFBB15PHARD4dzPH-0-5adcea83ec49fb0fecaafd120340c421)
例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_15.jpg?sign=1739329254-ovYA9pN23Brst5LyNntrVQyCMMKFDJVH-0-6ea7169fd29b2d91072e5b99402cb9b1)
在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=1739329254-MGJ3ARpQHDPC4RK9t3GPAsaewh4deGvz-0-bb0af451a5d565036dbc29cf20c72aa9)
不同的值表示不同状态,枚举类型的成员名清晰地描述了系统内核的当前状态。在后面的章节中,还可以看到其他操作系统一些常用的枚举类型,包括线程状态、线程优先级、系统状态等。
2.2.2 编译相关问题
C语言提供编译预处理的功能,允许在程序中使用几种特殊的命令(它们不是一般的C语句),在C语言编译系统对程序进行常规编译(包括语法分析、代码生成、优化等)之前,先对程序中的这些特殊命令进行预处理,然后将预处理的结果和源程序一起进行常规的编译处理,以得到目标代码。C语言提供的预处理功能主要有宏定义、条件编译和文件包含。
1.宏定义
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_17.jpg?sign=1739329254-QSBRhn0BcCLoscOusil3lLOYW3KAgyZP-0-2f6852ddf3eac4993609919ef2761466)
表达式既可以是数字、字符,也可以是若干条语句。在编译时,所有引用该宏的地方,都将自动被替换成宏所代表的表达式。例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_18.jpg?sign=1739329254-GudWjqQ1YG7RnlXbWA05YGdJYlqtVoC5-0-6235f6cf97650c06abef5fbe6033c060)
2.撤销宏定义
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_19.jpg?sign=1739329254-pzf7ldIwCAh2fKsKpojvh4U0rI2M4mLy-0-f5d6162da389ff6f7ae69198059b592a)
3.条件编译
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_20.jpg?sign=1739329254-KFTs0bAaaoEKfy2LqykB2Y2lcxcTeSCK-0-6ca988174bb00ce32feabbba34b73fe2)
若表达式成立,则编译#if下的程序,否则编译#else下的程序。#endif为条件编译的结束标志。
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_21.jpg?sign=1739329254-hHHHVnqxiC25tcWPkLyxKw0trHLX8Yvz-0-f917a0bfbfc629deeef04be5afd47000)
条件编译通常用来调试、保留程序(但不编译),或者在需要对两种状况做不同处理时使用。
4.文件包含处理
所谓文件包含是指一个源文件将另一个源文件的全部内容包含进来,其一般形式如下:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_22.jpg?sign=1739329254-JB7TajKvHGruBRn2myU5q4AKtLyyKj5v-0-4e28b8b2e86af64865447692043015d6)