创建型模式:装饰器模式(C语言实战指南)

做C语言开发或嵌入式开发的同学,大概率都遇到过这类棘手场景:项目里的通信模块、数据处理模块已经过严格测试,功能稳定上线,但后续迭代要新增额外职责------比如给通信数据加日志便于调试、对传输内容做加密保障安全、给数据处理结果加校验确保可靠。这时候改原有代码吧,容易引入新bug,还违反"对扩展开放、对修改关闭"的开闭原则;想用继承扩展吧,C语言本身不支持类继承,靠结构体嵌套模拟又会导致代码冗余、灵活性极差。其实这时候,装饰器模式就能派上大用场!它能在不改动原有代码结构的前提下,动态给"对象"附加额外职责。今天就从C语言实战角度,把装饰器模式的原理、实现、实战场景讲透,附可直接移植的代码示例,帮你轻松搞定功能扩展难题!

一、原理拆解:装饰器模式的核心逻辑

先抛开晦涩的设计模式术语,用通俗的例子讲明白装饰器模式:装饰器模式就像给开发板加外设模块------开发板本身(原有对象)的核心功能是运行控制逻辑,我们可以动态给它接日志模块(装饰器1,新增调试日志功能)、加密模块(装饰器2,新增数据加密功能)、校验模块(装饰器3,新增数据校验功能),这些外设不改变开发板本身的硬件结构和核心控制逻辑,还能灵活组合、增减,适配不同场景需求

对应到C语言开发中,装饰器模式的核心思想可以提炼为:通过"结构体嵌套被装饰对象指针"的组合方式,重写核心功能的函数指针,在完整保留原有功能的基础上,无缝附加额外职责。它的核心价值主要体现在两点:① 无侵入扩展:完全不修改原有模块的代码,避免破坏经过验证的稳定功能,降低迭代风险;② 动态灵活性:支持给同一个对象动态叠加多个装饰器,也能根据场景灵活移除,组合方式多样,比模拟继承的扩展方式灵活得多。

装饰器模式的三大核心角色(适配C语言实现):

  1. 抽象组件(Component):定义核心功能的统一接口,通常是一个包含函数指针的结构体。这是原有模块和所有装饰器的"通用规范",确保后续扩展时接口一致。比如通信模块的"发送数据""接收数据"核心接口,就可以定义在抽象组件中。

  2. 具体组件(ConcreteComponent):抽象组件的实际实现,也就是项目中已有的稳定功能模块。比如实际的串口通信模块、TCP通信模块,它们具体实现了抽象组件中定义的发送、接收核心逻辑,是被装饰的"核心对象"。

  3. 装饰器(Decorator):核心是包含一个抽象组件的指针(这个指针既可以指向具体组件,也可以指向其他装饰器,支持嵌套),同时严格实现抽象组件的接口。在接口实现中,会先调用被装饰对象的原有核心功能,再执行自己的额外职责(比如打印日志、数据加密)。像日志装饰器、加密装饰器,都属于这类角色。

二、工程化分析:装饰器模式的工程价值与C语言适配思路

在C语言底层开发和嵌入式开发场景中,装饰器模式的工程价值尤为突出,核心体现在"功能扩展的灵活性"和"现有代码的稳定性保护"两大方面。尤其是在长期维护的工业级项目中,原有模块经过多轮测试和现场验证,稳定性至关重要,这时候用装饰器模式新增功能,能最大程度降低对现有系统的影响,避免引入潜在bug。

C语言没有类和继承,实现装饰器模式的核心适配思路是:

  1. 用"结构体+函数指针"模拟抽象接口:这是C语言实现装饰器模式的基础。通过结构体封装核心功能的函数指针,定义统一的调用规范,确保具体组件和所有装饰器都遵循同一套接口,这样才能实现装饰器的嵌套叠加(一个装饰器装饰另一个装饰器)。

  2. 用"结构体组合"替代继承:C语言没有类继承机制,我们用"装饰器结构体嵌套抽象组件指针"的组合方式替代。通过这个指针,装饰器可以调用被装饰对象的原有功能,实现"动态附加"职责的效果,比模拟继承更灵活、内存开销更小。

  3. 重写函数指针实现功能扩展:装饰器需要严格实现抽象组件的函数指针,在实现逻辑中,先通过嵌套的指针调用被装饰对象的核心功能,再执行自己的额外职责(比如日志打印、数据加密)。这样既保证原有功能不缺失,又能无缝叠加新功能。

