![嵌入式实时操作系统:基于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=1739486911-EZ3tRSP5CwIUd8i5QeJ9C0WvetnJINFH-0-d0aabc6b753f471074b679ea8d0dd787)
例如,可以通过下面的声明来建立结构体类型:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_4.jpg?sign=1739486911-Nc4JHppsV2CcdF9k9GkujYGLCXVw1M8n-0-cbb19d209f3180d3aa442372a247e8b5)
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_5.jpg?sign=1739486911-6vb1wjJhVdNUuEEL5bk9WVnbRmq2chFQ-0-f24fdc2e0ebdd9c2b0cdd83aeb17f4dc)
结构体类型名用作结构体类型的标志,上面声明中的Date就是结构体类型名,大括号内是该结构体中的全部成员,由它们组成一个特定的结构体。上例中的year、month、day等都是结构体中的成员,结构体类型的大小是其成员大小之和。在声明一个结构体类型时必须对各成员都进行类型声明,每一个成员也称为结构体中的一个域。结构体的成员类型可以是另一个结构体类型,也就是说结构体可以嵌套定义。例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_6.jpg?sign=1739486911-0tPrJK7BoJGnvIuAzWSF7Z2wdo9PBNfp-0-663523c53c1df97d23cd89794f375edb)
这样就声明了一个新的结构体类型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=1739486911-jVSO4K7j7E1iVzimxi0eg9G35k9C4H28-0-6dd668bfe2fe8ae49eef42a1fc5e7b2f)
例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_8.jpg?sign=1739486911-PGrJXNE0SoMoEaBvMUPzHrfkpViNjY7U-0-28ed8c36c4709a8106570b2bab7547ce)
“.”是成员运算符,它在所有运算符中优先级最高,因此可以把stu1.num和stu1.age当作一个整体来看待,相当于一个变量。如果成员本身又属于一个结构体类型,那么要用若干个“.”运算符,一级一级找到最低一级的成员,只能对最低级的成员进行赋值或存取及运算。例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_9.jpg?sign=1739486911-DNoK1tzjBPOlxq2p6XUytukx4cOzgeLW-0-de840ebed5845c48c0d7d8033d2344f8)
结构体变量成员和结构体变量本身都具有地址,且都可以被引用。例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_10.jpg?sign=1739486911-caAonXG3TqZNgjhzfZqwPqSw2SOECac7-0-a8e5f96985266e7bfb15d696edc079e0)
注意:结构体变量的地址主要用作函数参数,传递结构体变量的地址。
3)结构体指针
结构体指针是指存储一个结构体变量起始地址的指针变量。一旦一个结构体指针变量指向了某个结构体变量,就可以通过结构体指针对该结构体变量进行操作。例如,上例中结构体变量stu1,也可以通过指针变量来进行操作:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_11.jpg?sign=1739486911-Z9aYu3BnZq2dBP6knx4pF546h2UJiiyz-0-b820cb158a29e1bd0800b9e2a3bde136)
代码中定义了一个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=1739486911-YwVs9II9zLXVlJqyztEvKsUoqOgkmXU0-0-395f06772590fb18e433ed4f41af8f8f)
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_13.jpg?sign=1739486911-fjFdjqsniIznY0Yge9GwnrWqfjdPjw9Z-0-45453593115991e802fb918b91dcf33f)
可以看到,线程控制块结构体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=1739486911-IpxJq87tSWDTzhfw8HqB9cgy2zYSYrg4-0-8a4196b358546fe36391b9e7e71a00c0)
例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_15.jpg?sign=1739486911-qpT9H8DZeUFp5FnsLDmJ3BimF9O7CGYB-0-776cc4a04f0fdb75cdfe491f397f92d3)
在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=1739486911-3DQclXYAFkKrivSZxfiiB6DYwcNYO2bP-0-b10a45aa0793774a83e7dc94831ed128)
不同的值表示不同状态,枚举类型的成员名清晰地描述了系统内核的当前状态。在后面的章节中,还可以看到其他操作系统一些常用的枚举类型,包括线程状态、线程优先级、系统状态等。
2.2.2 编译相关问题
C语言提供编译预处理的功能,允许在程序中使用几种特殊的命令(它们不是一般的C语句),在C语言编译系统对程序进行常规编译(包括语法分析、代码生成、优化等)之前,先对程序中的这些特殊命令进行预处理,然后将预处理的结果和源程序一起进行常规的编译处理,以得到目标代码。C语言提供的预处理功能主要有宏定义、条件编译和文件包含。
1.宏定义
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_17.jpg?sign=1739486911-ElOfvWXyACn9q7Tig17TWN6chogxooJJ-0-83b16431dfac2a20e5b52dc99cce286d)
表达式既可以是数字、字符,也可以是若干条语句。在编译时,所有引用该宏的地方,都将自动被替换成宏所代表的表达式。例如:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_18.jpg?sign=1739486911-5xUuxj9ziZ7A2pjlNWDuEZGkCz9Ujs2O-0-1790058a67a3d30358dc8688a6c63a75)
2.撤销宏定义
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_19.jpg?sign=1739486911-ibUbYNVW4Wj900GHCh0jXDS7sAFRz43n-0-bd3a61815741bb5649ba058b2d7a93d5)
3.条件编译
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_20.jpg?sign=1739486911-IXnePNTLfbQ71iixgp73JDVL7pEayl7R-0-da1457363ed14e1dd574e6b8794af5a1)
若表达式成立,则编译#if下的程序,否则编译#else下的程序。#endif为条件编译的结束标志。
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_21.jpg?sign=1739486911-xaCpNugBeMpnyH3EEXx3QY3R3maKrR1T-0-0d31a331caf7ecb4d7d3b29a943c34bc)
条件编译通常用来调试、保留程序(但不编译),或者在需要对两种状况做不同处理时使用。
4.文件包含处理
所谓文件包含是指一个源文件将另一个源文件的全部内容包含进来,其一般形式如下:
![](https://epubservercos.yuewen.com/B676CA/26763824301440306/epubprivate/OEBPS/Images/txt003_22.jpg?sign=1739486911-9oaRCEVKE2UNKW1PhXGvrWENundnLaZ2-0-2c5c1c5a887913e20f8a43f1710081e8)