C 语言设计模式(结构型)
文章目录
- 代理模式
- 场景
- 示例
- 门面模式
- 场景
- 示例
- 桥接模式
- 场景
- 示例
- 适配器模式
- 场景
- 示例
- 外观模式
- 场景
- 示例
- 享元模式
- 场景
- 示例
- 装饰器模式
- 场景
- 示例
- 组合模式
- 场景
- 示例
代理模式
C语言中,代理模式通常用于实现对象的间接访问。代理模式是一种结构型设计模式,它允许你提供一个代理对象来控制对另一个对象的访问。这种类型的设计模式属于结构型模式,因为它创建了对象的代理,以控制对它们的访问。
- 抽象主题(Subject):定义了真实主题和代理主题的共同接口,这样代理就可以用来代替真实主题。
2。 真实主题(Real Subject):定义了实际对象,代理对象是对它的一个引用。代理模式中的大部分工作都由真实主题完成,而代理主要负责将请求传递给真实主题。
- 代理主题(Proxy):保存一个引用使得代理可以访问真实主题,并提供与真实主题相同的接口,这样客户端就无需知道代理和真实主题的区别。
代理模式通常被用来解决以下一些问题:
-
远程代理(Remote Proxy):在不同的地址空间中代表一个对象。这种情况下,代理对象负责将请求和参数进行编码,并向远程对象发送请求。
-
虚拟代理(Virtual Proxy):用于按需创建昂贵对象,这样只有在真正需要时才会实例化真实对象。例如,一个图像浏览器可能只在需要显示图像时才会加载真正的图像数据。
-
保护代理(Protection Proxy):用于控制对对象的访问,可以根据访问权限拒绝调用者的请求。
场景
-
文件访问控制:在一个需要对文件进行访问控制的系统中,可以使用代理模式来创建一个文件访问代理,以控制对文件的读取和写入操作,实现对文件访问的安全管理。
-
网络通信:在网络通信中,可以使用代理模式来创建网络通信代理,以控制对网络资源的访问,实现网络通信的安全验证、加密解密等功能。
-
资源管理:在需要管理共享资源的系统中,可以使用代理模式来创建资源管理代理,以控制对资源的访问,实现资源的共享和保护。
-
缓存管理:在需要缓存数据以提高性能的系统中,可以使用代理模式来创建缓存代理,以控制对数据的访问,实现数据的缓存和更新策略。
-
权限管理:在需要对用户权限进行管理的系统中,可以使用代理模式来创建权限管理代理,以控制对资源的访问,实现对用户权限的控制和管理。
示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 定义抽象主题接口
struct File {void (*read)(struct File *);void (*write)(struct File *, const char *);
};// 定义真实主题
struct RealFile {struct File file; // 继承自抽象主题char *name;
};// 真实主题的读操作实现
void real_read(struct File *file) {struct RealFile *realFile = (struct RealFile *)file;printf("读取文件:%s\n", realFile->name);
}// 真实主题的写操作实现
void real_write(struct File *file, const char *data) {struct RealFile *realFile = (struct RealFile *)file;printf("写入文件:%s\n", realFile->name);printf("数据:%s\n", data);
}// 定义代理主题
struct ProxyFile {struct File file; // 继承自抽象主题struct RealFile *realFile;
};// 代理主题的读操作实现
void proxy_read(struct File *file) {struct ProxyFile *proxyFile = (struct ProxyFile *)file;printf("代理请求读取...\n");proxyFile->realFile->file.read(&(proxyFile->realFile->file));
}// 代理主题的写操作实现
void proxy_write(struct File *file, const char *data) {struct ProxyFile *proxyFile = (struct ProxyFile *)file;printf("代理请求写入...\n");proxyFile->realFile->file.write(&(proxyFile->realFile->file), data);
}int main() {// 创建真实文件对象struct RealFile *realFile = malloc(sizeof(struct RealFile));realFile->name = strdup("example.txt");realFile->file.read = real_read;realFile->file.write = real_write;// 创建文件代理对象struct ProxyFile *proxyFile = malloc(sizeof(struct ProxyFile));proxyFile->realFile = realFile;proxyFile->file.read = proxy_read;proxyFile->file.write = proxy_write;// 使用代理对象读写文件proxyFile->file.read(&(proxyFile->file));proxyFile->file.write(&(proxyFile->file), "Hello, Proxy Pattern!");// 释放内存free(realFile->name);free(realFile);free(proxyFile);return 0;
}
- 输出结果
代理请求读取...
读取文件:example.txt
代理请求写入...
写入文件:example.txt
数据:Hello, Proxy Pattern!
门面模式
C语言中,门面模式(Facade Pattern)是一种常见的设计模式,用于提供一个统一的接口,以简化一组复杂系统的使用。门面模式隐藏了系统的复杂性,为客户端提供了一个更简单的接口,从而使客户端不需要了解系统的内部实现细节。
-
门面(Facade):提供了一个简单的接口,用于将客户端的请求委派给系统内部的各个子系统。
-
子系统(Subsystems):实现了系统的各个功能,但是这些功能对客户端来说可能过于复杂。门面模式通过封装这些子系统,提供了一个更简单的接口供客户端使用。
场景
-
复杂系统的简化接口:当一个系统由多个子系统组成,而客户端需要使用的功能较少或者不需要了解系统内部的复杂结构时,门面模式可以提供一个简化的接口,隐藏系统的复杂性,使得客户端更容易使用。
-
库或框架的封装:当开发一个库或框架时,为了提供更加友好的接口给用户使用,可以使用门面模式来封装库或框架的内部实现细节,使得用户只需调用简单的接口即可完成复杂的操作。
-
遗留系统的接口升级:当需要对一个遗留系统进行接口升级或者重构时,可以使用门面模式来封装原有的接口,提供一个向后兼容的接口给客户端使用,从而减少对客户端的影响。
-
跨平台开发:当需要在不同平台上开发应用程序时,可以使用门面模式来封装不同平台的差异,提供一个统一的接口给客户端使用,从而简化开发工作。
-
系统的分层设计:当一个系统被分为多个层次,每个层次都有不同的责任时,可以使用门面模式来定义每个层次的门面,使得每个层次都可以独立于其他层次进行开发和测试。
示例
#include <stdio.h>// 子系统1:CPU
struct CPU {void (*run)(struct CPU *);
};// 子系统1的操作实现
void cpu_run(struct CPU *cpu) {printf("CPU开始运行\n");
}// 子系统2:内存
struct Memory {void (*load)(struct Memory *);
};// 子系统2的操作实现
void memory_load(struct Memory *memory) {printf("内存加载数据\n");
}// 子系统3:硬盘
struct HardDrive {void (*read)(struct HardDrive *);
};// 子系统3的操作实现
void harddrive_read(struct HardDrive *hardDrive) {printf("硬盘读取数据\n");
}// 门面:计算机
struct Computer {struct CPU cpu;struct Memory memory;struct HardDrive hardDrive;void (*start)(struct Computer *);
};// 门面的操作实现
void computer_start(struct Computer *computer) {printf("计算机启动中...\n");// 启动计算机时,依次启动CPU、内存和硬盘computer->cpu.run(&(computer->cpu));computer->memory.load(&(computer->memory));computer->hardDrive.read(&(computer->hardDrive));printf("计算机启动完成\n");
}int main() {// 创建计算机对象struct Computer computer;// 初始化计算机对象的各个子系统computer.cpu.run = cpu_run;computer.memory.load = memory_load;computer.hardDrive.read = harddrive_read;// 初始化计算机对象的启动函数computer.start = computer_start;// 使用门面模式启动计算机computer.start(&computer);return 0;
}
- 输出结果
计算机启动中...
CPU开始运行
内存加载数据
硬盘读取数据
计算机启动完成
桥接模式
C语言中,桥接模式(Bridge Pattern)是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立变化。桥接模式通过将抽象部分与实现部分进行解耦,允许它们可以独立地进行变化,从而提高了系统的灵活性和可扩展性。
-
抽象部分(Abstraction):定义了抽象类接口,维护一个指向实现部分的引用。
-
扩展抽象部分(Refined Abstraction):继承自抽象部分,扩展了抽象类接口,增加了新的功能或行为。
-
实现部分(Implementor):定义了实现类接口,提供了实现类的基本操作。
-
具体实现部分(Concrete Implementor):实现了实现部分接口的具体类。
场景
-
多维度变化的系统:当一个系统有多个维度的变化,而且这些维度都需要独立变化时,可以使用桥接模式将不同维度的变化进行解耦。例如,一个图形界面系统中,可能有多种不同的绘制方式和多种不同的控件类型,可以使用桥接模式将不同的绘制方式和控件类型进行解耦,使得系统更加灵活。
-
运行时切换实现:当一个系统需要在运行时动态地选择不同的实现时,可以使用桥接模式。例如,一个网络通信库可能需要支持多种不同的网络协议,可以使用桥接模式将不同的网络协议的实现进行解耦,使得系统可以在运行时动态地选择不同的网络协议。
-
多平台支持:当一个系统需要支持多个不同的平台时,可以使用桥接模式将不同平台的实现进行解耦。例如,一个图形库可能需要支持在Windows、Linux和macOS等多个平台上运行,可以使用桥接模式将不同平台的图形接口进行解耦,使得系统更易于扩展和维护。
-
数据库驱动程序:在数据库访问层中,不同的数据库可能有不同的实现方式,可以使用桥接模式将不同数据库的实现进行解耦,使得系统可以在运行时动态地选择不同的数据库实现。
示例
#include <stdio.h>// 实现部分接口
struct Implementor {void (*operationImpl)(void); // 操作实现函数指针
};// 具体实现部分A
void operationImplA() {printf("具体实现部分A的操作\n");
}// 具体实现部分B
void operationImplB() {printf("具体实现部分B的操作\n");
}// 抽象部分
struct Abstraction {struct Implementor *impl; // 实现部分指针void (*operation)(struct Abstraction *); // 操作函数
};// 扩展抽象部分
void operation(struct Abstraction *abstraction) {abstraction->impl->operationImpl(); // 调用实现部分的操作
}int main() {// 创建具体实现部分A的对象struct Implementor implA;implA.operationImpl = operationImplA;// 创建具体实现部分B的对象struct Implementor implB;implB.operationImpl = operationImplB;// 创建抽象部分对象,关联具体实现部分Astruct Abstraction abstractionA;abstractionA.impl = &implA;abstractionA.operation = operation;// 调用抽象部分的操作,实际执行具体实现部分A的操作abstractionA.operation(&abstractionA);// 创建抽象部分对象,关联具体实现部分Bstruct Abstraction abstractionB;abstractionB.impl = &implB;abstractionB.operation = operation;// 调用抽象部分的操作,实际执行具体实现部分B的操作abstractionB.operation(&abstractionB);return 0;
}
- 输出结果
具体实现部分A的操作
具体实现部分B的操作
适配器模式
C语言中,适配器模式(Adapter Pattern)是一种结构型设计模式,它允许接口不兼容的类能够一起工作。适配器模式通过将一个类的接口转换成客户端所期望的另一个接口,从而使得原本由于接口不兼容而不能一起工作的类可以一起工作。
-
目标接口(Target):客户端期望的接口,适配器模式通过适配器将被适配者的接口转换成目标接口。
-
被适配者(Adaptee):需要被适配的类,它的接口与目标接口不兼容。
-
适配器(Adapter):实现了目标接口,并包含一个被适配者的对象,它将客户端的请求转发给被适配者,并进行适配。
场景
-
使用现有库或框架:当需要使用一个已经存在的库或框架,但它的接口与系统的其他部分不兼容时,可以使用适配器模式将它的接口转换成系统所期望的接口。
-
跨平台开发:在跨平台开发中,不同平台上的API可能存在差异,导致代码无法跨平台使用。可以使用适配器模式将不同平台上的API接口进行统一,使得代码可以跨平台使用。
-
日志系统的适配:当系统需要更换日志系统时,可以使用适配器模式将新的日志系统的接口适配成原有日志系统的接口,使得系统可以无缝切换日志系统而不需要修改大量代码。
-
数据库访问的适配:当系统需要支持多种不同的数据库时,可以使用适配器模式将不同数据库的访问接口适配成统一的接口,使得系统可以使用相同的代码访问不同的数据库。
-
设备驱动的适配:当系统需要支持多种不同的硬件设备时,可以使用适配器模式将不同设备的驱动接口适配成统一的接口,使得系统可以使用相同的代码操作不同的设备。
示例
#include <stdio.h>// 目标接口
struct Target {void (*request)(struct Target *);
};// 被适配者
struct Adaptee {void (*specificRequest)(struct Adaptee *);
};// 被适配者的特定请求实现
void specificRequest(struct Adaptee *adaptee) {printf("被适配者的特定请求\n");
}// 适配器
struct Adapter {struct Target target; // 目标接口struct Adaptee adaptee; // 被适配者
};// 适配器的请求实现
void request(struct Target *target) {// 将目标接口的请求转发给被适配者的特定请求struct Adapter *adapter = (struct Adapter *)target;adapter->adaptee.specificRequest(&(adapter->adaptee));
}int main() {// 创建被适配者对象struct Adaptee adaptee;adaptee.specificRequest = specificRequest;// 创建适配器对象struct Adapter adapter;adapter.target.request = request;adapter.adaptee = adaptee;// 使用适配器调用目标接口adapter.target.request(&(adapter.target));return 0;
}
- 输出结果
被适配者的特定请求
外观模式
外观模式(Facade Pattern)是一种结构型设计模式,它提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,使得子系统更容易使用。
在C语言中,外观模式可以帮助简化复杂系统的使用,隐藏系统的复杂性,提供一个更加友好和简单的接口给客户端使用。外观模式通常包含以下几个角色:
-
外观(Facade):提供了一个简单的接口,用来访问子系统中的一群接口。外观模式通过将客户端的请求委派给子系统中的各个对象来实现这个接口。
-
子系统(Subsystems):实现了系统的各个功能,但是这些功能对客户端来说可能过于复杂。外观模式通过封装这些子系统,提供了一个更简单的接口供客户端使用。
场景
-
复杂系统的简化接口:当一个系统由多个子系统组成,而客户端只需使用其中的一部分功能时,可以使用外观模式将这些子系统的复杂功能进行封装,提供一个简化的接口给客户端使用。
-
隐藏系统的复杂性:当一个系统的内部结构复杂,而客户端不需要了解系统的内部实现细节时,可以使用外观模式隐藏系统的复杂性,提供一个更加友好和简单的接口给客户端使用。
-
简化接口的调用流程:当一个系统的接口调用流程比较繁琐,需要依次调用多个接口时,可以使用外观模式将这些接口调用流程进行封装,提供一个更简单和直观的接口给客户端使用。
-
封装遗留系统:当一个系统的接口需要向后兼容或者需要与其他系统进行集成时,可以使用外观模式封装遗留系统的接口,提供一个统一的接口给客户端使用,从而降低系统的耦合度。
-
统一接口规范:当一个系统需要支持多种不同的接口规范时,可以使用外观模式将这些不同的接口规范进行统一,提供一个统一的接口给客户端使用,使得客户端可以无需关心不同接口规范的细节。
示例
#include <stdio.h>// 子系统1:CPU
struct CPU {void (*run)(void); // 运行方法
};// 子系统1的具体实现
void cpu_run() {printf("CPU开始运行\n");
}// 子系统2:内存
struct Memory {void (*load)(void); // 加载方法
};// 子系统2的具体实现
void memory_load() {printf("内存加载数据\n");
}// 子系统3:硬盘
struct HardDrive {void (*read)(void); // 读取方法
};// 子系统3的具体实现
void harddrive_read() {printf("硬盘读取数据\n");
}// 外观:计算机
struct ComputerFacade {struct CPU cpu;struct Memory memory;struct HardDrive hardDrive;void (*start)(void); // 启动方法
};// 外观的实现
void start() {printf("计算机启动中...\n");// 启动计算机时,依次启动CPU、内存和硬盘cpu_run();memory_load();harddrive_read();printf("计算机启动完成\n");
}int main() {// 创建外观对象struct ComputerFacade computerFacade;// 初始化外观对象的各个子系统computerFacade.cpu.run = cpu_run;computerFacade.memory.load = memory_load;computerFacade.hardDrive.read = harddrive_read;// 初始化外观对象的启动方法computerFacade.start = start;// 使用外观模式启动计算机computerFacade.start();return 0;
}
- 输出结果
计算机启动中...
CPU开始运行
内存加载数据
硬盘读取数据
计算机启动完成
享元模式
享元模式(Flyweight Pattern)是一种结构型设计模式,它旨在通过共享对象来最大限度地减少内存使用和提高性能。在享元模式中,将对象的状态分为内部状态(Intrinsic State)和外部状态(Extrinsic State),其中内部状态可以被多个对象共享,而外部状态需要根据场景而变化。
-
享元工厂(Flyweight Factory):负责创建和管理享元对象,通过共享已创建的享元对象来减少对象的创建数量。
-
享元接口(Flyweight Interface):定义了享元对象的接口,包括设置外部状态的方法等。
-
具体享元对象(Concrete Flyweight):实现了享元接口,包含了内部状态,并对外部状态进行了处理。
-
客户端(Client):通过享元工厂获取享元对象,并设置外部状态,实现具体的业务逻辑。
场景
-
大量对象的共享:当系统中存在大量相似对象,并且这些对象可以共享部分状态时,可以使用享元模式来减少对象的创建数量,降低内存消耗。
-
对象的状态可分为内部状态和外部状态:当一个对象的状态可以分为内部状态(Intrinsic State)和外部状态(Extrinsic State),并且内部状态可以被多个对象共享时,可以使用享元模式来共享内部状态,减少对象的创建数量。
-
需要缓存对象的场景:当系统需要缓存一些常用的对象,并且这些对象可以共享部分状态时,可以使用享元模式来缓存这些对象,提高系统的性能。
-
系统需要保持独立的对象:当系统需要保持独立的对象,但又希望共享相同的状态时,可以使用享元模式来共享这些状态,同时保持对象的独立性。
-
需要减少系统中对象的数量:当系统中存在大量相似对象,并且这些对象可以共享部分状态时,可以使用享元模式来减少对象的创建数量,提高系统的性能和资源利用率。
示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 享元对象结构体
typedef struct {char *name; // 内部状态int size; // 外部状态
} Flyweight;// 享元工厂
Flyweight *get_flyweight(char *name) {static Flyweight *flyweights[10] = {NULL}; // 预先创建的享元对象数组static int index = 0;// 查找现有的享元对象for (int i = 0; i < index; i++) {if (strcmp(flyweights[i]->name, name) == 0) {return flyweights[i];}}// 创建新的享元对象Flyweight *flyweight = (Flyweight *)malloc(sizeof(Flyweight));flyweight->name = strdup(name);flyweights[index++] = flyweight;return flyweight;
}// 客户端使用享元对象
void client(char *name, int size) {Flyweight *flyweight = get_flyweight(name);printf("对象名:%s,大小:%d\n", flyweight->name, size);
}int main() {// 客户端使用享元对象client("object1", 10);client("object2", 20);client("object1", 30); // 复用现有的享元对象client("object3", 15);client("object2", 25); // 复用现有的享元对象return 0;
}
- 输出结果
对象名:object1,大小:10
对象名:object2,大小:20
对象名:object1,大小:30
对象名:object3,大小:15
对象名:object2,大小:25
装饰器模式
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许向现有对象动态地添加新功能,同时又不改变其结构。装饰器模式通过创建一个包装器(Wrapper),将被装饰的对象进行包装,从而可以在运行时动态地添加新的功能。
-
组件接口(Component Interface):定义了被装饰对象和装饰器共同实现的接口。
-
具体组件(Concrete Component):实现了组件接口的具体对象,它是被装饰的对象。
-
装饰器(Decorator):实现了组件接口,并包含了一个指向被装饰对象的指针,它可以动态地添加新的功能。
-
具体装饰器(Concrete Decorator):实现了装饰器接口的具体对象,用于添加新的功能。
场景
-
动态添加功能:当需要在不修改对象的基本结构的情况下,动态地添加额外的功能时,可以使用装饰器模式。这样可以避免修改现有的代码,使得系统更加灵活和可扩展。
-
多个功能的组合:当需要对对象的功能进行组合和排列,以满足不同的需求时,可以使用装饰器模式。通过组合不同的装饰器,可以实现不同的功能组合,而无需创建大量的子类。
-
功能的分层扩展:当需要对对象的功能进行分层扩展,以便于复用和维护时,可以使用装饰器模式。通过创建不同层次的装饰器,可以实现对对象功能的分层扩展,从而使得系统更易于理解和维护。
-
功能的独立扩展:当需要对对象的部分功能进行独立扩展和修改时,可以使用装饰器模式。通过创建独立的装饰器,可以实现对对象的部分功能进行扩展,而不影响其他功能的使用。
-
动态改变对象的行为:当需要动态地改变对象的行为,以适应不同的场景时,可以使用装饰器模式。通过在运行时动态地装饰对象,可以实现对对象行为的动态改变,从而提高系统的灵活性和可定制性。
示例
#include <stdio.h>// 组件接口
typedef struct {void (*operation)(void);
} Component;// 具体组件
typedef struct {Component component;
} ConcreteComponent;// 具体组件的操作实现
void concrete_operation(void) {printf("执行具体组件的操作\n");
}// 装饰器
typedef struct {Component component;void (*decorate)(Component *);
} Decorator;// 具体装饰器1
typedef struct {Decorator decorator;
} ConcreteDecorator1;// 具体装饰器1的装饰方法
void decorate1(Component *component) {printf("在具体装饰器1中添加功能1\n");component->operation();
}// 具体装饰器2
typedef struct {Decorator decorator;
} ConcreteDecorator2;// 具体装饰器2的装饰方法
void decorate2(Component *component) {printf("在具体装饰器2中添加功能2\n");component->operation();
}int main() {// 创建具体组件ConcreteComponent concreteComponent;concreteComponent.component.operation = concrete_operation;// 创建具体装饰器1,并装饰具体组件ConcreteDecorator1 concreteDecorator1;concreteDecorator1.decorator.component = concreteComponent.component;concreteDecorator1.decorator.decorate = decorate1;concreteDecorator1.decorator.decorate(&(concreteDecorator1.decorator.component));// 创建具体装饰器2,并装饰具体组件ConcreteDecorator2 concreteDecorator2;concreteDecorator2.decorator.component = concreteComponent.component;concreteDecorator2.decorator.decorate = decorate2;concreteDecorator2.decorator.decorate(&(concreteDecorator2.decorator.component));return 0;
}
- 输出结果
在具体装饰器1中添加功能1
执行具体组件的操作
在具体装饰器2中添加功能2
执行具体组件的操作
组合模式
组合模式(Composite Pattern)是一种结构型设计模式,它允许将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式让客户端以统一的方式处理单个对象以及对象组合,从而使得客户端无需区分对象和对象组合。
-
组件接口(Component Interface):定义了组合中所有对象共同的接口,以便于客户端统一处理对象和对象组合。
-
叶子组件(Leaf Component):表示组合中的叶子节点,它实现了组件接口,但是没有子节点。
-
复合组件(Composite Component):表示组合中的复合节点,它实现了组件接口,并且包含了子节点。
场景
-
文件系统:文件系统中的目录和文件可以使用组合模式来表示。目录可以看作是复合组件,它包含了多个文件或目录(叶子组件和复合组件),而文件可以看作是叶子组件,它不包含其他文件或目录。
-
图形用户界面(GUI):图形用户界面中的控件和容器可以使用组合模式来表示。控件可以看作是叶子组件,它显示具体的内容或功能,而容器可以看作是复合组件,它包含了多个控件(叶子组件和复合组件)。
-
组织架构:组织架构中的部门和员工可以使用组合模式来表示。部门可以看作是复合组件,它包含了多个员工(叶子组件和复合组件),而员工可以看作是叶子组件,它不包含其他员工。
-
菜单系统:菜单系统中的菜单项和菜单可以使用组合模式来表示。菜单项可以看作是叶子组件,它表示一个具体的功能或选项,而菜单可以看作是复合组件,它包含了多个菜单项(叶子组件和复合组件)。
-
物品组织:在游戏开发中,物品的组织结构可以使用组合模式来表示。物品可以看作是叶子组件,它表示一个具体的物品,而物品箱或背包可以看作是复合组件,它包含了多个物品(叶子组件和复合组件)。
示例
#include <stdio.h>// 定义组件类型
typedef enum {LEAF, // 叶子节点COMPOSITE // 复合节点
} NodeType;// 定义组件结构体
typedef struct Node {NodeType type; // 节点类型:叶子节点或复合节点char *name; // 节点名称union {struct Node *child; // 叶子节点指向NULL,复合节点指向子节点链表的头节点struct Node *next; // 复合节点链表中的下一个节点} next;
} Node;// 打印节点名称
void print_node(Node *node) {printf("%s\n", node->name);
}// 打印树节点
void print_tree(Node *root, int depth) {if (root == NULL) return;// 打印节点名称for (int i = 0; i < depth; ++i) {printf(" ");}print_node(root);// 递归打印子节点if (root->type == COMPOSITE) {print_tree(root->next.child, depth + 1);}// 递归打印兄弟节点print_tree(root->next.next, depth);
}int main() {// 创建根节点Node root = {COMPOSITE, "根节点", .next = {NULL}};// 创建子节点Node node1 = {LEAF, "叶子节点1", .next = {NULL}};Node node2 = {LEAF, "叶子节点2", .next = {NULL}};// 创建复合节点Node composite_node = {COMPOSITE, "复合节点", .next = {.child = &node1}};// 将复合节点添加到根节点root.next.next = &composite_node;composite_node.next.next = &node2;// 打印树节点print_tree(&root, 0);return 0;
}
- 输出结果
根节点复合节点叶子节点2叶子节点2
复合节点叶子节点2
叶子节点2
相关文章:
C 语言设计模式(结构型)
文章目录 代理模式场景示例 门面模式场景示例 桥接模式场景示例 适配器模式场景示例 外观模式场景示例 享元模式场景示例 装饰器模式场景示例 组合模式场景示例 代理模式 C语言中,代理模式通常用于实现对象的间接访问。代理模式是一种结构型设计模式,它…...