关键对比:装饰器模式 vs 继承(C语言模拟)

特性 装饰器模式(组合) 继承(C语言结构体嵌套模拟)
扩展方式 动态组合,可灵活增减、叠加功能 静态扩展,编译时固定,无法动态调整
代码侵入性 无侵入,不修改原有代码,风险低 有侵入,需修改基类或派生类,易破坏稳定功能
灵活性 高,支持多装饰器任意组合 低,继承层级越深,代码越冗余,扩展越受限
内存开销 较小,仅新增指针和少量逻辑 较大,嵌套结构体易产生冗余数据
适用场景 动态扩展多个独立职责(如日志+加密) 固定功能扩展,后续无变更需求

工程结论:在嵌入式开发中,装饰器模式远比模拟继承更适合功能扩展。尤其是需要给同一个模块动态叠加多个独立职责(比如给通信模块同时加日志、加密、校验)的场景,装饰器模式的灵活性和低风险性优势会更加明显。

三、C语言实现:装饰器模式的通用框架

下面以嵌入式开发中最常见的"通信模块功能扩展"为场景,实现装饰器模式的通用框架。这个框架包含抽象组件(通信接口)、具体组件(串口通信模块)、两个实用装饰器(日志装饰器、加密装饰器),支持装饰器嵌套叠加,代码可直接移植到实际项目中。

1. 定义抽象组件(统一通信接口)

c 复制代码
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>  // 补充malloc/free所需头文件

// 抽象组件:通信模块统一接口(核心功能:发送、接收数据)
// 所有具体通信模块和装饰器都需遵循此接口
typedef struct {
    // 发送数据:data-待发送数据缓冲区,len-数据长度;返回0成功,-1失败
    int (*send)(void *self, const uint8_t *data, uint16_t len);
    // 接收数据:buf-接收缓冲区,len-缓冲区最大长度;返回实际接收数据长度
    int (*recv)(void *self, uint8_t *buf, uint16_t len);
} CommunicationComponent;

2. 实现具体组件(原有串口通信模块)

这是项目中已有的稳定功能模块,也是我们要装饰的"核心对象"。实际开发中,这类模块通常已经过充分测试,我们完全不修改其代码,仅通过装饰器附加新功能。

c 复制代码
// 具体组件:串口通信模块(原有稳定功能)
typedef struct {
    CommunicationComponent base;  // 嵌套抽象组件,遵循统一接口
    uint32_t baudrate;            // 串口特有属性:波特率
} SerialCommunication;

// 串口发送核心实现(原有功能,不修改)
static int serial_send(void *self, const uint8_t *data, uint16_t len) {
    if (self == NULL || data == NULL || len == 0) {
        return -1;  // 参数错误返回-1
    }
    SerialCommunication *serial = (SerialCommunication *)self;
    // 模拟串口发送逻辑(实际项目中需操作串口寄存器/外设驱动)
    printf("[串口发送] 波特率:%d, 数据:", serial->baudrate);
    for (int i = 0; i < len; i++) {
        printf("%02X ", data[i]);  // 十六进制打印发送数据
    }
    printf("\n");
    return 0;  // 发送成功返回0
}

// 串口接收核心实现(原有功能,不修改)
static int serial_recv(void *self, uint8_t *buf, uint16_t len) {
    if (self == NULL || buf == NULL || len == 0) {
        return 0;  // 参数错误返回0(无数据接收)
    }
    // 模拟接收数据(实际项目中需读取串口接收缓冲区)
    uint8_t mock_data[] = {0x12, 0x34, 0x56};  // 模拟接收的3字节数据
    uint16_t copy_len = (len < sizeof(mock_data)) ? len : sizeof(mock_data);
    memcpy(buf, mock_data, copy_len);  // 拷贝模拟数据到接收缓冲区
    
    // 打印接收数据(原有功能)
    printf("[串口接收] 数据:");
    for (int i = 0; i < copy_len; i++) {
        printf("%02X ", buf[i]);
    }
    printf("\n");
    return copy_len;  // 返回实际接收数据长度
}

