const 是 C 语言中的一个关键字,它的全称是 "constant"(常量),它的核心作用是限定一个变量为只读,即告诉编译器,这个变量的值在初始化之后就不能再被修改了。

这听起来简单,但 const 在 C 语言中的用法非常灵活,也常常是初学者的难点,下面我们从浅入深,结合实例来彻底搞懂它。
const 的基本用法:修饰普通变量
这是最常见、最直观的用法。
const int a = 10; // 或者 int const a = 10;
这两种写法是完全等价的,都表示 a 是一个整型常量,一旦初始化后,任何试图修改 a 的行为都会导致编译错误。
const int a = 10; a = 20; // 错误!编译器会报错:assignment of read-only variable 'a'
关键点:

const修饰的变量不是常量,而是一个“只读变量”。- 它的值在编译时并不一定是已知的(比如通过函数初始化),但它一旦被赋值,就不能再改变。
const变量仍然有自己的存储空间,它不是一个纯粹的文本替换(不像 C++ 中的const或 C 中的#define)。
const 与指针:最复杂的部分
当 const 和指针结合时,情况就变得复杂了。const 的位置不同,其修饰的对象也不同,主要有以下四种情况:
const 修饰指针指向的数据(最常用)
const int *ptr; // 或者 int const *ptr;
- 含义:
ptr是一个指向整型常量的指针。 - 限制:不能通过
ptr来修改它所指向的值。ptr本身可以指向其他内存地址。 - 例子:
int a = 10; int b = 20; const int *ptr = &a; // ptr 指向 a *ptr = 30; // 错误!不能通过 ptr 修改 a 的值,因为 a 在 ptr 看来是只读的。 ptr = &b; // 正确!ptr 可以指向 b,因为它本身不是 const
const 修饰指针本身(不常用)
int * const ptr = &a;
- 含义:
ptr是一个本身是常量的指针。 - 限制:
ptr必须在声明时初始化,之后不能再指向其他地址,可以通过ptr来修改它所指向的值。 - 例子:
int a = 10; int b = 20; int * const ptr = &a; // ptr 必须初始化 ptr = &b; // 错误!ptr 本身是 const,不能再指向别处 *ptr = 30; // 正确!可以通过 ptr 修改 a 的值
const 同时修饰指针本身和它指向的数据
const int * const ptr = &a;
- 含义:
ptr是一个指向整型常量的、本身也是常量的指针。 - 限制:
ptr本身不能改变指向,也不能通过ptr修改它所指向的值,这是一个“只读”的指针。 - 例子:
int a = 10; int b = 20; const int * const ptr = &a; ptr = &b; // 错误!ptr 本身不能改变 *ptr = 30; // 错误!不能通过 ptr 修改 a 的值
指针指向 const 数据
int a = 10; const int ca = 20; int *ptr = &ca; // 错误!不能将 const int* 的地址赋给 int*,这会绕过 const 的限制
- 含义:这是一个安全规则,C 语言禁止将一个
const对象的地址赋给一个非const的指针。 - 原因:如果允许这样做,你就可以通过这个非
const指针去修改const对象的值,这就破坏了const的初衷,带来了安全隐患。 - 如何解决? 如果确实需要用一个指针去操作一个
const变量,应该使用第一种情况的指针。
const int ca = 20; const int *ptr = &ca; // 正确
const 与函数参数
将函数参数声明为 const 是一种非常好的编程实践,主要有两个好处:
- 防止函数内部意外修改参数的值。
- 允许传入
const变量作为实参,扩大了函数的适用范围。
// 一个函数,用于打印一个整数数组
void print_array(const int *arr, int size) {
// arr 是一个指向 const int 的指针
// 这保证了在 print_array 函数内部,我们不会意外地修改数组内容
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
// arr[i] = 0; // 错误!不能修改
}
printf("\n");
}
int main() {
int my_array[] = {1, 2, 3, 4, 5};
const int const_array[] = {10, 20, 30};
print_array(my_array, 5); // 正常,传入普通数组
print_array(const_array, 3); // 正常,传入 const 数组
// print_array 的参数不是 const int*,那么调用 print_array(const_array, 3) 就会编译错误
}
const 与函数返回值
将函数的返回值声明为 const,可以防止返回值被意外修改。
const int get_max(int a, int b) {
return (a > b) ? a : b;
}
int main() {
// 假设 get_max 返回一个很大的值,我们想用它作为某个数组的索引
const int max_val = get_max(100, 200);
// max_val = 150; // 错误!max_val 是 const
// 如果函数返回的是一个非常量对象,例如一个结构体
// const struct MyStruct s = get_struct();
// 这样可以防止 s 被意外修改。
}
const 与全局/静态变量
当 const 用于全局或静态变量时,它意味着这个变量的作用域仅限于当前文件(内部链接),并且其值不能被修改。

