当前位置: 首页 > news >正文

写文案的网站关于集团网站建设申请

写文案的网站,关于集团网站建设申请,婚礼策划公司排名,wordpress首页空白这篇文章是我阅读《嵌入式实时操作系统μCOS-II原理及应用》后的读书笔记#xff0c;记录目的是为了个人后续回顾复习使用。 文章目录 结构体结构体基础声明和定义结构体类型声明和定义结构体变量初始化结构体变量初始化各个成员使用列表符号初始化 使用结构体变量综上 结构体…这篇文章是我阅读《嵌入式实时操作系统μCOS-II原理及应用》后的读书笔记记录目的是为了个人后续回顾复习使用。 文章目录 结构体结构体基础声明和定义结构体类型声明和定义结构体变量初始化结构体变量初始化各个成员使用列表符号初始化 使用结构体变量综上 结构体的简写表示法压缩表示法匿名结构体typedeftypedef 关键字typedef 用于结构体typedef 用于匿名结构体 嵌套结构体结构体指针声明和定义初始化指针使用 结构体数组位域位域基础缺失位示例 紧凑成员 结构体的应用基本应用结构体嵌套程序结构注意点和重要知识点 结构类型的函数指针成员结构体的仿类类型嵌套结构类型指针基类函数钩子实现多态特性计算外层对象指针的宏定义 《嵌入式实时操作系统μC/OS-Ⅱ原理及应用》这本书的前言部分有提到 C 指针看起来像是一个复习的内容其实是要重点强调 C 指针中的函数指针因为这种数据类型在操作系统软件中使用的频率太高了而高校的 C 语言教学又大多不把它当作重点所以致使相当一部分高校学生甚至不知道函数指针为何物。除了 C 指针之外C 语言中的关键字 typedef 及其常用方法也是由于上述原因而被初学者忽视从而造成了学习上的困难因此在第 2 章也增加了这方面的内容。当然因为本书的宗旨不是介绍 C 语言所以仅依靠本书的寥寥数语并不能真正使读者完全掌握函数指针但起码能使读者知道基础的欠缺之所在从而主动去查找和阅读文献。 之前我们已经复习了 C 语言指针的基础知识。 因此接下来我们需要复习 C 语言中的结构体这部分的知识内容为接下来的实时操作系统的学习打基础。 下方内容翻译自 Embeetle 的 Embedded C Tutorial在翻译的过程中我把自己上机实验的结果以截图的方式插入到相应的位置。 结构体 结构体是一种复合数据类型它用于将某些可能不同类型的成员组合成一种单一类型。 结构体是一种用户定义的数据类型允许将不同类型的数据组合在一起。结构体中的各个元素称为成员。 结构体可以包含任意数量的成员这些成员可以是任何数据类型。它有点类似于数组——但数组只能包含相同类型的数据。 结构体基础 请记住上文结构体中的定义 结构体是一种用户定义的数据类型允许将不同类型的数据组合在一起。结构体中的各个元素称为成员。 严格意义上来说“结构体”或“结构”指的是数据类型。然而它也常用于代指由数据类型实例化出来的变量这时就容易引起混淆 为了严谨地避免这种歧义我们将从不单独使用“结构体”这个术语。我们将始终使用“结构体类型”或“结构体变量”组合词 结构体类型本质上是一种数据类型。数据类型仅在编译时存在因此仅仅指定结构体类型不会占用任何内存。一旦指定了结构体类型我们就可以用它实例化出结构体变量。每个结构体变量都会消耗一部分内存——就像其他变量一样整数、浮点数等。 将其比作有一个食谱并用它实际烹饪菜肴 结构体类型是食谱结构体变量是菜肴。只有菜肴才会占用餐桌上的空间。 我们将首先看看如何声明和定义结构体类型。在第 2 节中我们将开始由这种类型实例化出变量。 声明和定义结构体类型 指定结构体类型分为两个步骤首先声明类型然后定义它。注意此时没有创建任何变量我们在此仅使用“声明”和“定义”术语来指代结构体类型本身 结构体类型的声明只是告知编译器有一个特定的结构体数据类型存在及其名称。 结构体类型的定义 完整地指定了结构体数据类型。定义后编译器了解了所有关于它的信息 结构体的确切内存布局及其所有成员。结构体实例在内存中占用的空间当我们声明并定义一个结构体变量时就会创建一个“结构体实例”。我们将在第 2 节中进行学习了解。 我们来看一个例子 // 声明数据类型 struct Point struct Point;// 定义数据类型 struct Point struct Point {int x;int y; };在声明之后编译器知道存在一种结构体数据类型 struct Point。没错关键词 struct 是该数据类型名称的一部分在定义之后编译器也知道了关于该结构体数据类型的所有细节。注意此时还没有实例化该结构体数据类型的变量因此尚未分配任何内存。 {..}   在同一行上声明和定义  通常结构体类型的显式声明会被省略这样的话定义就可以同时起到声明和定义类型的双重作用 // 声明并定义数据类型 struct Point struct Point {int x;int y; };在某些情况下我们必须事先显式声明 // 声明数据类型 struct Foo struct Foo;// 声明数据类型 struct Bar struct Bar;// 定义数据类型 struct Bar struct Bar {struct Foo *foo;... };// 定义数据类型 struct Foo struct Foo {struct Bar *bar;... };在下一章中我们将实例化结构体类型从中创建变量。 声明和定义结构体变量 最后让我们创建变量假设我们已经声明并定义了数据类型 struct Point 现在我们来声明和定义变量 // 声明变量 a 和 b extern struct Point a; extern struct Point b;// 定义变量 a 和 b struct Point a; struct Point b;再次强调数据类型不是 Point 而是 struct Point如果记住这一点那么声明和定义结构体变量其实并没有什么特别之处。 就像普通变量一样我们应该记住声明和定义之间的区别。我们在这里重复一遍 变量的声明告知编译器一个特定变量存在及其名称、类型和大小(对于结构体变量数据类型显然是我们之前声明并定义的结构体类型(见第 1 章))编译器随后知道足够的信息来与变量交互。然而此时不会进行任何内存的分配。 变量的定义为变量分配一个或多个内存单元这发生在编译器将源文件转换为目标文件时目标文件为每个定义的变量保留内存空间。大多数目标文件是可重定位的这意味着它们从地址 0x0000 开始分配内存空间链接器最终将所有的这些目标文件合并在一起上下移动它们的基地址以使它们都能够塞进内存中只有在那之后绝对内存地址才会被知道。 初始化结构体变量 一旦声明和定义了结构体变量就应该为其成员赋值换句话说结构体变量应该被初始化。这可以通过多种方式实现。 初始化各个成员 要初始化结构体变量我们可以分别为每个成员赋值这非常简单。假设结构体变量已经声明和定义初始化如下所示 // 初始化结构体变量 a a.x 3; a.y 5;使用列表符号初始化 我们可以使用列表符号一次性为所有成员赋值而不是分别赋值 // 初始化结构体变量 a a (struct Point){.x 3,.y 5, };注意强制转换前缀{..} 块内表达式的结果需要转换为数据类型 struct Point然后才能赋值给变量 a。如果在定义变量 a 的同一条语句中对其进行初始化则可以省略强制转换。 在 {..} 表达式中.x 和 .y 明确显示了哪个成员被赋予了什么值。但是如果你愿意也可以省略它们 // 初始化结构体变量 a a (struct Point){3,5, };使用结构体变量 一旦我们的结构体变量 a 声明、定义并初始化后我们就可以像使用其他任何变量一样使用它。然而有一个特殊的特性我们可以使用点表示法访问结构体变量内部的各个成员。 // 使用结构体变量 a a.x a.y 7;综上 我们从声明和定义一个结构体类型开始然后我们使用该类型声明和定义结构体变量最终进行初始化。让我们将所有这些放在这个测试文件中 // C 代码测试文件 // #include stdio.h// 声明数据类型 struct Point struct Point;// 定义数据类型 struct Point struct Point {int x;int y; };// 声明变量 a 和 b extern struct Point a; extern struct Point b;// 定义变量 a 和 b struct Point a; struct Point b;int main() {// 初始化结构体变量 aa.x 3;a.y 5;// 初始化结构体变量 b// 不同的方法b (struct Point){.x 7,.y 9,};printf(a.x %d\n, a.x);printf(a.y %d\n, a.y);printf(b.x %d\n, b.x);printf(b.y %d\n, b.y); }你会得到以下输出 gcc test.c -Wall a.exe a.x 3 a.y 5 b.x 7 b.y 9运行结果如下图所示 我知道你现在在想什么我都快闻到味儿了 确实我们分步骤做了每件事 声明结构体数据类型定义结构体数据类型声明一个结构体变量定义结构体变量初始化结构体变量。 当然在实际情况下这样详细地描述是不合理的但这是一次极好的学习经验在下一章节中我们将看到简写符号。换句话说我们将学习如何将上述几个步骤糅合进单个表达式中。 结构体的简写表示法 回顾我们在上一章节中如何创建结构体 声明结构体数据类型定义结构体数据类型声明结构体变量定义结构体变量初始化结构体变量。 这显得非常冗长让我们看看如何简化这一过程。 压缩表示法 首先让我们从结构体数据类型的声明和定义开始 // 声明数据类型 struct Point struct Point;// 定义数据类型 struct Point struct Point {int x;int y; };首先我们可以将声明和定义合并为一个表达式。只需省略声明部分使定义同时具有声明的功能 // 声明并定义数据类型 struct Point struct Point {int x;int y; };在结构体数据类型声明和定义之后我们可以实例化变量。我们通常在单独的表达式中进行但也可以将其合并为一个表达式 // 声明并定义数据类型 struct Point然后 // 使用该数据类型声明并定义变量 a 和 b。 struct Point {int x;int y; } a, b;如何初始化变量呢这也可以实现 // 声明并定义数据类型 struct Point然后 // 从中声明并定义变量 a 和 b并初始化它们。 struct Point {int x;int y; } a {3, 5}, b {7, 9};我们把它放在一个测试文件中 // C 代码测试文件 // #include stdio.h// 声明并定义数据类型 struct Point然后 // 从中声明并定义变量 a 和 b并初始化它们。 struct Point {int x;int y; } a {3, 5}, b {7, 9};int main() {printf(a.x %d\n, a.x);printf(a.y %d\n, a.y);printf(b.x %d\n, b.x);printf(b.y %d\n, b.y); }你会得到以下输出 gcc test.c -Wall a.exe a.x 3 a.y 5 b.x 7 b.y 9运行结果如下图所示 匿名结构体 在某些情况下我们只希望从一个结构体数据类型中实例化一个或几个变量我们不打算以后再实例化更多的变量那么给这个结构体数据类型命名就没有意义只需省略名称 // 声明并定义一个匿名结构体数据类型然后 // 从中声明并定义变量 a 和 b并初始化它们。 struct {int x;int y; } a {3, 5}, b {7, 9};从这个结构体数据类型中只有变量 a 和 b 存在。一旦表达式结束就无法再创建新变量 typedef 在处理结构体时typedef 关键字经常被使用到。通常该关键字用于为给定的数据类型创建一个额外的名称别名。让我们详细说明一下。 typedef 关键字 typedef 关键字并不会创建一个新数据类型它只是为已有的数据类型创建了一个别名。因此它常用于简化语法。以下是一个简单的例子 // 将 BYTE 做为 unsigned char 的别名 typedef unsigned char BYTE;一旦 BYTE 被指定为 unsigned char 的别名就可以这样使用 // 使用 BYTE 作为 unsigned char 的缩写 BYTE b1, b2;typedef 用于结构体 typedef 关键字还可以给结构体数据类型创建别名 // 声明并定义数据类型 struct Point struct Point {int x;int y; };// 指定 POINT 为 struct Point 的别名 typedef struct Point POINT;糟糕我们又使用了冗长的方式首先我们声明并定义了数据类型 struct Point然后利用 typedef 关键字为结构体数据类型创建别名 POINT。这两个动作可以合并为一个表达式 // 声明并定义数据类型 struct Point然后 // 指定 POINT 作为该数据类型的别名。 typedef struct Point {int x;int y; } POINT;一旦结构体数据类型有了别名就可以通过两种方式实例化变量——使用或不使用别名 // 声明变量 a 和 b extern struct Point a; extern POINT b;// 定义变量 a 和 b struct Point a; POINT b;我将别名写成大写而结构体名写成小写并没有特别的原因只要它们是不同的标识符就可以。 typedef 用于匿名结构体 我们使用 typedef 为 struct Point 指定了别名 POINT。之后我们有两种方式从结构体中实例化变量——使用或不使用别名这有点愚蠢。更合理的做法是使用 typedef 为匿名结构体指定一个别名 // 声明并定义一个匿名结构体数据类型然后 // 指定 POINT 作为该数据类型的别名。 typedef struct {int x;int y; } POINT;这样做时我们本质上为匿名结构体去掉了匿名。现在我们只能使用别名来实例化变量 // 声明变量 a 和 b extern POINT a; extern POINT b;// 定义变量 a 和 b POINT a; POINT b;很聪明对吧 无论如何事实是大多数人更喜欢使用 typedef 的方式这看起来更简洁。 嵌套结构体 结构体可以嵌套例如 // C 代码测试文件 // #include stdio.h// 声明并定义一个匿名结构体数据类型然后 // 指定 Point 作为该数据类型的别名。 typedef struct {int x;int y; } Point;// 声明并定义一个匿名结构体数据类型然后 // 指定 Line 作为该数据类型的别名。 typedef struct {Point a;Point b; } Line;int main() {Line line;line.a.x 3;line.a.y 8;line.b.x 7;line.b.y 9;printf(line.a.x %d\n, line.a.x);printf(line.a.y %d\n, line.a.y);printf(line.b.x %d\n, line.b.x);printf(line.b.y %d\n, line.b.y); }运行结果如下图所示 在这个例子中结构体 Line 包含两个 Point 结构体作为其成员。要访问这些点的成员只需再使用一级点号表示法即可。 结构体指针 从理论上讲“结构”或“结构体”指的是数据类型尽管有时也用来指代该数据类型的结构体变量实例。我们在结构体基础章节中已经提到过这种歧义性。 当谈到“结构体指针”时我们实际上指的是“指向结构体实例的指针”请记住这一点。 声明和定义 如果我们已经指定了数据类型 struct Point那么我们可以像下面这样声明和定义一个指向该结构体实例的指针 // 声明指针 struct Point *p;// 定义指针 extern struct Point *p;通常我们会有一个用别名引用的 typedef 结构体如 POINT // 声明指针 POINT *p;// 定义指针 extern POINT *p;如你所见指向结构体实例的指针的声明和定义与其他指针的用法没有区别。 初始化 要初始化指针它需要设置为其他变量的地址——在当前这种情况下是结构体实例的地址。假设 a 是这样一个变量那么 // 初始化指针 p a;指针使用 一旦指针被声明、定义和初始化后它就可以像它指向的变量一样使用。为此我们需要在指针前加上 *解引用操作符。在这方面没有什么特别之处。 // 通过指针访问成员 x (*p).x 5; // 等效于 a.x 5;有一种简写表示法 // 通过指针访问成员 x p-x 5; // 等效于 a.x 5;换句话说 p-x 等效于 (*p).x 请参考以下示例 // C 代码测试文件 // #include stdio.h// 声明并定义数据类型 struct Point struct Point {int x;int y; };// 指定 POINT 为 struct Point 的别名 typedef struct Point POINT;// 实例化结构体声明并定义变量 a POINT a;// 声明、定义并初始化指向 a 的指针 POINT *p a;int main() {// 初始化结构体变量 aa.x 3;a.y 5;printf(a.x %d\n, a.x);printf(a.y %d\n, a.y);printf(p-x %d\n, p-x);printf(p-y %d\n, p-y); }这段代码展示了如何使用结构体指针访问结构体成员。 运行结果如下图所示 结构体数组 “结构体数组”实际上是指“结构体实例的数组”。事实上这并没有什么特别之处它只是一个数组每个元素恰好是一个结构体实例。唯一特别的是我们可以使用列表表示法来初始化数组 // 声明、定义并初始化结构体数组 struct Point my_arr[3] {{.x4, .y5}, // 初始化第一个元素{.x6, .y7}, // 初始化第二个元素{.x8, .y9} // 初始化第三个元素 };然后我们可以这样访问成员my_arr[0].x 初始化甚至可以更简短 // 声明、定义并初始化结构体数组 struct Point my_arr[3] { {4, 5}, {6, 7}, {8, 9} };位域 MCU 在其内存中保留了一部分用于特殊功能寄存器SFRs。这些寄存器的详细信息可以在 MCU 的数据手册中找到。例如以下是 dsPIC33FJ256MC710A 的 PORTA 和 LATA 寄存器 它们在 RAM 中有固定的位置地址分别是 0x02C2 和 0x02C4。由于这是一个 16 位处理器它们中的每一个都是 16 位宽的。通过 LATA 寄存器可以强制 MCU 的引脚电平是高或低PORTA 寄存器用于感知它们的状态。但如何从往这些寄存器中读写单个位呢 我们可以使用位掩码来实现。位掩码是一种巧妙的使用按位操作符例如 和 |来操纵变量中某些位的方法而不影响其他位。然而这种方法很快会导致代码难以阅读。 更优雅的方法是使用位域。 位域基础 位域是结构体中的无符号整数成员占据指定数量的相邻位。例如 typedef struct {unsigned int lo: 1;unsigned int mid: 6;unsigned int hi: 1; } FooBits;在这个例子中指定了一个匿名结构体并给它取名为 FooBits。结构体有三个位域lo、mid 和 hi。它们每一个都是无符号整数类型并且指定了各自的位数。无符号整数类型甚至可以写成 unsigned typedef struct {unsigned lo: 1;unsigned mid: 6;unsigned hi: 1; } FooBits;现在用这个结构体实例化一个变量 // 声明并定义来自结构体 FooBits 的变量 foo FooBits foo;// 初始化位域 foo.lo 1; foo.mid 8; foo.hi 0;本质上这与普通的结构体变量非常相似。我们可以使用点号表示法来访问位域成员。结构体变量也可以使用列表表示法进行初始化如果结构体变量的定义和初始化在同一条语句中进行则可以省略类型转换 (FooBits) // 声明并定义来自结构体 FooBits 的变量 foo FooBits foo;// 初始化位域 foo (FooBits){1, 8, 0};缺失位 许多 MCU 的寄存器都有未使用的位。本章节开头的例子显示了 PORTA 和 LATA 寄存器中第 8 位和第 11-13 位是未使用的。这些未使用的位域在结构体定义中为简化起见不命名 typedef struct {unsigned lo: 1; // 位域命名为 lounsigned : 6; // 匿名位域unsigned hi: 1; // 位域命名为 hi } FooBits;在这个例子中位域 lo 和 hi 之间的 6 位是不可达的——这正是我们所需要的。初始化结构体变量时也应忽略这些位 // 声明并定义来自结构体 FooBits 的变量 foo FooBits foo;// 初始化位域忽略匿名位域 foo (FooBits){1, 0};示例 这是 Microchip 定义的结构体 LATAbits 的样子 // 声明变量 LATA extern volatile uint16_t LATA __attribute__((__sfr__));// 指定类型 struct tagLATABITS 并提供别名 // LATABITS。 typedef struct tagLATABITS {uint16_t LATA0:1;uint16_t LATA1:1;uint16_t LATA2:1;uint16_t LATA3:1;uint16_t LATA4:1;uint16_t LATA5:1;uint16_t LATA6:1;uint16_t LATA7:1;uint16_t :1;uint16_t LATA9:1;uint16_t LATA10:1;uint16_t :3;uint16_t LATA14:1;uint16_t LATA15:1; } LATABITS;// 声明变量 LATAbits 为该结构体的实例。 extern volatile LATABITS LATAbits __attribute__((__sfr__));Microchip 在这里声明了但没有定义两个变量 类型为 uint16_t16 位整数的 LATA类型为 LATABITS带位域的结构体总共 16 位的 LATAbits 由于它们未被定义编译器不会为它们分配内存地址extern 关键字处理这一点。更进一步的是__attribute__((__sfr__)) 编译器指令暗示编译器地址分配将在构建过程的最后阶段链接步骤中进行。链接脚本提供绝对地址 PORTA 0x2C2; _PORTA 0x2C2; _PORTAbits 0x2C2; LATA 0x2C4; _LATA 0x2C4; _LATAbits 0x2C4;变量 LATA 和 LATAbits 都映射到相同的地址 0x2C4。变量 LATA 是一个 16 位整数我们可以整体操作。LATAbits 总共也是 16 位但它的数据类型是结构体因此它提供了对各个位的细粒度访问 紧凑成员 当你看到这样的代码 // 指定匿名结构体类型并通过别名 Foo 提供 typedef struct {char a;char b; } Foo;// 声明、定义并初始化一个结构体变量 Foo foo {A, B};你可能认为变量 foo 在内存中的样子如下 然而这并不一定正确编译器通常会根据 CPU 的位数对成员进行对齐。换句话说在一个 16 位的 MCU 上我们可能会得到这样的情况 变量 foo 现在消耗了两个 16 位的内存单元每个内存单元的下半部分用于存储 char 类型的数据。 在大多数情况下这无关紧要。但是当这很重要时我们需要使用编译器指令来紧凑成员 typedef struct {char a;char b __attribute__((__packed__)); // 成员 b 紧跟在 a 之后 } Foo;在上面的例子中成员 b 被紧凑存放它紧跟在前一个成员后面没有任何填充。 如果有多个成员需要紧凑存放我们可以将编译器指令放在结构体的顶部 typedef struct __attribute__((__packed__)) {char a;char b;char c;char d; } Foo;注意嵌套结构体例如 typedef struct {uint8_t x;uint8_t y; } Point;typedef struct __attribute__((__packed__)) {Point a;Point b; } Line;结构体 Line 是紧凑的但这并不意味着结构体 Point 也是紧凑的换句话说成员 a 和 b 之间不会有填充但成员 x 和 y 之间仍然可能有填充。 上方内容翻译自 Embeetle 的 Embedded C Tutorial 结构体的应用 RTOS 中的各种资源的控制块是结构体类型对象因此有“没有结构体类型就没有操作系统”的说法由此可见C 语言的结构体类型在 RTOS 中作用巨大。 基本应用 为下图所示的个人基本信息表声明一个结构体类型然后定义这个类型的对象并为其成员赋初值最后输出它们 测试用程序代码如下 #include stdio.h #include stdlib.hstruct personal {char *name;char *sex;char *birthday;char *nationality;char *health;int age;char *height;char *email;char *address; };int main(void) {struct personal ps1;ps1.name ZhangSan;ps1.sex male;ps1.birthday 5/1/2000;ps1.nationality han;ps1.health good;ps1.age 24;ps1.height 180;ps1.email ZhangSanqq.com;ps1.address BeiJing;printf(Name: \t%s\nSex: \t%s\nAge: \t%d\n, ps1.name, ps1.sex, ps1.age);exit(0); }运行结果如下图所示 这段代码首先包含了两个头文件 #include stdio.h 和 #include stdlib.h分别用于输入输出操作和内存管理。 接着定义了一个名为 personal 的结构体其中包含了成员变量 name、sex、birthday、nationality、health、age、height、email 和 address用于存储个人信息。 在 main 函数中创建了一个结构体变量 ps1并为其赋值name 为 “ZhangSan”sex 为 “male”birthday 为 “5/1/2000”nationality 为 “han”health 为 “good”age 为 24height 为 “180”email 为 “ZhangSanqq.com”address 为 “BeiJing”。 最后通过 printf 函数打印出 ps1 结构体变量中的 name、sex 和 age 信息并通过 exit(0) 退出程序。 结构体嵌套 在上面个人基本信息表的基础上增加如下图所示的学习经历表然后将这两个表格组合成一个学生信息表 测试用程序代码如下 #include stdio.h #include stdlib.h//声明个人基本信息表即声明结构类型 struct personal {char *name;char *sex;char *birthday;char *nationality;char *health;int age;char *height;char *email;char *address; };//声明学习经历表 struct studyExperience {char *school;char *university; };//声明学生信息表 struct studentMessage {struct personal ps1Tab;struct studyExperience stuExp; };int main(void) {//定义个人基本信息表对象并初始化struct personal ps1 {ZhangSan,male,5/1/2000,han,good,24,180,ZhangSanqq.com,BeiJing};//读取并输出结构体部分成员的值printf(Name: \t%s\nSex: \t%s\nAge: \t%d\n\n, ps1.name, ps1.sex, ps1.age);//定义学习经历表对象并初始化struct studyExperience se {zhongshan 1 school,guangdong university};//读取并输出结构体成员的值printf(School: \t\t%s\nUniversity: \t%s\n\n, se.school, se.university);//定义学生信息表对象并初始化struct studentMessage sm {ps1,se};//输出部分成员值printf(Name: \t\t%s\nSchool: \t%s\n, sm.ps1Tab.name, sm.stuExp.university);exit(0); } 运行结果如下图所示 这个C语言程序定义了几个结构体类型来存储个人信息和学习经历并在main函数中创建了这些结构体的实例对它们进行了初始化并打印了一些成员的值。以下是对程序的详细解释及需要注意的细节和知识点 程序结构 结构体声明 struct personal定义了一个结构体来存储个人基本信息包括姓名、性别、生日、国籍、健康状况、年龄、身高、电子邮件和地址。这些成员中的一些如name, sex, birthday等是char*类型即指向字符的指针。 struct studyExperience定义了一个结构体来存储学习经历包括学校和大学这两个成员都是char*类型。 struct studentMessage定义了一个结构体来存储学生的信息它包含两个结构体成员分别是personal和studyExperience类型的实例。 main函数 个人信息表对象初始化 struct personal ps1 {ZhangSan,male,5/1/2000,han,good,24,180,ZhangSanqq.com,BeiJing };这部分定义了一个personal类型的结构体对象ps1并为各个成员赋了初值。需要注意的是虽然这些值是字符串常量但ps1结构体的成员是char*类型这意味着它们是指向这些字符串常量的指针。这样做的好处是节省内存但如果你需要修改这些字符串你需要在堆上分配内存并使用strdup等函数复制字符串。 学习经历表对象初始化 struct studyExperience se {zhongshan 1 school,guangdong university };这部分定义了一个studyExperience类型的结构体对象se并为学校和大学赋初值。 学生信息表对象初始化 struct studentMessage sm {ps1,se };这部分定义了一个studentMessage类型的结构体对象sm它包含了ps1和se两个结构体的实例分别对应个人信息和学习经历。 输出结构体成员 printf(Name: \t%s\nSex: \t%s\nAge: \t%d\n\n, ps1.name, ps1.sex, ps1.age); printf(School: \t\t%s\nUniversity: \t%s\n\n, se.school, se.university); printf(Name: \t\t%s\nSchool: \t%s\n, sm.ps1Tab.name, sm.stuExp.university);这些printf语句用于打印结构体成员的值。注意%s格式说明符用于打印字符串%d用于打印整数。 注意点和重要知识点 字符串和指针 在这个程序中字符串成员是用char*来声明的。这意味着这些成员存储的是指向字符串的指针而不是实际的字符串数据。如果在程序中修改这些字符串数据需要分配动态内存并使用strdup函数来复制字符串。 结构体成员的初始化 结构体成员的初始化需要保持数据类型一致。例如对于char*类型的成员需要确保字符串常量的生命周期足够长以防止在结构体使用期间字符串数据被修改或释放。 内存管理 如果要在结构体中存储动态分配的内存记得在程序结束之前释放这些内存避免内存泄漏。 结构体的嵌套 studentMessage结构体通过将personal和studyExperience结构体作为成员实现了嵌套。结构体嵌套是组织和管理复杂数据的有效方法。 exit函数 exit(0);用于正常终止程序。在许多简单程序中它的作用类似于return 0;表示程序正常结束。 结构类型的函数指针成员 测试用程序代码如下 /** 结构类型的函数指针成员*/ #include stdio.h #include stdlib.hstruct studentMessage; //对象前置声明 void showMessage(struct studentMessage x); //函数前置声明 //声明个人基本信息表即声明结构类型 struct personal {char *name;char *sex;char *birthday;char *nationality;char *health;int age;char *height;char *email;char *address; }; //声明学习经历表 struct studyExperience {char *school;char *university; }; //声明学生信息表 struct studentMessage {struct personal *ps1Tab;struct studyExperience *stuExp;void (*disPer)(struct studentMessage x); };int main(void) {//定义个人基本信息表对象并初始化struct personal ps1 {ZhangSan,male,5/1/2000,han,good,24,180,ZhangSanqq.com,BeiJing};//读取并输出结构体部分成员的值printf(Name: \t%s\nSex: \t%s\nAge: \t%d\n\n, ps1.name, ps1.sex, ps1.age);//定义学习经历表对象并初始化struct studyExperience se {zhongshan 1 school,guangdong university};//读取并输出结构体成员的值printf(School: \t\t%s\nUniversity: \t%s\n\n, se.school, se.university);//定义学生信息表对象并初始化struct studentMessage sm {.ps1Tab ps1,.stuExp se,.disPer showMessage};//通过结构类型对象的函数指针调用了函数 showMessagesm.disPer(sm);exit(0); }/** 打印信息 */ void showMessage(struct studentMessage x) {//输出部分成员值printf(Name: \t%s\nSchool: %s\n, x.ps1Tab-name, x.stuExp-school); }运行结果如下图所示 结构体的仿类类型 测试用程序代码如下 /** 结构体的仿类类型*/ #include stdio.h #include stdlib.hstruct studentMessage; //对象前置声明 void showMessage(struct studentMessage x); //函数前置声明 void setAge(struct studentMessage x, int y); //设置 age 值的函数前置声明 int getAge(struct studentMessage x); //获取 age 值的函数前置声明 //声明个人基本信息表即声明结构类型 struct personal {char *name;char *sex;char *birthday;char *nationality;char *health;int age;char *height;char *email;char *address; }; //声明学习经历表 struct studyExperience {char *school;char *university; }; //声明学生信息表 struct studentMessage {struct personal *ps1Tab;struct studyExperience *stuExp;void (*disPer)(struct studentMessage x);void (*set_age)(struct studentMessage x, int y); //set 函数指针int (*get_age)(struct studentMessage x); //get 函数指针 };int main(void) {//定义个人基本信息表对象并初始化struct personal ps1 {ZhangSan,male,5/1/2000,han,good,24,180,ZhangSanqq.com,BeiJing};//读取并输出结构体部分成员的值printf(Name: \t%s\nSex: \t%s\nAge: \t%d\n\n, ps1.name, ps1.sex, ps1.age);//定义学习经历表对象并初始化struct studyExperience se {zhongshan 1 school,guangdong university};//读取并输出结构体成员的值printf(School: \t\t%s\nUniversity: \t%s\n\n, se.school, se.university);//定义学生信息表对象并初始化struct studentMessage sm {.ps1Tab ps1,.stuExp se,.disPer showMessage, //为函数指针赋值.set_age setAge,.get_age getAge};//通过结构类型对象的函数指针调用了函数 showMessagesm.disPer(sm);//调用 set_age 函数sm.set_age(sm, 35);//调用 get_age 函数并输出printf(Age: \t%d\n, sm.get_age(sm));exit(0); }/** 打印信息 */ void showMessage(struct studentMessage x) {//输出部分成员值printf(Name: \t%s\nSchool: %s\n, x.ps1Tab-name, x.stuExp-school); } /** 设置 age 值的函数*/ void setAge(struct studentMessage x, int y) {x.ps1Tab-age y; } /** 获取 age 值的函数*/ int getAge(struct studentMessage x) {return x.ps1Tab-age; }运行结果如下图所示 嵌套结构类型指针 测试用程序代码如下 /** 嵌套结构类型指针*/ #include stdio.h #include stdlib.h//声明了一个只有一个成员的结构类型猫猫类型 struct cat {char *color; //猫猫的颜色 }; //声明嵌套结构类型小猫类型其中内嵌了 struct cat 对象 struct childCat {struct cat c; //内嵌对象char *color; //猫咪的颜色 };int main(void) {//定义一个包含有内嵌对象的外层对象struct childCat chi;chi.c.color white; //为内嵌对象 color 成员赋值chi.color black; //为外层对象 color 成员赋值//把首地址赋予了内嵌对象类型指针struct cat *p (struct cat *)(chi);//输出内嵌对象的 color 的值printf(Cat color: %s\n, p-color);exit(0); }运行结果如下图所示 基类函数钩子 测试用程序代码如下 /** 基类函数钩子*/ #include stdio.h #include stdlib.h//内嵌类型基类 struct cat {char *color; //猫猫的颜色void (*_show)(struct cat *p); //函数钩子 }; //外层类型派生类 struct childCat {struct cat c; //内嵌对象char *color; //猫咪的颜色 };/** 基类类型的 show 函数*/ void showCat(struct cat *p) {printf(Cat color: %s\n, p-color); }/** 派生类类型的 show 函数*/ void showChildCat(struct cat *p) {//将 p 转换为派生类类型printf(Child Cat color: %s\n, ((struct childCat *)p)-color); }int main(void) {//定义基类对象并初始化struct cat stCat;stCat.color white;stCat._show showCat; //为钩子挂接函数 showCat//定义派生类对象并初始化struct childCat stChildCat;stChildCat.color black; //为外层对象 color 成员赋值stChildCat.c._show showChildCat; //为钩子挂接函数 showChildCat//使用对象调用钩子stCat._show(stCat);stChildCat.c._show((struct cat *)stChildCat);//定义两个基类类型的指针变量struct cat *p1, *p2;p1 stCat;p2 (struct cat *)stChildCat;//使用基类类型的指针调用钩子调用界面完全相同p1-_show(stCat);p2-_show((struct cat *)stChildCat);exit(0); }程序运行结果如下图所示 实现多态特性 测试用程序代码如下 /** 实现多态特性*/ #include stdio.h #include stdlib.h//内嵌类型基类 struct cat {char *color; //猫猫的颜色void (*_show)(struct cat *p); //函数钩子 }; //外层类型派生类 struct childCat {struct cat c; //内嵌对象char *color; //猫咪的颜色 };/** 基类类型的 show 函数*/ void showCat(struct cat *p) {if (NULL p) //指针为空返回return;printf(Cat color: %s\n, p-color); }/** 派生类类型的 show 函数*/ void showChildCat(struct cat *p) {if (NULL p) //指针为空返回return;//将 p 转换为派生类类型printf(Child Cat color: %s\n, ((struct childCat *)p)-color); }/** 同一个函数因接收对象的类型不同其 show 的结果也不同*/ void show(struct cat *_cat) {if (NULL _cat) //指针为空返回return;//调用钩子上的函数_cat-_show(_cat); }int main(void) {//定义基类对象并初始化struct cat stCat;stCat.color white;stCat._show showCat; //为钩子挂接函数 showCat//定义派生类对象并初始化struct childCat stChildCat;stChildCat.color black; //为外层对象 color 成员赋值stChildCat.c._show showChildCat; //为钩子挂接函数 showChildCat//以 struct childCat * 类型对象为实参调用 show()show((struct cat *)stChildCat);//以 struct cat * 类型对象为实参调用 show()show(stCat);exit(0); }程序运行结果如下图所示 计算外层对象指针的宏定义 测试用程序代码如下 /** 计算外层对象指针的宏定义*/ #include stdio.h #include stdlib.h//offsetof 和 container_of 的定义 #define offsetof(TYPE, MEMBER) ((size_t)((TYPE*)0)-MEMBER) #define container_of(ptr, type, member) ({\const typeof(((type *)0)-member) *__mptr (ptr); \(type *)((char *)__mptr - offsetof(type, member));})//定义了结构 struct parentStruct 本例中它为内嵌对象的结构类型 struct parentStruct {int a; }; //定义了结构 struct childStruct 本例中它为外层对象的结构类型 struct childStruct {int b;struct parentStruct parent; };/** 参数为内嵌结构类型指针的函数 show()*/ void show(struct parentStruct *p) {//使用宏 container_of 获得外层对象指针struct childStruct *pChild container_of(p, struct childStruct, parent);printf(a %d\nb %d\n, p-a, pChild-b); }int main(void) {//定义外层对象并初始化struct childStruct child;child.b 53;child.parent.a 77; //给内嵌对象成员 a 赋值show(child.parent);exit(0); }运行结果如下图所示
http://www.laogonggong.com/news/138731.html

