跳转至

C语言高级特性详解

概述

C语言作为嵌入式开发的核心语言,其高级特性是编写高效、可维护代码的关键。完成本文学习后,你将能够:

  • 掌握指针的高级用法,包括多级指针和指针数组
  • 理解并使用函数指针实现回调机制
  • 灵活运用结构体和联合体组织数据
  • 使用位操作进行高效的底层控制
  • 掌握预处理器的高级技巧

背景知识

C语言在嵌入式中的地位

C语言因其接近硬件、执行效率高、可移植性强等特点,成为嵌入式开发的首选语言。掌握C语言的高级特性,能够:

  • 编写更高效的代码
  • 更好地控制硬件资源
  • 实现复杂的软件架构
  • 提高代码的可维护性

前置知识要求

本文假设你已经掌握: - C语言基本语法 - 基本的指针概念 - 基本的数据类型和变量

核心内容

1. 指针高级用法

1.1 多级指针

多级指针是指向指针的指针,在嵌入式开发中常用于动态数据结构和参数传递。

#include <stdio.h>
#include <stdint.h>

// 二级指针示例:修改指针本身
void allocate_buffer(uint8_t **buffer, uint32_t size) {
    *buffer = (uint8_t *)malloc(size);
    if (*buffer != NULL) {
        printf("Buffer allocated at: %p\n", (void *)*buffer);
    }
}

int main(void) {
    uint8_t *data = NULL;

    // 传递指针的地址,函数内部可以修改指针本身
    allocate_buffer(&data, 256);

    if (data != NULL) {
        // 使用分配的内存
        data[0] = 0xAA;
        printf("First byte: 0x%02X\n", data[0]);
        free(data);
    }

    return 0;
}

代码说明: - uint8_t **buffer:二级指针,可以修改传入的指针变量 - *buffer = malloc(size):修改指针指向的地址 - &data:传递指针的地址

应用场景: - 函数内部分配内存并返回 - 修改链表节点指针 - 实现动态数组

1.2 指针数组与数组指针

#include <stdio.h>
#include <stdint.h>

int main(void) {
    // 指针数组:数组的每个元素都是指针
    const char *error_messages[] = {
        "Success",
        "Invalid parameter",
        "Timeout",
        "Hardware error"
    };

    // 数组指针:指向整个数组的指针
    uint32_t data[5] = {1, 2, 3, 4, 5};
    uint32_t (*p_array)[5] = &data;

    // 使用指针数组
    uint8_t error_code = 2;
    printf("Error: %s\n", error_messages[error_code]);

    // 使用数组指针
    printf("Third element: %u\n", (*p_array)[2]);

    return 0;
}

关键区别: - char *arr[]:指针数组,arr是数组,元素是指针 - char (*p)[]:数组指针,p是指针,指向数组

1.3 const与指针

在嵌入式开发中,正确使用const可以提高代码安全性和编译器优化。

#include <stdint.h>

// 指向常量的指针:不能通过指针修改数据
void read_sensor(const uint32_t *sensor_data) {
    uint32_t value = *sensor_data;  // 可以读取
    // *sensor_data = 100;  // 编译错误:不能修改
}

// 常量指针:指针本身不能改变指向
void process_buffer(uint8_t * const buffer, uint32_t size) {
    buffer[0] = 0xFF;  // 可以修改数据
    // buffer = NULL;  // 编译错误:不能修改指针
}

// 指向常量的常量指针:都不能修改
void read_config(const uint32_t * const config) {
    uint32_t value = *config;  // 只能读取
    // *config = 100;  // 编译错误
    // config = NULL;  // 编译错误
}

记忆技巧: - const在*左边:数据是常量 - const在*右边:指针是常量

2. 函数指针

函数指针是C语言实现回调机制、状态机和插件系统的核心技术。

2.1 基本用法

#include <stdio.h>
#include <stdint.h>

// 定义函数类型
typedef void (*callback_t)(uint32_t event);

// 回调函数实现
void button_pressed_handler(uint32_t event) {
    printf("Button pressed: event %u\n", event);
}

void timer_expired_handler(uint32_t event) {
    printf("Timer expired: event %u\n", event);
}

// 事件处理器
void register_callback(callback_t callback, uint32_t event) {
    if (callback != NULL) {
        callback(event);
    }
}