【云原生--K8S】K8S python接口研究
文章目录 前言一、搭建ubuntu运行环境1.运行ubuntu容器2.拷贝kubeconfig文件二、python程序获取k8s信息1.获取node信息2.获取svc信息3.常用kubernetes API总结前言 在前面的文章中我们都是通过kubectl命令行来访问操作K8S,但是在实际应用中可能需要提供更方便操作的图形化界面…...
5.26作业
服务器 2 3 #define BUFSIZE 10244 #define login_msg_len 205 6 typedef struct Node{7 char name[login_msg_len];8 struct sockaddr_in addr;9 struct Node *next;10 }Node;11 12 typedef struct Msgtype{13 char type;14 char username[login_msg_len]…...
链接库文件体积优化工具篇:bloaty
笔者之前参与过一个嵌入式智能手表项目,曾经碰到过这样一个问题:手表的flash大小只有2M,这意味着只能在上面烧录2M大小的代码。随着开发不断进行,代码越写越多,编译出来的bin也越来越大。最后bin大小超过了2M, 就没法烧…...

使用pyqt绘制一个爱心!
使用pyqt绘制一个爱心! 介绍效果代码 介绍 使用pyqt绘制一个爱心! 效果 代码 import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget from PyQt5.QtGui import QPainter, QPen, QBrush, QColor from PyQt5.QtCore import Qt, Q…...
关于 Transformer 的11个常见面试题
Transformer 是如何工作的? Transformer 是一种深度学习算法,特别适用于自然语言处理(NLP)任务,如语言翻译、语言生成和语言理解。它们能够处理长度可变的输入序列并捕捉长距离依赖关系,使其在理解和处理自…...
OS多核多线程锁记录笔记
自旋锁作用 自旋锁的是为了保护两个核上的公共资源,也就是全局变量,只有在一方也就是一个核抢到了自选锁,才能对公共资源进行操作修改,当然还有其他形似的锁如互斥锁,这里不比较两者的区别,以前没有深入的去…...
nginx做TCP代理
要实现TCP代理,可以使用Nginx的stream模块。stream模块允许Nginx作为一个转发代理来处理TCP流量,包括TCP代理、负载均衡和SSL终止等功能。 以下是配置Nginx实现TCP代理的基本步骤: 在Nginx配置文件中添加stream块,并在该块中配置…...
python 异常处理 try
异常 我们常见的代码错误后 会出现此类异常 SyntaxError:语法错误 AttributeError:属性错误 IndexError:索引错误 TypeError:类型错误 NameError:变量名不存在错误 KeyError:映射中不存在的关键字…...
月入10万+管道收益,揭秘旅游卡运营的5个阶段!
网上的项目众多,只要用心,便能发现不少商机。在互联网上运营,关键在于理解项目的底层逻辑。今天,我们来揭秘旅游卡项目,如何做到月入10万。 1、先赚成本 开始项目时,首要任务是回本。不要急于求成&#x…...