// 串口模块初始化(原有接口,不修改)
SerialCommunication* serial_comm_init(uint32_t baudrate) {
    // 动态分配内存(实际嵌入式场景可替换为静态内存)
    SerialCommunication *serial = (SerialCommunication *)malloc(sizeof(SerialCommunication));
    if (serial == NULL) {
        return NULL;  // 内存分配失败返回NULL
    }
    // 绑定抽象组件的函数指针,实现接口
    serial->base.send = serial_send;
    serial->base.recv = serial_recv;
    serial->baudrate = baudrate;  // 初始化波特率
    return serial;
}

// 串口模块销毁(原有接口,不修改)
void serial_comm_destroy(SerialCommunication *serial) {
    if (serial != NULL) {
        free(serial);  // 释放动态分配的内存
    }
}

3. 实现装饰器(日志装饰器+加密装饰器)

装饰器的核心设计思路是"嵌套抽象组件指针+实现统一接口":通过指针关联被装饰对象,在接口实现中先调用原有功能,再附加新职责。下面实现两个嵌入式开发中最常用的装饰器。

3.1 日志装饰器(添加发送/接收日志)
c 复制代码
// 装饰器1:日志装饰器(给通信添加调试日志功能)
// 作用:打印发送/接收的数据长度,便于调试定位问题
typedef struct {
    CommunicationComponent base;          // 实现统一接口
    CommunicationComponent *component;    // 嵌套被装饰对象的指针(核心)
} LogDecorator;

// 日志装饰器的发送实现:先打印日志,再调用被装饰对象的发送功能
static int log_decorator_send(void *self, const uint8_t *data, uint16_t len) {
    if (self == NULL || data == NULL || len == 0) {
        return -1;
    }
    LogDecorator *decorator = (LogDecorator *)self;
    // 附加职责:打印发送日志(数据长度)
    printf("[日志装饰器] 发送数据长度:%d\n", len);
    // 调用被装饰对象的核心发送功能(保留原有功能)
    return decorator->component->send(decorator->component, data, len);
}

// 日志装饰器的接收实现:先调用被装饰对象的接收功能,再打印日志
static int log_decorator_recv(void *self, uint8_t *buf, uint16_t len) {
    if (self == NULL || buf == NULL || len == 0) {
        return 0;
    }
    LogDecorator *decorator = (LogDecorator *)self;
    // 先调用被装饰对象的核心接收功能
    int recv_len = decorator->component->recv(decorator->component, buf, len);
    // 附加职责:打印接收日志(数据长度)
    printf("[日志装饰器] 接收数据长度:%d\n", recv_len);
    return recv_len;
}

// 日志装饰器初始化:传入被装饰对象(抽象组件指针)
LogDecorator* log_decorator_init(CommunicationComponent *component) {
    if (component == NULL) {
        return NULL;  // 被装饰对象不能为空
    }
    LogDecorator *decorator = (LogDecorator *)malloc(sizeof(LogDecorator));
    if (decorator == NULL) {
        return NULL;  // 内存分配失败
    }
    // 绑定接口函数指针(重写接口,实现扩展)
    decorator->base.send = log_decorator_send;
    decorator->base.recv = log_decorator_recv;
    decorator->component = component;  // 关联被装饰对象
    return decorator;
}

// 日志装饰器销毁
void log_decorator_destroy(LogDecorator *decorator) {
    if (decorator != NULL) {
        free(decorator);  // 释放装饰器内存(注意:不销毁被装饰对象,由外部管理)
    }
}
3.2 加密装饰器(给数据添加简单XOR加密)
c 复制代码
// 装饰器2:加密装饰器(给通信数据添加XOR加密/解密功能)
// 作用:保护传输数据安全,嵌入式场景中常用的轻量级加密方案
typedef struct {
    CommunicationComponent base;          // 实现统一接口
    CommunicationComponent *component;    // 嵌套被装饰对象的指针
    uint8_t key;                          // 加密密钥(XOR对称加密,加密解密用同一密钥)
} EncryptDecorator;

// 辅助函数:XOR加密/解密(对称加密,加密和解密逻辑完全一致)
static void xor_encrypt_decrypt(uint8_t *data, uint16_t len, uint8_t key) {
    for (int i = 0; i < len; i++) {
        data[i] ^= key;  // 异或运算实现加密/解密
    }
}