int main(void) {
    // 注册不同的回调函数
    register_callback(button_pressed_handler, 1);
    register_callback(timer_expired_handler, 2);

    return 0;
}

代码说明: - typedef void (*callback_t)(uint32_t):定义函数指针类型 - callback(event):通过函数指针调用函数 - 函数指针使代码更灵活,支持运行时绑定

2.2 函数指针数组

函数指针数组常用于实现状态机和命令分发。

#include <stdio.h>
#include <stdint.h>

// 状态处理函数类型
typedef void (*state_handler_t)(void);

// 各状态处理函数
void state_idle(void) {
    printf("State: IDLE\n");
}

void state_running(void) {
    printf("State: RUNNING\n");
}

void state_error(void) {
    printf("State: ERROR\n");
}

// 状态机实现
typedef enum {
    STATE_IDLE = 0,
    STATE_RUNNING,
    STATE_ERROR,
    STATE_MAX
} system_state_t;

// 状态处理函数表
state_handler_t state_table[STATE_MAX] = {
    state_idle,
    state_running,
    state_error
};

void execute_state(system_state_t state) {
    if (state < STATE_MAX && state_table[state] != NULL) {
        state_table[state]();
    }
}

int main(void) {
    // 执行不同状态
    execute_state(STATE_IDLE);
    execute_state(STATE_RUNNING);
    execute_state(STATE_ERROR);

    return 0;
}

应用场景: - 状态机实现 - 命令解析器 - 事件驱动系统 - 协议处理

3. 结构体与联合体

3.1 结构体高级用法

#include <stdio.h>
#include <stdint.h>
#include <string.h>

// 位域结构体:节省内存
typedef struct {
    uint8_t mode : 2;      // 2位:0-3
    uint8_t enable : 1;    // 1位:0-1
    uint8_t priority : 3;  // 3位:0-7
    uint8_t reserved : 2;  // 2位:保留
} gpio_config_t;

// 柔性数组成员:动态大小的结构体
typedef struct {
    uint32_t length;
    uint8_t data[];  // 柔性数组成员
} packet_t;

// 结构体对齐
typedef struct {
    uint8_t  a;    // 1字节
    uint32_t b;    // 4字节,可能有3字节填充
    uint16_t c;    // 2字节
} aligned_struct_t;

int main(void) {
    // 位域使用
    gpio_config_t gpio = {0};
    gpio.mode = 2;
    gpio.enable = 1;
    gpio.priority = 5;
    printf("GPIO config size: %zu bytes\n", sizeof(gpio_config_t));

    // 柔性数组使用
    packet_t *packet = (packet_t *)malloc(sizeof(packet_t) + 10);
    packet->length = 10;
    memset(packet->data, 0xAA, 10);
    printf("Packet size: %zu bytes\n", sizeof(packet_t));
    free(packet);

    // 结构体对齐
    printf("Aligned struct size: %zu bytes\n", sizeof(aligned_struct_t));

    return 0;
}

关键点: - 位域可以节省内存,但可能影响性能 - 柔性数组成员必须是结构体最后一个成员 - 结构体对齐影响内存占用和访问效率

3.2 联合体的妙用

联合体在嵌入式开发中常用于数据类型转换和寄存器访问。

#include <stdio.h>
#include <stdint.h>

// 联合体:多种方式访问同一数据
typedef union {
    uint32_t word;
    uint16_t half[2];
    uint8_t  byte[4];
} data_converter_t;

// 寄存器访问
typedef union {
    uint32_t value;
    struct {
        uint32_t enable : 1;
        uint32_t mode : 2;
        uint32_t speed : 2;
        uint32_t reserved : 27;
    } bits;
} register_t;

int main(void) {
    // 数据转换
    data_converter_t data;
    data.word = 0x12345678;
    printf("Word: 0x%08X\n", data.word);
    printf("Half[0]: 0x%04X\n", data.half[0]);
    printf("Half[1]: 0x%04X\n", data.half[1]);
    printf("Bytes: 0x%02X 0x%02X 0x%02X 0x%02X\n",
           data.byte[0], data.byte[1], data.byte[2], data.byte[3]);

    // 寄存器操作
    register_t reg = {0};
    reg.bits.enable = 1;
    reg.bits.mode = 2;
    reg.bits.speed = 3;
    printf("Register value: 0x%08X\n", reg.value);

    return 0;
}

