第1章:const与指针——三种组合,三种人生
指针这东西,本来就已经够让人头疼了。再加上一个const,很多人直接就懵了。我记得刚学C语言那会儿,看到const int *p和int * const p,心里就一个想法:这俩到底有啥区别?
后来在嵌入式项目里栽过跟头,才真正搞明白。今天咱们就把这三种组合掰开揉碎了讲清楚。
1.1 先搞清楚const修饰的是谁
其实核心就一句话:const修饰的是它右边最近的那个东西。你想想看,是不是这个理?
咱们来看三个典型写法:
const int *p; // 情况A:指向const整型的指针
int * const p; // 情况B:指向整型的const指针
const int * const p; // 情况C:指向const整型的const指针
嗯,这里要注意:读法是有技巧的。从右往左读,反而更清楚。
快速记忆口诀:
const int *p→ p是一个指针,指向一个const int(指针可变,指向的值不可变)int * const p→ p是一个const指针,指向一个int(指针不可变,指向的值可变)const int * const p→ p是一个const指针,指向一个const int(指针和值都不可变)
1.2 情况A:const int *p —— 指向常量的指针
这种写法,说白了就是:你可以改变指针的指向,但不能通过指针修改指向的值。
int a = 10;
int b = 20;
const int *p = &a;
*p = 30; // ❌ 编译错误!不能修改指向的值
p = &b; // ✅ 可以!指针可以指向别处
我在项目中遇到过这种情况:读取传感器数据时,有些寄存器是只读的。用const int *来指向它们,编译器会帮你拦住所有写操作。这比你自己记着“别写啊别写”靠谱多了。
实用场景:
- 函数参数中,如果你只想读取数据而不修改它,用
const int *传参 - 指向硬件只读寄存器
- 字符串常量(
const char *str = "hello";)
1.3 情况B:int * const p —— 常量指针
这种正好反过来:指针本身是常量,不能指向别处,但可以通过指针修改指向的值。
int a = 10;
int b = 20;
int * const p = &a;
*p = 30; // ✅ 可以!修改指向的值
p = &b; // ❌ 编译错误!指针不能指向别处
我曾经在写DMA(直接存储器访问)驱动时用过这个。DMA控制器的寄存器地址是固定的,用int * const来定义,既保证了地址不会跑偏,又能正常读写寄存器值。嗯,这种场景下特别实用。
注意:常量指针必须在定义时初始化,因为之后不能再赋值了。
int * const p; // ❌ 编译错误!未初始化
int * const p = &a; // ✅ 正确
1.4 情况C:const int * const p —— 双重const
这个就厉害了——指针不能改,指向的值也不能改。两头都锁死。
int a = 10;
int b = 20;
const int * const p = &a;
*p = 30; // ❌ 编译错误!不能修改值
p = &b; // ❌ 编译错误!不能修改指针
说实话,这种写法在实际项目中用得不多。但我在做Bootloader时用过一次——指向一个固定的、只读的系统配置表。那个表在Flash里,地址固定,内容也不能改。用const int * const正好。
1.5 三种组合对比表
| 声明 | 指针本身 | 指向的值 | 典型场景 |
|---|---|---|---|
const int *p |
可变 | 不可变 | 函数参数、只读数据 |
int * const p |
不可变 | 可变 | 硬件寄存器地址 |
const int * const p |
不可变 | 不可变 | 系统配置表、Bootloader |
1.6 避坑指南
我曾经在代码里写过这样的bug:
const int *p;
int a = 5;
p = &a;
// 后来忘了p是const的,试图 *p = 10;
// 编译器报错,排查了半天才想起来
还有一次更隐蔽:
void func(const int *p) {
int *q = p; // ❌ 编译警告!丢弃了const限定符
*q = 100; // 危险!
}
嗯,这里要注意:const指针不能隐式转换为非const指针。如果你非要转,得用强制类型转换,但那样做通常意味着你的设计有问题。
我的建议:
- 能用const就用const,让编译器帮你检查
- 函数参数中,如果只读不写,加const
- 全局指针变量,如果指向固定地址,用
int * const - 别轻易去掉const,那往往是bug的源头
1.7 知识体系图
下面这张图帮你理清这三种组合的关系:
1.8 写在最后
这三种组合,说白了就是谁不能被修改的问题。指针本身?还是指向的值?还是两者都不能改?搞清楚这个,你就掌握了const与指针的精髓。
我个人习惯是:能加const的地方都加上。这不仅仅是风格问题,更是让编译器帮你抓bug。你想想看,如果某个变量本不该被修改,结果不小心改了,那bug多难查啊。const就是你的第一道防线。
下一章咱们聊聊指针运算,那又是另一个有意思的话题了。
公众号:蓝海资料掘金营,微信deep3321