在 C 语言中,const 用来修饰变量,表示该变量值不可被修改,我们通常称之为常量(Constant)。《C 语言 const 的用法详解》一节已对此进行了详细阐述。在 C++ 中,const 的基本含义不变,但在编译器处理和链接过程上有一些重要差异,以下针对最主要的两点进行说明。
1. 编译阶段的“替换” vs. 运行阶段的内存读取
const int m = 10;
int n = m;
- 在 C 语言(C99 及以后)中,
const int m 仍是一个内存对象;执行 int n = m; 时,编译器生成代码读取 m 所在内存,然后将其值存入 n 。
- 在 C++ 中,编译器会将
m 看作编译期常量,将 int n = m; 等价替换为 int n = 10; ,不再生成对内存的读取指令——这与 #define m 10 的效果类似。
这样做的优点在于减少运行时开销,但缺点是如果强制通过指针修改了 m ,随后对 m 的访问仍会使用编译期间替换的值:
// test.c
#include <stdio.h>
int main() {
const int n = 10;
int *p = (int*)&n;
*p = 99;
printf("C 输出: %d\n", n); // 输出 99(读取内存)
return 0;
}
// test.cpp
#include <stdio.h>
int main() {
const int n = 10;
int *p = (int*)&n;
*p = 99;
printf("C++ 输出: %d\n", n); // 输出 10(编译期替换)
return 0;
}
注意:虽然 const 在语法层面阻止修改,但可以通过强制转换指针绕过。此处示例仅用于说明编译器处理差异,实际代码中请勿滥用。
2. 全局 const 变量的链接属性
-
C 语言:全局变量默认具有外部链接(external linkage),即使使用了 const ,在其他源文件中使用 extern 声明后也可访问。
-
C++:全局 const 对象默认具有内部链接(internal linkage),等同于隐含了 static ,只能在声明它的翻译单元(源文件)内可见。若将其放入头文件,多次包含也不会导致重定义错误:
// module.h
const int N = 10;
void func();
// module.cpp
#include "module.h"
void func() { printf("module: %d\n", N); }
// main.cpp
#include "module.h"
int main() {
func();
printf("main: %d\n", N);
return 0;
}
若要在 C++ 中让全局 const 具有外部链接,可显式添加 extern (GCC 支持,但 MSVC/Clang 对此行为有所差异):
// globals.h
extern const int M = 20; // 显式外部链接
3. C++11 及以后的 constexpr
从 C++11 起,引入了 constexpr 关键字,用于定义常量表达式:
constexpr 对象在符合规则时可以用于编译期计算,比 const 更严格。
- 可修饰函数、构造函数、变量。
constexpr int square(int x) { return x * x; }
constexpr int SIZE = square(5); // 在编译期求值,等同于 25
使用 constexpr 可以更好地表达“编译期常量”的语义,并在泛型编程和模板元编程中发挥重要作用。
4. 总结
- 语义清晰:在 C++ 中,优先使用
constexpr (C++11+)和 const ,而非 #define ,以获得类型检查和调试友好性。
- 注意链接:了解全局
const 的内部链接特性,必要时可用 extern 或命名空间管理可见性。
- 不要滥用指针修改:即使可以通过指针修改
const 对象,也严重违反常量语义,应极力避免。
|