- C编程教程
- C语言 - 首页
- C语言基础
- C语言 - 概述
- C语言 - 特性
- C语言 - 历史
- C语言 - 环境搭建
- C语言 - 程序结构
- C语言 - Hello World
- C语言 - 编译过程
- C语言 - 注释
- C语言 - 词法单元
- C语言 - 关键字
- C语言 - 标识符
- C语言 - 用户输入
- C语言 - 基本语法
- C语言 - 数据类型
- C语言 - 变量
- C语言 - 整数提升
- C语言 - 类型转换
- C语言 - 类型强制转换
- C语言 - 布尔值
- C语言中的常量和字面量
- C语言 - 常量
- C语言 - 字面量
- C语言 - 转义序列
- C语言 - 格式说明符
- C语言中的运算符
- C语言 - 运算符
- C语言 - 算术运算符
- C语言 - 关系运算符
- C语言 - 逻辑运算符
- C语言 - 位运算符
- C语言 - 赋值运算符
- C语言 - 一元运算符
- C语言 - 自增和自减运算符
- C语言 - 三元运算符
- C语言 - sizeof运算符
- C语言 - 运算符优先级
- C语言 - 其他运算符
- C语言中的决策机制
- C语言 - 决策机制
- C语言 - if语句
- C语言 - if...else语句
- C语言 - 嵌套if语句
- C语言 - switch语句
- C语言 - 嵌套switch语句
- C语言中的循环
- C语言 - 循环
- C语言 - while循环
- C语言 - for循环
- C语言 - do...while循环
- C语言 - 嵌套循环
- C语言 - 无限循环
- C语言 - break语句
- C语言 - continue语句
- C语言 - goto语句
- C语言中的函数
- C语言 - 函数
- C语言 - 主函数
- C语言 - 按值调用函数
- C语言 - 按引用调用函数
- C语言 - 嵌套函数
- C语言 - 可变参数函数
- C语言 - 用户自定义函数
- C语言 - 回调函数
- C语言 - 返回语句
- C语言 - 递归
- C语言中的作用域规则
- C语言 - 作用域规则
- C语言 - 静态变量
- C语言 - 全局变量
- C语言中的数组
- C语言 - 数组
- C语言 - 数组的特性
- C语言 - 多维数组
- C语言 - 将数组传递给函数
- C语言 - 从函数返回数组
- C语言 - 变长数组
- C语言中的指针
- C语言 - 指针
- C语言 - 指针和数组
- C语言 - 指针的应用
- C语言 - 指针运算
- C语言 - 指针数组
- C语言 - 指向指针的指针
- C语言 - 将指针传递给函数
- C语言 - 从函数返回指针
- C语言 - 函数指针
- C语言 - 指向数组的指针
- C语言 - 指向结构体的指针
- C语言 - 指针链
- C语言 - 指针与数组的区别
- C语言 - 字符指针和函数
- C语言 - 空指针
- C语言 - void指针
- C语言 - 野指针
- C语言 - 指针解引用
- C语言 - 近指针、远指针和巨指针
- C语言 - 指针数组的初始化
- C语言 - 指针与多维数组的比较
- C语言中的字符串
- C语言 - 字符串
- C语言 - 字符串数组
- C语言 - 特殊字符
- C语言中的结构体和联合体
- C语言 - 结构体
- C语言 - 结构体和函数
- C语言 - 结构体数组
- C语言 - 自引用结构体
- C语言 - 查找表
- C语言 - 点 (.) 运算符
- C语言 - 枚举 (enum)
- C语言 - 结构体填充和打包
- C语言 - 嵌套结构体
- C语言 - 匿名结构体和联合体
- C语言 - 联合体
- C语言 - 位域
- C语言 - typedef
- C语言中的文件处理
- C语言 - 输入 & 输出
- C语言 - 文件I/O (文件处理)
- C语言预处理器
- C语言 - 预处理器
- C语言 - 编译指示
- C语言 - 预处理器运算符
- C语言 - 宏
- C语言 - 头文件
- C语言中的内存管理
- C语言 - 内存管理
- C语言 - 内存地址
- C语言 - 存储类
- 其他主题
- C语言 - 错误处理
- C语言 - 可变参数
- C语言 - 命令执行
- C语言 - 数学函数
- C语言 - static关键字
- C语言 - 随机数生成
- C语言 - 命令行参数
- C编程资源
- C语言 - 问答
- C语言 - 快速指南
- C语言 - 速查表
- C语言 - 有用资源
- C语言 - 讨论
C语言中的自引用结构体
什么是自引用结构体?
自引用结构体是C语言中的一种结构体数据类型,其中一个或多个元素是指向其自身类型变量的指针。自引用用户定义类型在C编程中具有极其重要的作用。它们被广泛用于构建复杂的动态数据结构,例如链表和树。
在C编程中,数组在编译时分配所需的内存,并且数组大小在运行时无法修改。自引用结构体允许您通过动态处理大小来模拟数组。
操作系统的文件管理系统建立在动态构建的树结构之上,这些结构由自引用结构体操作。自引用结构体也用于许多复杂的算法中。
定义自引用结构体
定义自引用结构体的通用语法如下:
strut typename{ type var1; type var2; ... ... struct typename *var3; }
让我们通过以下示例了解如何使用自引用结构体。我们定义了一个名为mystruct的结构体类型。它有一个整数元素“a”,而“b”是指向mystruct类型本身的指针。
我们声明三个mystruct类型的变量:
struct mystruct x = {10, NULL}, y = {20, NULL}, z = {30, NULL};
接下来,我们声明三个“mystruct”指针,并将x、y和z的引用赋值给它们。
struct mystruct * p1, *p2, *p3; p1 = &x; p2 = &y; p3 = &z;
变量“x”、“y”和“z”是相互独立的,因为它们将位于随机位置,这与所有元素都在相邻位置的数组不同。
自引用结构体的示例
示例1
为了明确建立三个变量之间的链接,我们可以在“x”中存储“y”的地址,在“y”中存储“z”的地址。让我们在下面的程序中实现这一点:
#include <stdio.h> struct mystruct{ int a; struct mystruct *b; }; int main(){ struct mystruct x = {10, NULL}, y = {20, NULL}, z = {30, NULL}; struct mystruct * p1, *p2, *p3; p1 = &x; p2 = &y; p3 = &z; x.b = p2; y.b = p3; printf("Address of x: %d a: %d Address of next: %d\n", p1, x.a, x.b); printf("Address of y: %d a: %d Address of next: %d\n", p2, y.a, y.b); printf("Address of z: %d a: %d Address of next: %d\n", p3, z.a, z.b); return 0; }
输出
运行代码并检查其输出:
Address of x: 659042000 a: 10 Address of next: 659042016 Address of y: 659042016 a: 20 Address of next: 659042032 Address of z: 659042032 a: 30 Address of next: 0
示例2
让我们进一步改进上面的程序。我们不声明变量然后将它们的地址存储在指针中,而是使用malloc()函数动态分配内存,其地址存储在指针变量中。然后,我们像下面这样建立三个节点之间的链接:
#include <stdio.h> #include <stdlib.h> struct mystruct{ int a; struct mystruct *b; }; int main(){ struct mystruct *p1, *p2, *p3; p1 = (struct mystruct *)malloc(sizeof(struct mystruct)); p2 = (struct mystruct *)malloc(sizeof(struct mystruct)); p3 = (struct mystruct *)malloc(sizeof(struct mystruct)); p1 -> a = 10; p1->b=NULL; p2 -> a = 20; p2->b=NULL; p3 -> a =30; p3->b=NULL; p1 -> b = p2; p2 -> b = p3; printf("Add of x: %d a: %d add of next: %d\n", p1, p1->a, p1->b); printf("add of y: %d a: %d add of next: %d\n", p2, p2->a, p2->b); printf("add of z: %d a: %d add of next: %d\n", p3, p3->a, p3->b); return 0; }
输出
运行代码并检查其输出:
Add of x: 10032160 a: 10 add of next: 10032192 add of y: 10032192 a: 20 add of next: 10032224 add of z: 10032224 a: 30 add of next: 0
示例3
我们可以从前面元素中存储的地址访问链接中的下一个元素,因为“p1 → b”指向“p2”的地址。我们可以使用while循环来显示链表,如下例所示:
#include <stdio.h> #include <stdlib.h> struct mystruct{ int a; struct mystruct *b; }; int main(){ struct mystruct *p1, *p2, *p3; p1=(struct mystruct *)malloc(sizeof(struct mystruct)); p2=(struct mystruct *)malloc(sizeof(struct mystruct)); p3=(struct mystruct *)malloc(sizeof(struct mystruct)); p1 -> a = 10; p1 -> b = NULL; p2 -> a = 20; p2 -> b = NULL; p3 -> a = 30; p3 -> b = NULL; p1 -> b = p2; p2 -> b = p3; while (p1 != NULL){ printf("Add of current: %d a: %d add of next: %d\n", p1, p1->a, p1->b); p1 = p1 -> b; } return 0; }
输出
运行代码并检查其输出:
Add of current: 10032160 a: 10 add of next: 10032192 Add of current: 10032192 a: 20 add of next: 10032224 Add of current: 10032224 a: 30 add of next: 0
使用自引用结构体创建链表
在上面的示例中,动态构建的列表具有三个通过指针链接的离散元素。我们可以使用for循环通过动态分配内存来设置所需数量的元素,并将下一个元素的地址存储在前面的节点中。
示例
以下示例显示了如何使用自引用结构体创建链表:
#include <stdio.h> #include <stdlib.h> struct mystruct{ int a; struct mystruct *b; }; int main(){ struct mystruct *p1, *p2, *start; int i; p1 = (struct mystruct *)malloc(sizeof(struct mystruct)); p1 -> a = 10; p1 -> b = NULL; start = p1; for(i = 1; i <= 5; i++){ p2 = (struct mystruct *)malloc(sizeof(struct mystruct)); p2 -> a = i*2; p2 -> b = NULL; p1 -> b = p2; p1 = p2; } p1 = start; while(p1 != NULL){ printf("Add of current: %d a: %d add of next: %d\n", p1, p1 -> a, p1 -> b); p1 = p1 -> b; } return 0; }
输出
运行代码并检查其输出:
Add of current: 11408416 a: 10 add of next: 11408448 Add of current: 11408448 a: 2 add of next: 11408480 Add of current: 11408480 a: 4 add of next: 11408512 Add of current: 11408512 a: 6 add of next: 11408544 Add of current: 11408544 a: 8 add of next: 11408576 Add of current: 11408576 a: 10 add of next: 0
使用自引用结构体创建双向链表
链表从开始遍历到到达NULL。您还可以构建一个双向链表,其中结构体有两个指针,每个指针都指向前一个和下一个元素的地址。
为此目的的结构体定义如下:
struct node { int data; int key; struct node *next; struct node *prev; };
使用自引用结构体创建树
自引用结构体也用于构建非线性数据结构,例如树。二叉搜索树由下图逻辑表示:
实现树的结构体定义如下:
struct node { int data; struct node *leftChild; struct node *rightChild; };
要详细了解这些复杂的数据结构,您可以访问DSA教程:数据结构算法