// file1.c const int global_const = 100; // file2.c extern int global_const; // 如果这样声明,链接时会报错,因为类型不匹配 // 正确的 extern 声明应该是: extern const int global_const; // 任何试图修改 global_const 的代码都会编译错误
注意:全局 const 变量和全局 #define 宏有本质区别:
const int GLOB = 10;:有存储空间,类型安全,有作用域。#define GLOB 10:简单的文本替换,无类型,无作用域。
const 与 #define 的区别
| 特性 | const 变量 |
#define 宏 |
|---|---|---|
| 类型 | 有类型,编译器会进行类型检查 | 无类型,只是简单的文本替换 |
| 作用域 | 有作用域(由 或文件决定) | 无作用域,从定义处到文件末尾都有效 |
| 存储 | 通常分配存储空间(除非是优化掉的只读变量) | 不分配存储空间,只是替换 |
| 调试 | 在调试器中可见,可以查看其值 | 在预处理阶段被替换,调试器中不可见 |
| 安全性 | 类型安全 | 不安全,可能产生意想不到的替换结果 |
在 C 语言中,优先使用 const,而不是 #define 来定义常量,除非你需要做一些 const 无法做到的事情(比如定义宏函数)。
const 的进阶:C++ 中的 const 与 C 语言的对比
重要提示:C 语言中的
const和 C++ 中的const是有区别的!很多 C++ 程序员会混淆这一点。
在 C++ 中,const 变量更接近于真正的“常量”,编译器可能会将 const 变量直接替换成其值(类似 #define),const 变量可以用于定义数组大小等需要编译时常量的地方。
// C++ 代码 const int SIZE = 100; int arr[SIZE]; // 合法,因为 SIZE 是编译时常量
在 C 语言 中,const 变量不一定是编译时常量,它只是一个只读变量,你不能用它来定义数组大小。
// C 语言代码 const int SIZE = 100; int arr[SIZE]; // 错误!C 语言要求数组大小必须是编译时常量,而 const int 不是
在 C 语言中,如果想要一个编译时常量,应该使用 enum 或 #define。
// C 语言中的正确做法
#define SIZE 100
int arr[SIZE];
// 或者使用 enum
enum { SIZE = 100 };
int arr[SIZE];
const 位置 |
含义 | 示例 |
|---|---|---|
const int a; |
a 是一个只读的整型变量 |
a = 10; (ok), a = 20; (error) |
const int *ptr; |
ptr 是一个指向只读整型的指针 |
*ptr = 10; (error), ptr = &b; (ok) |
int * const ptr; |
ptr 是一个本身只读的指针 |
*ptr = 10; (ok), ptr = &b; (error) |
const int * const ptr; |
ptr 是一个指向只读整型的、本身也只读的指针 |
*ptr = 10; (error), ptr = &b; (error) |
掌握 const 是写出健壮、安全、可读性高的 C 代码的重要一步,记住它的核心思想:“承诺不修改”,通过 const,你可以向编译器和代码的阅读者清晰地表达你的意图。
