vesperW · 3 天前

嵌入式 C 指针与 const 的完美组合

本文给大家分享一下嵌入式 C 语言 const 与指针的门门道道,大家都知道在 C 语言中,指针的灵活性是其核心魅力,但也如同一把双刃剑——稍有不慎就可能引发内存越界、数据篡改等问题。

而 const 关键字的加入,则为指针赋予了“选择性自由”的能力:既能保留指针的动态操作,又能精准控制数据的可修改性。

今天,我们从实际工程场景出发,揭秘 const 与指针结合的核心技巧,助你写出更安全、更健壮的代码。

一、技术本质:const 与指针的三种“契约”

在深入应用前,先明确 const 修饰指针时的三种组合形态(以 int 类型为例):

image.png

记忆口诀:

  • const 在左(const T *p )→ 数据不可变
  • const 在右(T *const p )→ 指针不可变
  • 两边都 const → 双锁封印

二、实战场景:const 指针的五大高光时刻

函数参数:防御性编程的利器

场景:设计一个打印字符串长度的函数,确保内部不会误改字符串内容。

size_t safe_strlen(const char *str) {
    // str[0] = 'A';  // 编译报错!禁止修改数据
    size_t len = 0;
    while (str[len] != '\0') len++;
    return len;
}

价值:

  • 明确函数职责:const char * 告知调用者“此函数不会修改你的数据”。
  • 防止内部误操作:即使函数内部有复杂逻辑,也无法通过指针篡改数据。

硬件寄存器访问:地址不可变的强制约束

场景:操作嵌入式设备的寄存器,要求指针地址固定,但允许写入数据。

#define HW_REG_ADDR 0x40000000

volatile uint32_t *const reg = (uint32_t *)HW_REG_ADDR;

void set_register() {
    *reg = 0x55AA;    // 正确:写入数据
    // reg = (void*)0x50000000;  // 编译报错!地址不可变
}

价值:

  • 防止指针被意外修改,确保硬件操作地址的绝对正确性。
  • volatile 与 const 配合,既保证地址固定,又避免编译器优化。

嵌入式配置表:双重保护敏感数据

场景:存储设备的只读配置参数(如波特率、校验位)。

const struct UartConfig {
    int baud_rate;
    char parity;
} *const config_table = (const struct UartConfig*)0x8000;

void init_uart() {
    // config_table->baud_rate = 115200;  // 错误:数据不可改
    // config_table = NULL;               // 错误:指针不可改
    set_uart(config_table->baud_rate, config_table->parity);
}

价值:

  • 数据与指针双 const ,防止运行时意外覆盖配置表。
  • 映射到固定内存地址,适用于 ROM 或 Flash 存储的配置数据。

动态内存管理:防止指针“漂移”

场景:在内存池中分配固定块,确保管理指针不越界。

uint8_t memory_pool[1024];
uint8_t *const pool_start = memory_pool;
uint8_t *const pool_end = memory_pool + sizeof(memory_pool);

void* allocate_mem(size_t size) {
    static uint8_t *current = memory_pool;
    if (current + size > pool_end) return NULL;
    void *ptr = current;
    current += size;
    return ptr;
}

价值:

  • pool_start 和 pool_end 作为边界哨兵,禁止修改,确保内存池范围恒定。

字符串常量:避免野指针陷阱

场景:定义只读的全局字符串常量。

const char *const LOG_HEADER = "[SYSTEM]: ";

void log_message(const char *msg) {
    printf("%s%s\n", LOG_HEADER, msg);
    // LOG_HEADER[0] = '(';       // 错误:数据不可改
    // LOG_HEADER = "[ERROR]: ";  // 错误:指针不可改
}

价值:

  • 防止字符串常量被意外修改(否则可能触发段错误)。

三、进阶技巧:const 与类型转换的博弈

穿透 const 限制?小心 UB!

问题:能否通过其他指针修改 const 数据?

const int a = 100;
int *p = (int*)&a;
*p = 200;  // 未定义行为(UB)!可能崩溃或静默错误

结论:

  • const
    是开发者的“君子协定”,强制突破可能导致不可预知后果。

多级指针的 const 传递

规则:const
修饰应遵循“从右向左”结合原则:

const int **pp1;     // pp1可变,*pp1可变,**pp1不可变
int *const *pp2;     // pp2可变,*pp2不可变,**pp2可变
int **const pp3;     // pp3不可变,*pp3可变,**pp3可变

应用:在复杂数据结构(如链表、树)中逐层约束可变性。

四、总结:const 指针的工程哲学

  1. 安全第一:通过编译时检查,将运行时错误消灭在萌芽阶段。
  2. 代码即文档:const 明确传达了数据的使用权限,减少团队协作成本。
  3. 资源契约:在嵌入式、系统级开发中,const 指针是硬件、内存、数据的“守护者”。

END

作者:Mr.Deng
来源:嵌入式专栏

推荐阅读

欢迎大家点赞留言,更多 Arm 技术文章动态请关注极术社区嵌入式客栈专栏欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。

推荐阅读
关注数
2921
内容数
355
分享一些在嵌入式应用开发方面的浅见,广交朋友
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息