const 是 constant(常量)的缩写,作用是将变量声明为只读,告诉编译器”这个值不会被修改”。它既是给编译器看的约束,也是给读代码的人看的文档。
核心规则:看左边
const 修饰的是它左边的东西;如果左边没有东西,就修饰右边的。
const int a = 10; // 左边没东西,修饰右边的 int → a 是常量
int const b = 20; // 等价写法,修饰左边的 int → b 是常量
两种写法完全等价,但推荐用 const int,更符合直觉。
指针的四种组合
这是 const 最容易搞混的地方,记住”看左边”规则就不会错:
const int *p; // const 修饰左边:const 在 int 左边没东西,修饰 int → *p 不可改,p 可改
int const *p; // 等价于上面,const 修饰 int → *p 不可改,p 可改
int * const p; // const 修饰左边的 *(指针)→ p 不可改,*p 可改
const int * const p; // 两个 const → *p 不可改,p 也不可改
用表格总结:
| 声明 | *p 能改吗? | p 能改吗? | 含义 |
|---|---|---|---|
const int *p | ❌ | ✅ | 指向常量的指针(指针可以换方向) |
int * const p | ✅ | ❌ | 常量指针(指针钉死在一个地址上) |
const int * const p | ❌ | ❌ | 指向常量的常量指针 |
具体例子
int a = 1, b = 2;
// 1. 指向常量的指针
const int *p = &a;
*p = 10; // ❌ 编译错误:不能通过 p 修改它指向的值
p = &b; // ✅ 可以:p 本身可以指向别的变量
a = 10; // ✅ 注意:a 本身不是 const,直接改 a 是可以的!
// const 只限制通过 p 去改,不限制变量本身
// 2. 常量指针
int * const q = &a;
*q = 10; // ✅ 可以通过 q 修改值
q = &b; // ❌ 编译错误:q 永远指向 a,不能换
// 3. 双重 const
const int * const r = &a;
*r = 10; // ❌ 不能改值
r = &b; // ❌ 不能换方向
重要理解
const int *p不是说 p 指向的值真的永远不变,而是承诺”我不会通过 p 去改它”。变量本身可以通过其他方式(比如直接用变量名、通过其他非 const 指针)修改。
const 在函数参数中的用法
这是 const 最常用的场景之一。当你写一个函数,承诺不会修改传进来的数据,就用 const:
// 承诺:我只读取 buf 的内容,不会修改它
void printData(const char *buf) {
// buf[0] = 'x'; // ❌ 编译器直接报错,防止你误改
printf("%s\n", buf); // ✅ 只读操作没问题
}
为什么要这么做?
- 编译器帮你抓 bug:不小心写了修改操作,编译期就报错
- 告诉调用者:放心传进来,我不会改你的数据
- 允许传入 const 数据:比如传入字符串字面量(字符串字面量本身就是 const 的)
// 如果参数不写 const,下面的调用会有警告甚至错误
printData("hello"); // "hello" 是 const char[6]
const vs define
两者都能定义”常量”,但本质不同:
const | #define | |
|---|---|---|
| 本质 | 带类型的只读变量 | 预处理阶段的文本替换 |
| 类型检查 | ✅ 有类型,编译器检查 | ❌ 无类型,纯文本替换 |
| 作用域 | ✅ 遵循 C 语言作用域 | ❌ 从定义处到文件结束 |
| 调试 | ✅ 可以在调试器中看到 | ❌ 预处理后名字就消失了 |
| 内存 | 通常占内存(编译器可能优化) | 不占变量内存,直接替换到代码 |
| 取地址 | ✅ 可以 &p 取地址 | ❌ 不能取地址 |
#define MAX 100 // 预处理时把所有 MAX 替换成 100
const int max = 100; // 一个只读的整型变量
int arr1[MAX]; // ✅ 可以(C99后VCC也支持,但数组大小推荐用宏或枚举)
int arr2[max]; // ⚠️ 在C中不一定可以(数组大小需要编译期常量,C++可以)
建议
需要编译期常量(数组大小、case 标签)用
#define或enum;需要带类型、有作用域的只读变量用const。
常见误区
误区一:const 变量存放在只读内存里
不一定。const 只是编译器层面的约束,通过强制类型转换可以绕过(但不推荐):
const int a = 10;
int *p = (int *)&a;
*p = 20; // 能编译通过(有警告),但行为未定义
// 在某些嵌入式平台上 const 数据确实在 Flash(只读),写会直接崩溃
// 在 PC 上通常在栈或数据段,可以被改,但这是未定义行为
误区二:const 让代码变慢
不会。const 是编译期约束,运行时没有任何额外开销。编译器反而可能因为 const 做更好的优化。
下一步:02-缓冲区
相关:返回目录