// 加密装饰器的发送实现:先加密数据,再调用被装饰对象的发送功能
static int encrypt_decorator_send(void *self, const uint8_t *data, uint16_t len) {
    if (self == NULL || data == NULL || len == 0) {
        return -1;
    }
    EncryptDecorator *decorator = (EncryptDecorator *)self;
    // 附加职责:加密数据(先拷贝数据,避免修改原数据)
    uint8_t *encrypt_data = (uint8_t *)malloc(len);
    if (encrypt_data == NULL) {
        return -1;  // 内存分配失败
    }
    memcpy(encrypt_data, data, len);  // 拷贝原始数据
    xor_encrypt_decrypt(encrypt_data, len, decorator->key);  // 加密
    
    // 打印加密后的数据(便于调试)
    printf("[加密装饰器] 加密后数据:");
    for (int i = 0; i < len; i++) {
        printf("%02X ", encrypt_data[i]);
    }
    printf("\n");
    
    // 调用被装饰对象的核心发送功能
    int ret = decorator->component->send(decorator->component, encrypt_data, len);
    free(encrypt_data);  // 释放加密数据缓冲区
    return ret;
}

// 加密装饰器的接收实现:先调用被装饰对象的接收功能,再解密数据
static int encrypt_decorator_recv(void *self, uint8_t *buf, uint16_t len) {
    if (self == NULL || buf == NULL || len == 0) {
        return 0;
    }
    EncryptDecorator *decorator = (EncryptDecorator *)self;
    // 先调用被装饰对象的核心接收功能
    int recv_len = decorator->component->recv(decorator->component, buf, len);
    if (recv_len == 0) {
        return 0;  // 无数据接收,直接返回
    }
    // 附加职责:解密数据
    xor_encrypt_decrypt(buf, recv_len, decorator->key);
    
    // 打印解密后的数据(便于调试)
    printf("[加密装饰器] 解密后数据:");
    for (int i = 0; i < recv_len; i++) {
        printf("%02X ", buf[i]);
    }
    printf("\n");
    
    return recv_len;
}

// 加密装饰器初始化:传入被装饰对象和加密密钥
EncryptDecorator* encrypt_decorator_init(CommunicationComponent *component, uint8_t key) {
    if (component == NULL) {
        return NULL;  // 被装饰对象不能为空
    }
    EncryptDecorator *decorator = (EncryptDecorator *)malloc(sizeof(EncryptDecorator));
    if (decorator == NULL) {
        return NULL;  // 内存分配失败
    }
    // 绑定接口函数指针(重写接口)
    decorator->base.send = encrypt_decorator_send;
    decorator->base.recv = encrypt_decorator_recv;
    decorator->component = component;  // 关联被装饰对象
    decorator->key = key;              // 初始化加密密钥
    return decorator;
}

// 加密装饰器销毁
void encrypt_decorator_destroy(EncryptDecorator *decorator) {
    if (decorator != NULL) {
        free(decorator);  // 释放装饰器内存(不销毁被装饰对象)
    }
}

四、实战验证:装饰器的灵活组合与使用

下面通过三个递进的实战场景,验证装饰器模式的灵活性和实用性:① 仅使用原有串口功能(无装饰);② 用日志装饰器扩展(新增调试日志);③ 用"日志+加密"嵌套装饰(同时新增日志和加密功能)。这三个场景覆盖了嵌入式开发中功能扩展的常见需求,代码可直接编译运行验证。

1. 实战场景代码

