变量的值可以随时改变,但有些数据从一开始就不该被改动。本节介绍 C++ 中定义"不能被修改的数据"的两种写法,以及为什么其中一种明显更值得优先使用。
2.5
前面讲的变量,值是可以随时改变的。但有些数据从一开始就不应该被改变——比如圆周率 3.14159、一个数组的最大长度 100——这类"定义好之后就不允许再修改"的数据,就应该定义成常量。C++ 里定义常量主要有两种写法:const 关键字和 #define 宏定义。
const 是 C++ 的关键字,加在类型前面,表示"这个变量的值一旦初始化就不能再修改"。语法是:const 类型 常量名 = 值;
| 1 | const double PI = 3.14159; |
| 2 | const int MAX_N = 100; |
| 3 | |
| 4 | cout << PI * 2 << endl; // 可以正常参与运算,输出 6.28318 |
| 5 | PI = 3.14; // ✗ 编译报错!const 变量初始化后不允许再赋值 |
PI 是一个真正的 double 类型变量,只是多了"不能被修改"这条限制——编译器会检查类型是否匹配,调试时也能像普通变量一样在调试器里看到它的名字和值。这也是 const 相比下面 #define 的核心优势。#define 是预处理指令,不是 C++ 语句——它在程序真正编译之前,由预处理器进行一次纯文本替换:把代码里所有出现的"常量名",原样替换成后面写的内容。语法是:#define 常量名 值(注意末尾不加分号)。
| 1 | #define PI 3.14159 // 注意:末尾没有分号 |
| 2 | #define MAX_N 100 |
| 3 | |
| 4 | int main() |
| 5 | { |
| 6 | int a[MAX_N]; // 预处理后,这一行实际变成 int a[100]; |
| 7 | cout << PI; // 预处理后,这一行实际变成 cout << 3.14159; |
| 8 | } |
PI 这个名字了——它已经被替换成了 3.14159 本身。所以 #define 定义的"常量"没有类型,编译器也不会做任何类型检查;如果代码写错了,报错信息里也只会显示替换后的内容(比如一长串 3.14159),而不会提示"PI 这里有问题",调试起来更麻烦。| 对比项 | const | #define |
|---|---|---|
| 处理阶段 | 编译阶段,是真正的 C++ 语句 | 预处理阶段,编译之前就已经被替换掉 |
| 类型检查 | 有类型,编译器会检查类型是否匹配 | 没有类型,只是字符串替换,不做任何检查 |
| 作用域 | 遵守 C++ 正常的作用域规则 | 没有作用域概念,从定义处到文件结尾(或被 #undef)全局生效 |
| 调试可见性 | 调试器里能看到变量名和值 | 调试器里只能看到替换后的字面值,看不到宏名 |
| 竞赛中的建议 | 优先使用 | 了解即可,老代码或某些场景仍会见到 |
MAX_N、PI、ARRAY_SIZE——一看到全大写的名字,就知道这是一个不会变的常量,而不是普通变量,这是 C/C++ 里非常通行的约定。#define 末尾如果多写了分号(#define MAX_N 100;),替换后分号也会一起被带入代码,可能导致语法错误,且报错位置常常和实际写错的地方对不上,容易让人摸不着头脑;② 试图给 const 变量重新赋值会在编译期直接报错,这其实是优点——比起运行到一半才发现数值被意外改动,编译期报错能更早、更清楚地暴露问题。