电子产业一站式赋能平台

PCB联盟网

搜索
查看: 21|回复: 0
收起左侧

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

[复制链接]

591

主题

591

帖子

7358

积分

高级会员

Rank: 5Rank: 5

积分
7358
发表于 昨天 11:45 | 显示全部楼层 |阅读模式
关注+星标公众,不错过精彩内容作者 | Mr.Deng转自 | 嵌入式情报局
本文给大家分享一下嵌入式C语言const与指针的门门道道,大家都知道在C语言中,指针的灵活性是其核心魅力,但也如同一把双刃剑——稍有不慎就可能引发内存越界、数据篡改等问题。
而const关键字的加入,则为指针赋予了“选择性自由”的能力:既能保留指针的动态操作,又能精准控制数据的可修改性。
今天,我们从实际工程场景出发,揭秘const与指针结合的核心技巧,助你写出更安全、更健壮的代码。
一、技术本质:const与指针的三种“契约”

在深入应用前,先明确const修饰指针时的三种组合形态(以int类型为例):
语法指针可变性内容可变性核心意义const int *p???只读数据,指针灵活int *const p???固定地址,数据可改const int *const p??完全只读记忆口诀:
?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
", 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指针的工程哲学
安全第一:通过编译时检查,将运行时错误消灭在萌芽阶段。代码即文档:const明确传达了数据的使用权限,减少团队协作成本。资源契约:在嵌入式、系统级开发中,const指针是硬件、内存、数据的“守护者”。[/ol]------------ END ------------

xad4vnmrlxy64042765314.gif

xad4vnmrlxy64042765314.gif

●专栏《嵌入式工具●专栏《嵌入式开发》●专栏《Keil教程》●嵌入式专栏精选教程
关注公众号回复“加群”按规则加入技术交流群,回复“1024”查看更多内容。
点击“阅读原文”查看更多分享。
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


联系客服 关注微信 下载APP 返回顶部 返回列表