c 复制代码
int main(void) {
    // 测试数据:模拟嵌入式场景中的传感器数据/控制指令
    uint8_t send_data[] = {0xAA, 0xBB, 0xCC};
    uint8_t recv_buf[10] = {0};  // 接收缓冲区

    // ----------------场景1:仅使用原有串口功能(无装饰)----------------
    printf("=== 场景1:原有串口功能(无扩展) ===\n");
    SerialCommunication *serial = serial_comm_init(115200);  // 初始化串口(115200波特率)
    if (serial != NULL) {
        // 调用串口的核心发送/接收功能
        serial->base.send(&serial->base, send_data, sizeof(send_data));
        serial->base.recv(&serial->base, recv_buf, sizeof(recv_buf));
        memset(recv_buf, 0, sizeof(recv_buf));  // 清空接收缓冲区,避免影响后续测试
        serial_comm_destroy(serial);  // 销毁串口模块
    }
    printf("\n");  // 换行分隔,便于查看输出

    // ----------------场景2:日志装饰器扩展(新增调试日志)----------------
    printf("=== 场景2:日志装饰器扩展(新增调试日志) ===\n");
    serial = serial_comm_init(115200);
    // 用日志装饰器装饰串口模块
    LogDecorator *log_decorator = log_decorator_init(&serial->base);
    if (log_decorator != NULL) {
        // 调用装饰器的接口(统一接口,用法和原有串口一致)
        log_decorator->base.send(&log_decorator->base, send_data, sizeof(send_data));
        log_decorator->base.recv(&log_decorator->base, recv_buf, sizeof(recv_buf));
        memset(recv_buf, 0, sizeof(recv_buf));
        log_decorator_destroy(log_decorator);  // 先销毁装饰器
    }
    serial_comm_destroy(serial);  // 再销毁被装饰的串口模块
    printf("\n");

    // ----------------场景3:日志+加密嵌套装饰(同时扩展两个功能)----------------
    printf("=== 场景3:日志+加密嵌套装饰(日志+加密双扩展) ===\n");
    serial = serial_comm_init(115200);
    // 嵌套装饰:先日志装饰,再加密装饰(顺序可灵活调整)
    log_decorator = log_decorator_init(&serial->base);
    EncryptDecorator *encrypt_decorator = encrypt_decorator_init(&log_decorator->base, 0x5A);  // 密钥0x5A
    if (encrypt_decorator != NULL) {
        // 调用最外层装饰器的接口,自动触发所有嵌套装饰器的功能
        encrypt_decorator->base.send(&encrypt_decorator->base, send_data, sizeof(send_data));
        encrypt_decorator->base.recv(&encrypt_decorator->base, recv_buf, sizeof(recv_buf));
        encrypt_decorator_destroy(encrypt_decorator);  // 先销毁最外层装饰器
    }
    if (log_decorator != NULL) {
        log_decorator_destroy(log_decorator);  // 再销毁内层装饰器
    }
    serial_comm_destroy(serial);  // 最后销毁串口模块

    return 0;
}

2. 验证结果与分析

text 复制代码
=== 场景1:原有串口功能(无扩展) ===
[串口发送] 波特率:115200, 数据:AA BB CC 
[串口接收] 数据:12 34 56 

=== 场景2:日志装饰器扩展(新增调试日志) ===
[日志装饰器] 发送数据长度:3
[串口发送] 波特率:115200, 数据:AA BB CC 
[串口接收] 数据:12 34 56 
[日志装饰器] 接收数据长度:3

=== 场景3:日志+加密嵌套装饰(日志+加密双扩展) ===
[加密装饰器] 加密后数据:F0 ED 96 
[日志装饰器] 发送数据长度:3
[串口发送] 波特率:115200, 数据:F0 ED 96 
[串口接收] 数据:12 34 56 
[日志装饰器] 接收数据长度:3
[加密装饰器] 解密后数据:48 6E 0C 
text 复制代码
=== 场景1:原有串口功能 ===
[串口发送] 波特率:115200, 数据:AA BB CC 
[串口接收] 数据:12 34 56 

=== 场景2:日志装饰器扩展 ===
[日志装饰器] 发送数据长度:3
[串口发送] 波特率:115200, 数据:AA BB CC 
[串口接收] 数据:12 34 56 
[日志装饰器] 接收数据长度:3

=== 场景3:日志+加密嵌套装饰 ===
[加密装饰器] 加密后数据:F0 ED 96 
[日志装饰器] 发送数据长度:3
[串口发送] 波特率:115200, 数据:F0 ED 96 
[串口接收] 数据:12 34 56 
[日志装饰器] 接收数据长度:3
[加密装饰器] 解密后数据:48 6E 0C 

结果分析:① 场景1仅输出串口发送/接收的核心数据,无任何扩展功能,符合原有模块的预期;② 场景2在原有功能基础上,新增了数据长度日志,不影响原有串口功能,实现了无侵入扩展,便于调试;③ 场景3嵌套两个装饰器后,发送流程为"加密→打印日志→串口发送",接收流程为"串口接收→打印日志→解密",两个扩展功能无缝叠加,且原有串口代码未做任何修改。这充分验证了装饰器模式的灵活性和实用性。

五、问题解决:装饰器模式实现的常见坑与解决方案