应用场景: - 数据类型转换 - 寄存器位域访问 - 协议数据包解析 - 节省内存空间

4. 位操作技巧

位操作是嵌入式开发的基本功,用于高效的硬件控制和数据处理。

4.1 基本位操作

#include <stdio.h>
#include <stdint.h>

// 位操作宏定义
#define BIT_SET(reg, bit)       ((reg) |= (1U << (bit)))
#define BIT_CLEAR(reg, bit)     ((reg) &= ~(1U << (bit)))
#define BIT_TOGGLE(reg, bit)    ((reg) ^= (1U << (bit)))
#define BIT_READ(reg, bit)      (((reg) >> (bit)) & 1U)
#define BIT_WRITE(reg, bit, val) \
    ((val) ? BIT_SET(reg, bit) : BIT_CLEAR(reg, bit))

int main(void) {
    uint32_t gpio_reg = 0x00000000;

    // 设置位
    BIT_SET(gpio_reg, 5);
    printf("After SET bit 5: 0x%08X\n", gpio_reg);

    // 清除位
    BIT_CLEAR(gpio_reg, 5);
    printf("After CLEAR bit 5: 0x%08X\n", gpio_reg);

    // 翻转位
    BIT_TOGGLE(gpio_reg, 3);
    printf("After TOGGLE bit 3: 0x%08X\n", gpio_reg);

    // 读取位
    uint8_t bit_value = BIT_READ(gpio_reg, 3);
    printf("Bit 3 value: %u\n", bit_value);

    // 写入位
    BIT_WRITE(gpio_reg, 7, 1);
    printf("After WRITE bit 7: 0x%08X\n", gpio_reg);

    return 0;
}

4.2 多位操作

#include <stdio.h>
#include <stdint.h>

// 多位操作宏
#define BITS_SET(reg, mask)         ((reg) |= (mask))
#define BITS_CLEAR(reg, mask)       ((reg) &= ~(mask))
#define BITS_TOGGLE(reg, mask)      ((reg) ^= (mask))
#define BITS_READ(reg, mask, pos)   (((reg) & (mask)) >> (pos))
#define BITS_WRITE(reg, mask, pos, val) \
    ((reg) = ((reg) & ~(mask)) | (((val) << (pos)) & (mask)))

// 位域定义
#define GPIO_MODE_MASK      0x00000003  // 位0-1
#define GPIO_MODE_POS       0
#define GPIO_SPEED_MASK     0x0000000C  // 位2-3
#define GPIO_SPEED_POS      2
#define GPIO_PULL_MASK      0x00000030  // 位4-5
#define GPIO_PULL_POS       4

int main(void) {
    uint32_t gpio_config = 0x00000000;

    // 设置模式为输出(值2)
    BITS_WRITE(gpio_config, GPIO_MODE_MASK, GPIO_MODE_POS, 2);
    printf("After set mode: 0x%08X\n", gpio_config);

    // 设置速度为高速(值3)
    BITS_WRITE(gpio_config, GPIO_SPEED_MASK, GPIO_SPEED_POS, 3);
    printf("After set speed: 0x%08X\n", gpio_config);

    // 读取模式
    uint32_t mode = BITS_READ(gpio_config, GPIO_MODE_MASK, GPIO_MODE_POS);
    printf("Mode value: %u\n", mode);

    return 0;
}

应用场景: - GPIO配置 - 寄存器操作 - 标志位管理 - 数据打包解包

5. 预处理器技巧

预处理器是C语言的强大工具,合理使用可以提高代码的灵活性和可维护性。

5.1 条件编译

#include <stdio.h>
#include <stdint.h>

// 调试开关
#define DEBUG_ENABLE 1

#if DEBUG_ENABLE
    #define DEBUG_PRINT(fmt, ...) \
        printf("[DEBUG] " fmt "\n", ##__VA_ARGS__)
#else
    #define DEBUG_PRINT(fmt, ...)
#endif

// 平台相关代码
#ifdef STM32F4
    #define LED_PIN GPIO_PIN_5
    #define LED_PORT GPIOA
#elif defined(STM32F1)
    #define LED_PIN GPIO_Pin_13
    #define LED_PORT GPIOC
#else
    #error "Unsupported platform"
#endif

int main(void) {
    uint32_t value = 100;
    DEBUG_PRINT("Value: %u", value);
    DEBUG_PRINT("Starting system...");

    return 0;
}

5.2 宏函数技巧

#include <stdio.h>
#include <stdint.h>

// 安全的宏函数:使用do-while(0)
#define SAFE_FREE(ptr) \
    do { \
        if ((ptr) != NULL) { \
            free(ptr); \
            (ptr) = NULL; \
        } \
    } while(0)

// 最小值和最大值
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))