android_binder源码分析之_binder驱动使用服务
一,binder驱动源码分析,使用服务过程 uint32_t svcmgr_lookup(struct binder_state *bs, uint32_t target, const char *name) {uint32_t handle;unsigned iodata[512/4];struct binder_io msg, reply;bio_init(&msg, iodata, sizeof(iodata), 4);b…...
【波点音乐看广告】
import uiautomator2 as u2 import time from datetime import datetime import xml.etree.ElementTree as ET import re import os 连接设备 d u2.connect() os.system(‘adb shell chmod 775 /data/local/tmp/atx-agent’) os.system(‘adb shell /data/local/tmp/atx-age…...

[SWPUCTF 2021 新生赛]pop
常见的魔术方法 魔术方法__construct() 类的构造函数,在对象实例化时调用 __destruct() 类的析构函数,在对象被销毁时被调用 __call() 在对象中调用一个不可访问的对象时被调用,比如一个对象被调用时,里面没有程序想调用的属性 …...

【DevOps】Jenkins + Dockerfile自动部署Maven(SpringBoot)项目
环境 docker_host192.168.0.1jenkins_host192.168.0.2 jenkins_host构建完成后把jar发布到docker_host,再通过dockerfile自动构建镜像,运行镜像 1 Jenkins安装 AWS EC2安装Jenkins:AWS EC2 JDK11 Jenkins-CSDN博客 AWS EC2上Docker安装…...

【C++】——入门基础知识超详解
目录 编辑 1.C关键字 2. 命名空间 2.1 命名空间定义 2.2 命名空间使用 命名空间的使用有三种方式: 注意事项 3. C输入&输出 示例 1:基本输入输出 示例 2:读取多个值 示例 3:处理字符串输入 示例 4:读…...
ChatGPT技术演进简介
chatGPT(chat generative pre-train transformer, 可以对话的预训练trasformer模型),讨论点: 1、chatGPT为什么突然火了 2、GPT 1.0、2.0、3.0、3.5 、4和4o区别和特性,在不同应用场景中如何选对模型 3、未…...

C语言 | Leetcode C语言题解之第114题二叉树展开为链表
题目: 题解: void flatten(struct TreeNode* root) {struct TreeNode* curr root;while (curr ! NULL) {if (curr->left ! NULL) {struct TreeNode* next curr->left;struct TreeNode* predecessor next;while (predecessor->right ! NULL)…...
Vue 子组件向父组件传值
1、使用自定义事件 ($emit) 这是Vue中最常用的子组件向父组件传递数据的方式。子组件通过触发一个自定义事件,并附加数据作为参数,父组件则监听这个事件并处理传递过来的数据。 子组件 (发送数据): <template><button click"…...

【前端笔记】Vue项目报错Error: Cannot find module ‘webpack/lib/RuleSet‘
网上搜了下发现原因不止一种,这里仅记录本人遇到的原因和解决办法,仅供参考 原因:因为某种原因导致本地package.json中vue/cli与全局vue/cli版本不同导致冲突。再次提示,这是本人遇到的,可能和大家有所不同,…...

edge浏览器的网页复制
一些网页往往禁止复制粘贴,本文方法如下: 网址最前面加上 read: (此方法适用于Microsoft Edge 浏览器)在此网站网址前加上read:进入阅读器模式即可...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...

分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...