用C语言实现装饰器模式时,新手容易在接口统一、内存管理、嵌套逻辑这三个地方踩坑。下面整理了嵌入式开发中最常见的4个问题及对应的解决方案,帮你避开这些"雷区"。

  1. 接口不统一导致嵌套失败:这是最常见的问题,表现为装饰器和具体组件的函数指针参数、返回值不一致,无法嵌套调用。解决方案:严格遵循"抽象组件"定义的接口规范,所有具体组件和装饰器的函数指针,其参数类型、参数顺序、返回值类型必须完全一致。比如本文中所有send函数都统一为"(void *self, const uint8_t *data, uint16_t len)"参数和"int"返回值。

  2. 内存泄漏风险:嵌入式场景中内存资源宝贵,若装饰器和具体组件的动态内存未正确释放,容易导致内存泄漏。解决方案:制定统一的"销毁顺序"------先销毁最外层装饰器,再依次销毁内层装饰器,最后销毁具体组件;如果是资源紧张的嵌入式场景,可改用静态内存分配(比如全局变量、栈内存)替代malloc,从根本上避免内存泄漏。

  3. 嵌套顺序错误导致功能异常:多个装饰器嵌套时,若顺序不当,会导致额外职责的执行顺序不符合预期(比如想先日志再加密,结果变成先加密再日志)。解决方案:明确嵌套顺序的规则------外层装饰器的额外职责先执行,内层装饰器的额外职责后执行。比如"加密装饰器嵌套日志装饰器"是先加密再日志,"日志装饰器嵌套加密装饰器"是先日志再加密,根据实际需求调整顺序即可。

  4. self指针使用错误导致崩溃:装饰器函数中,误将装饰器自身的self指针传给被装饰对象,导致访问非法内存。解决方案:装饰器函数中,必须通过"component指针"调用被装饰对象的接口,且传入的self指针是被装饰对象的(即decorator->component),而非装饰器自身的self。比如正确写法是"decorator->component->send(decorator->component, data, len)",而非"decorator->component->send(self, data, len)"。

六、总结+互动引导

总结一下:装饰器模式是C语言开发者解决"功能扩展"问题的高效工具,其核心优势是"无侵入扩展"和"动态组合",完美契合嵌入式开发中"保护原有稳定代码"的核心需求。用C语言实现装饰器模式,关键是抓住三个核心点:抽象组件接口(结构体+函数指针)、结构体组合(嵌套被装饰对象指针)、函数指针重写(附加额外职责),掌握这三点就能灵活实现各种功能扩展。

对比传统的模拟继承扩展,装饰器模式在灵活性、低侵入性、内存开销上都有明显优势,尤其适合嵌入式场景中"动态叠加多个独立职责"的需求。本文的实现框架可直接移植到实际项目中,比如给传感器数据处理模块加校验装饰器、给文件存储模块加CRC校验装饰器、给网络通信模块加超时重试装饰器等。

如果这篇内容帮你搞定了C语言功能扩展的痛点,别忘了点赞、收藏 备用!后续还会更新其他创建型模式(如工厂模式、单例模式)的C语言实战教程,都是嵌入式开发中能直接用到的干货。关注我,获取更多底层开发和设计模式的实战技巧!如果在实际项目中遇到装饰器模式的嵌套逻辑、内存管理问题,或者有其他想了解的C语言设计模式场景,欢迎在评论区留言讨论,一起攻克技术难点~

相关推荐
燃于AC之乐2 小时前
【C++手撕STL】Vector模拟实现:从零到一的容器设计艺术
开发语言·c++·容器·stl·vector·底层·模板编程
开开心心就好2 小时前
视频伪装软件,.vsec格式批量伪装播放专用
java·linux·开发语言·网络·python·电脑·php
松涛和鸣2 小时前
63、IMX6ULL ADC驱动开发
c语言·arm开发·驱动开发·单片机·gpt·fpga开发
kaoshi100app2 小时前
2026年注册安全工程师报考条件解读
开发语言·人工智能·职场和发展·学习方法
是三好2 小时前
java集合
java·开发语言
凯子坚持 c2 小时前
Qt常用控件指南(5)
开发语言·数据库·qt
foundbug9992 小时前
MATLAB实现轴承刚度计算
开发语言·matlab
C++ 老炮儿的技术栈2 小时前
CMFCEditBrowseCtrl用法一例
c语言·开发语言·c++·windows·qt·visual studio code
Three~stone2 小时前
Matlab R2024b 保姆级安装教程(附:解决win10问题)
开发语言·算法·matlab