// 数组大小
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

// 字段偏移
#define OFFSET_OF(type, member) ((size_t)&(((type *)0)->member))

// 容器获取
#define CONTAINER_OF(ptr, type, member) \
    ((type *)((char *)(ptr) - OFFSET_OF(type, member)))

typedef struct {
    uint32_t id;
    char name[32];
    uint32_t value;
} device_t;

int main(void) {
    // 使用MIN/MAX
    uint32_t a = 10, b = 20;
    printf("MIN: %u, MAX: %u\n", MIN(a, b), MAX(a, b));

    // 使用ARRAY_SIZE
    uint32_t array[] = {1, 2, 3, 4, 5};
    printf("Array size: %zu\n", ARRAY_SIZE(array));

    // 使用OFFSET_OF
    printf("Offset of value: %zu\n", OFFSET_OF(device_t, value));

    return 0;
}

5.3 字符串化和连接

#include <stdio.h>
#include <stdint.h>

// 字符串化
#define TO_STRING(x) #x
#define STRINGIFY(x) TO_STRING(x)

// 标记连接
#define CONCAT(a, b) a##b
#define MAKE_NAME(prefix, suffix) CONCAT(prefix, suffix)

// 版本信息
#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#define VERSION_PATCH 3
#define VERSION_STRING STRINGIFY(VERSION_MAJOR) "." \
                       STRINGIFY(VERSION_MINOR) "." \
                       STRINGIFY(VERSION_PATCH)

int main(void) {
    // 字符串化
    printf("Version: %s\n", VERSION_STRING);
    printf("Compiled on: %s %s\n", __DATE__, __TIME__);

    // 标记连接
    uint32_t MAKE_NAME(var_, 1) = 100;
    uint32_t MAKE_NAME(var_, 2) = 200;
    printf("var_1: %u, var_2: %u\n", var_1, var_2);

    return 0;
}

预处理器最佳实践: - 宏参数使用括号保护 - 复杂宏使用do-while(0)包装 - 避免宏的副作用 - 使用inline函数替代复杂宏

实践示例

综合示例:设备驱动框架

下面是一个综合运用上述特性的设备驱动框架示例:

#include <stdio.h>
#include <stdint.h>
#include <string.h>

// 设备操作函数指针
typedef struct device_ops {
    int (*init)(void *dev);
    int (*read)(void *dev, uint8_t *buf, uint32_t len);
    int (*write)(void *dev, const uint8_t *buf, uint32_t len);
    int (*ioctl)(void *dev, uint32_t cmd, void *arg);
} device_ops_t;

// 设备结构体
typedef struct device {
    const char *name;
    uint32_t id;
    void *private_data;
    const device_ops_t *ops;
} device_t;

// UART设备私有数据
typedef struct {
    uint32_t baudrate;
    uint8_t data_bits;
    uint8_t stop_bits;
} uart_config_t;

// UART设备操作实现
int uart_init(void *dev) {
    device_t *device = (device_t *)dev;
    uart_config_t *config = (uart_config_t *)device->private_data;
    printf("[%s] Init: baudrate=%u\n", device->name, config->baudrate);
    return 0;
}

int uart_read(void *dev, uint8_t *buf, uint32_t len) {
    device_t *device = (device_t *)dev;
    printf("[%s] Read %u bytes\n", device->name, len);
    return len;
}

int uart_write(void *dev, const uint8_t *buf, uint32_t len) {
    device_t *device = (device_t *)dev;
    printf("[%s] Write %u bytes\n", device->name, len);
    return len;
}

int uart_ioctl(void *dev, uint32_t cmd, void *arg) {
    device_t *device = (device_t *)dev;
    printf("[%s] IOCTL cmd=0x%X\n", device->name, cmd);
    return 0;
}

// UART设备操作表
const device_ops_t uart_ops = {
    .init = uart_init,
    .read = uart_read,
    .write = uart_write,
    .ioctl = uart_ioctl
};

