← 目录 / 第二章 · 变量与数据类型 / 2.5 常量

2.5 常量:const 与 #define

变量的值可以随时改变,但有些数据从一开始就不该被改动。本节介绍 C++ 中定义"不能被修改的数据"的两种写法,以及为什么其中一种明显更值得优先使用。

常量:const 与 #define

2.5.1 为什么需要常量

前面讲的变量,值是可以随时改变的。但有些数据从一开始就不应该被改变——比如圆周率 3.14159、一个数组的最大长度 100——这类"定义好之后就不允许再修改"的数据,就应该定义成常量。C++ 里定义常量主要有两种写法:const 关键字和 #define 宏定义。

2.5.2 写法一:const(推荐)

const 是 C++ 的关键字,加在类型前面,表示"这个变量的值一旦初始化就不能再修改"。语法是:const 类型 常量名 = 值;

C++ · const 常量
1const double PI = 3.14159;
2const int MAX_N = 100;
3
4cout << PI * 2 << endl; // 可以正常参与运算,输出 6.28318
5PI = 3.14; // ✗ 编译报错!const 变量初始化后不允许再赋值
💡
const 变量和普通变量一样有类型、有作用域:PI 是一个真正的 double 类型变量,只是多了"不能被修改"这条限制——编译器会检查类型是否匹配,调试时也能像普通变量一样在调试器里看到它的名字和值。这也是 const 相比下面 #define 的核心优势。

2.5.3 写法二:#define(宏定义)

#define预处理指令,不是 C++ 语句——它在程序真正编译之前,由预处理器进行一次纯文本替换:把代码里所有出现的"常量名",原样替换成后面写的内容。语法是:#define 常量名 值(注意末尾不加分号)。

C++ · #define 宏定义
1#define PI 3.14159 // 注意:末尾没有分号
2#define MAX_N 100
3
4int main()
5{
6 int a[MAX_N]; // 预处理后,这一行实际变成 int a[100];
7 cout << PI; // 预处理后,这一行实际变成 cout << 3.14159;
8}
⚠️
#define 只是"文本替换",编译器根本不知道 PI、MAX_N 的存在:预处理阶段结束后,代码里已经找不到 PI 这个名字了——它已经被替换成了 3.14159 本身。所以 #define 定义的"常量"没有类型,编译器也不会做任何类型检查;如果代码写错了,报错信息里也只会显示替换后的内容(比如一长串 3.14159),而不会提示"PI 这里有问题",调试起来更麻烦。

2.5.4 两种写法怎么选

对比项 const #define
处理阶段 编译阶段,是真正的 C++ 语句 预处理阶段,编译之前就已经被替换掉
类型检查 有类型,编译器会检查类型是否匹配 没有类型,只是字符串替换,不做任何检查
作用域 遵守 C++ 正常的作用域规则 没有作用域概念,从定义处到文件结尾(或被 #undef)全局生效
调试可见性 调试器里能看到变量名和值 调试器里只能看到替换后的字面值,看不到宏名
竞赛中的建议 优先使用 了解即可,老代码或某些场景仍会见到
📌
命名习惯:不管用哪种写法,常量名通常都全部大写,多个单词用下划线连接,例如 MAX_NPIARRAY_SIZE——一看到全大写的名字,就知道这是一个不会变的常量,而不是普通变量,这是 C/C++ 里非常通行的约定。
⚠️
常见陷阱:#define 末尾如果多写了分号(#define MAX_N 100;),替换后分号也会一起被带入代码,可能导致语法错误,且报错位置常常和实际写错的地方对不上,容易让人摸不着头脑;② 试图给 const 变量重新赋值会在编译期直接报错,这其实是优点——比起运行到一半才发现数值被意外改动,编译期报错能更早、更清楚地暴露问题。