相关文章:

  • 佛山专业网站建设的公司互联网装修公司排行榜
  • 名校建设专题网站制作公众号的编辑器
  • 最专业的营销网站建设公司排名tornado 做网站
  • 网站配色教程wordpress注册验证邮箱
  • 做外贸要看哪些网站网站的文本链接怎么做
  • 溧水区住房和城乡建设厅网站oa网站建设推广
  • server2008 做网站湖南长沙大学
  • 烟台网站建设专业臻动传媒有源码如何搭建app
  • 汉南网站建设qq音乐插件 wordpress
  • 做网站定金交多少合适wordpress上传目录
  • 我要学习做网站小程序生成平台系统
  • 专门做投标书的网站网站后台样式
  • 手机网站 input贵阳百度推广电话
  • ps网站首页设计郑州最出名的不孕不育医院
  • wordpress每页不显示文章哈尔滨网络优化工程师
  • 织梦手机网站标签调用大全阿里巴巴怎么建设网站首页
  • 国外设计文章的网站owo表情添加wordpress
  • 交换机做网站北京建设教育协会
  • 做网站 excel2023年新闻热点事件
  • 关于建设门户网站的请示成都彭超艺术设计有限公司
  • dw做的网站怎样才有域名wordpress 分类目录代码
  • wordpress 删除图片说说seo论坛
  • 怎么躲避wordpress审核评论哈尔滨百度网站快速优化
  • 西部数码如何建设自己的网站温州人才网招聘网官网
  • 专门做特医食品的网站如何查询网站所有人
  • 高校网站首页设计wordpress 母婴类模板
  • 网站建设请示公众号开发者密钥重置影响
  • 网站建设所要花费的资金小城市门户网站建设方案
  • 垫江做网站怎么刷网站点击量
  • 医院网站建设情况汇报wordpress制作索引页