// 设备注册和使用
int main(void) {
    // UART配置
    uart_config_t uart_config = {
        .baudrate = 115200,
        .data_bits = 8,
        .stop_bits = 1
    };

    // 创建UART设备
    device_t uart_device = {
        .name = "UART1",
        .id = 1,
        .private_data = &uart_config,
        .ops = &uart_ops
    };

    // 使用设备
    if (uart_device.ops->init) {
        uart_device.ops->init(&uart_device);
    }

    uint8_t tx_buf[] = "Hello";
    uart_device.ops->write(&uart_device, tx_buf, sizeof(tx_buf));

    uint8_t rx_buf[32];
    uart_device.ops->read(&uart_device, rx_buf, sizeof(rx_buf));

    return 0;
}

代码说明: - 使用函数指针实现设备操作接口 - 使用结构体组织设备数据 - 实现了类似Linux驱动的框架 - 支持多种设备类型扩展

深入理解

性能考虑

内联函数 vs 宏

// 宏定义:无类型检查,可能有副作用
#define SQUARE_MACRO(x) ((x) * (x))

// 内联函数:有类型检查,无副作用
static inline uint32_t square_inline(uint32_t x) {
    return x * x;
}

int main(void) {
    uint32_t a = 5;

    // 宏的副作用
    uint32_t result1 = SQUARE_MACRO(a++);  // a被计算两次!

    // 内联函数无副作用
    uint32_t result2 = square_inline(a++);  // a只计算一次

    return 0;
}

选择建议: - 简单操作:使用宏 - 复杂逻辑:使用inline函数 - 类型安全:优先inline函数

内存对齐优化

#include <stdio.h>
#include <stdint.h>

// 未优化的结构体
typedef struct {
    uint8_t  a;    // 1字节
    uint32_t b;    // 4字节,前面有3字节填充
    uint8_t  c;    // 1字节
    uint32_t d;    // 4字节,前面有3字节填充
} unoptimized_t;  // 总共16字节

// 优化后的结构体
typedef struct {
    uint32_t b;    // 4字节
    uint32_t d;    // 4字节
    uint8_t  a;    // 1字节
    uint8_t  c;    // 1字节
    // 2字节填充
} optimized_t;    // 总共12字节

int main(void) {
    printf("Unoptimized size: %zu bytes\n", sizeof(unoptimized_t));
    printf("Optimized size: %zu bytes\n", sizeof(optimized_t));

    return 0;
}

优化原则: - 按大小降序排列成员 - 相同大小的成员放在一起 - 使用__attribute__((packed))强制紧凑(慎用)

最佳实践

1. 指针使用规范

// 好的做法
uint8_t *ptr = NULL;  // 初始化为NULL
if (ptr != NULL) {    // 使用前检查
    *ptr = 0xFF;
}

// 释放后置NULL
free(ptr);
ptr = NULL;

// 避免野指针
uint8_t *get_buffer(void) {
    static uint8_t buffer[256];  // 使用static
    return buffer;
}

2. 函数指针使用规范

// 定义清晰的函数指针类型
typedef int (*operation_t)(int a, int b);

// 使用前检查
operation_t op = get_operation();
if (op != NULL) {
    int result = op(10, 20);
}

// 提供默认实现
int default_operation(int a, int b) {
    return a + b;
}

3. 位操作规范

// 使用明确的位宽类型
uint32_t reg = 0;

// 使用宏定义位掩码
#define BIT_MASK_MODE   0x03
#define BIT_POS_MODE    0

// 避免魔数
reg |= (2 << BIT_POS_MODE);  // 好
// reg |= 0x02;  // 不好,魔数

// 使用括号保护
#define SET_BITS(reg, mask, val) \
    ((reg) = ((reg) & ~(mask)) | ((val) & (mask)))

常见问题

Q1: 什么时候使用多级指针?

A: 多级指针主要用于以下场景: - 函数需要修改指针本身(如动态分配内存) - 实现多维动态数组 - 构建复杂的数据结构(如链表的链表)

示例:

// 需要修改指针本身
void create_buffer(uint8_t **buf, size_t size) {
    *buf = malloc(size);
}

// 使用
uint8_t *buffer = NULL;
create_buffer(&buffer, 256);  // buffer被修改

Q2: 函数指针和函数有什么区别?

A: - 函数:编译时确定,直接调用 - 函数指针:运行时确定,间接调用

函数指针的优势: - 实现回调机制 - 支持插件系统 - 实现多态行为 - 构建状态机

Q3: 位域和位操作哪个更好?

A: 各有优劣:

位域优势: - 代码更清晰易读 - 编译器自动处理位操作 - 适合寄存器映射

位操作优势: - 更灵活,可以动态指定位 - 性能可能更好 - 可移植性更强

建议: - 固定的寄存器定义:使用位域 - 动态的位操作:使用位操作宏

Q4: 如何避免宏的副作用?

A: 几种方法:

  1. 使用括号保护

    #define SQUARE(x) ((x) * (x))  // 好
    // #define SQUARE(x) x * x     // 不好
    

  2. 使用do-while(0)

    #define SWAP(a, b) \
        do { \
            typeof(a) temp = (a); \
            (a) = (b); \
            (b) = temp; \
        } while(0)
    

  3. 使用inline函数

    static inline uint32_t square(uint32_t x) {
        return x * x;
    }
    

Q5: 结构体对齐为什么重要?

A: 结构体对齐影响:

  1. 内存占用:不对齐会浪费内存
  2. 访问效率:对齐的数据访问更快
  3. 硬件要求:某些架构要求对齐访问

示例:

// 未对齐:16字节
struct bad {
    uint8_t a;   // 1 + 3填充
    uint32_t b;  // 4
    uint8_t c;   // 1 + 3填充
    uint32_t d;  // 4
};

// 对齐:12字节
struct good {
    uint32_t b;  // 4
    uint32_t d;  // 4
    uint8_t a;   // 1
    uint8_t c;   // 1 + 2填充
};

总结

本文深入讲解了C语言的高级特性,这些特性是编写高质量嵌入式代码的基础:

核心要点

  1. 指针高级用法
  2. 多级指针用于修改指针本身
  3. 指针数组和数组指针的区别
  4. const与指针的组合使用

  5. 函数指针

  6. 实现回调机制和事件驱动
  7. 函数指针数组构建状态机
  8. 提高代码的灵活性和可扩展性

  9. 结构体与联合体

  10. 位域节省内存空间
  11. 柔性数组成员实现动态结构
  12. 联合体用于数据转换和寄存器访问

  13. 位操作

  14. 基本位操作:设置、清除、翻转、读取
  15. 多位操作处理寄存器字段
  16. 高效的底层硬件控制

  17. 预处理器

  18. 条件编译实现平台适配
  19. 宏函数提高代码复用
  20. 字符串化和标记连接技巧

实践建议

  • 循序渐进:从简单特性开始,逐步掌握复杂用法
  • 多写代码:通过实践加深理解
  • 阅读源码:学习优秀开源项目的代码
  • 注重规范:遵循编码规范,提高代码质量

下一步学习

掌握这些高级特性后,建议继续学习: - C语言内存管理深入 - C语言数据结构实现 - 嵌入式C编程规范 - C语言常见陷阱与避免

延伸阅读

推荐书籍

  • 《C专家编程》- 深入理解C语言的高级特性
  • 《C陷阱与缺陷》- 避免常见的C语言错误
  • 《嵌入式C语言自我修养》- 嵌入式C编程实践

在线资源

相关文章

  • C语言内存管理深入 - 深入理解内存分配和管理
  • 嵌入式C编程规范 - MISRA C和编码标准
  • C语言常见陷阱与避免 - 常见错误和调试技巧

参考资料

  1. ISO/IEC 9899:2018 - C语言标准
  2. MISRA C:2012 - 嵌入式C编程规范
  3. ARM Cortex-M Programming Guide - ARM官方编程指南
  4. Linux Kernel Coding Style - Linux内核编码风格

练习题

  1. 编写一个使用函数指针数组实现的简单计算器,支持加减乘除四种运算
  2. 使用联合体实现一个数据包解析器,能够以字节和字段两种方式访问数据
  3. 设计一个位操作库,提供常用的位操作宏和函数
  4. 实现一个设备驱动框架,支持多种设备类型的注册和操作

实践项目

设计一个简单的嵌入式操作系统内核,要求: - 使用函数指针实现任务调度 - 使用位操作管理任务状态 - 使用结构体组织任务控制块 - 使用预处理器实现平台适配

下一步:建议学习 C语言内存管理深入