C++中的const
const概述
const单词字面意思为常数,不变的。它是C/C++中的一个关键字,是一个限定符,它用来限定一个变量不允许改变,它将一个对象转换成一个常量。
const int a = 10;
A = 100; //编译错误,const是一个常量,不可修改
C/C++中const的区别
C中的const
常量的引进是在C++早期版本中,当时标准C规范正在制定。那时,尽管C委员会决定在C中引入const,但是他们C中的const理解为”一个不能改变的普通变量”,也就是认为const应该是一个只读变量,既然是变量那么就会给const分配内存,并且在C中const是一个全局只读变量,C语言中const修饰的只读变量是外部连接的。
如果这么写:
const int arrSize = 10;
int arr[arrSize];
看似是一件合理的编码,但是这将得出一个错误。 因为arrSize占用某块内存,所以C编译器不知道它在编译时的值是多少?
C++中的const
在C++中,一个const不必创建内存空间,而在C中,一个const总是需要一块内存空间。在C++中,是否为const常量分配内存空间依赖于如何使用。一般说来,如果一个const仅仅用来把一个名字用一个值代替(就像使用#define
一样),那么该存储局空间就不必创建。
如果存储空间没有分配内存的话,在进行完数据类型检查后,为了代码更加有效,值也许会折叠到代码中。
不过,取一个const地址,或者把它定义为extern,则会为该const创建内存空间。
C/C++中const异同总结
C语言会被存储到只读数据段。C++中全局const当声明extern或者对变量取地址时,编译器会分配存储地址,变量存储在只读数据段。两个都受到了只读数据段的保护,不可修改。
cconst int constA = 10; int main(int argc, char *argv[]) { int* p = (int*)&constA; *p = 200; }
以上代码在C/C++中编译通过,在运行期,修改constA的值时,发生写入错误。原因是修改只读数据段的数据。
C语言中存储在堆栈区,只是不能通过变量直接修改const只读变量的值,但是可以跳过编译器的检查,通过指针间接修改const值。
cconst int constA = 10; int* p = (int*)&constA; *p = 300; printf("constA:%d\n", constA); printf("*p:%d\n", *p);
运行结果:
shellconstA:300 *p:10
C语言中,通过指针间接赋值修改了constA的值。
C++中对于局部的const变量要区别对待:
对于基础数据类型,也就是
const int a = 10
这种,编译器会把它放到符号表中,不分配内存,当对其取地址时,会分配内存。cconst int constA = 10; int* p = (int*)&constA; *p = 300; cout << "constA:" << constA << endl; cout << "*p:" << *p << endl;
运行结果:
shellconstA:10 *p:300
constA在符号表中,当我们对constA取地址,这个时候为constA分配了新的空间,*p操作的是分配的空间,而constA是从符号表获得的值。
对于基础数据类型,如果用一个变量初始化const变量,如果
const int a = b
,那么也是会给a分配内存。cint b = 10; const int constA = b; int* p = (int*)&constA; *p = 300; cout << "constA:" << constA << endl; cout << "*p:" << *p << endl;
运行结果:
shellconstA:300 *p:300
constA 分配了内存,所以我们可以修改constA内存中的值。
对于自定数据类型,比如类对象,那么也会分配内存。
cconst Person person; //未初始化age //person.age = 50; //不可修改 Person* pPerson = (Person*)&person; //指针间接修改 pPerson->age = 100; cout << "pPerson->age:" << pPerson->age << endl; pPerson->age = 200; cout << "pPerson->age:" << pPerson->age << endl;
运行结果:
shellpPerson->age:100 pPerson->age:200
为person分配了内存,所以我们可以通过指针的间接赋值修改person对象。
C中const默认为外部连接,C++中const默认为内部连接。当C语言两个文件中都有
const int a
的时候,编译器会报重定义的错误。而在C++中则不会,因为C++中的const默认是内部连接的。如果想让C++中的const具有外部连接,必须显示声明为:extern const int a = 10;
const由C++采用,并加进标准C中,尽管他们很不一样。在C中,编译器对待const如同对待变量一样,只不过带有一个特殊的标记,意思是”你不能改变我”。在C++中定义const时,编译器为它创建空间,所以如果在两个不同文件定义多个同名的const,链接器将发生链接错误。简而言之,const在C++中用的更好。
了解: 能否用变量定义数组:
在支持C99标准的编译器中,可以使用变量定义数组。
微软官方描述vs2013编译器不支持C99:
Microsoft C conforms to the standard for the C language as set forth in the 9899:1990 edition of the ANSI C standard.
以下代码在Linux GCC支持C99编译器编译通过
cint a = 10; int arr[a]; int i = 0; for (; i < 10; i++) arr[i] = i; i = 0; for (; i < 10; i++) printf("%d\n", arr[i]);
尽量以const替换#define
在旧版本C中,如果想建立一个常量,必须使用预处理器#define MAX 1024;
解决办法就是用一个常量替换上面的宏。
const int max= 1024;
const和#define区别总结
const有类型,可进行编译器类型安全检查。
#define
无类型,不可进行类型检查const有作用域,而
#define
不重视作用域,默认定义处到文件结尾。如果定义在指定作用域下有效的常量,那么#define
就不能用宏常量没有类型,所以调用了int类型重载的函数。const有类型,所以调用希望的short类型函数
cpp#include <iostream> using namespace std; #define PARAM 128 const short param = 128; void func(short a) { cout << "short!" << endl; } void func(int a) { cout << "int" << endl; } int main(int argc, char *argv[]) { func(PARAM); func(param); return 0; }
程序输出:
shellint short!
宏常量不重视作用域
cpp#include <iostream> using namespace std; void func1() { const int a = 10; #define A 20 //#undef A //卸载宏常量A } void func2() { //cout << "a:" << a << endl; //不可访问,超出了const int a作用域 cout << "A:" << A << endl; //#define作用域从定义到文件结束或者到#undef,可访问 } int main(int argc, char *argv[]) { func2(); return 0; }
程序输出:
shellA:20
问题: 宏常量可以有命名空间吗?
cpp#include <iostream> using namespace std; namespace MySpace { #define num 1024 } void test() { //cout << MySpace::NUM << endl; //错误 //int num = 100; //命名冲突 cout << num << endl; } int main(int argc, char *argv[]) { test(); return 0; }
程序输出:
shell1024
注意
const与指针转换时
const int * <= int * // ok
int * <= const int * // error
面试题
c和c++中const的区别
点击查看答案
整理结构的话,可能分为以下几点:
- 语义差异:只读变量 vs 真正常量
- 初始化要求:C允许不初始化,C++必须初始化
- 作用域与链接属性:外部 vs 内部
- 类型系统严格性:C++更严格,禁止隐式转换
- 内存处理机制:符号表优化 vs 分配内存
- 应用扩展:C++中的更多用法(函数、类成员等)
1. 语义差异:只读变量 vs 真正常量
- C语言:
const
修饰的变量是“只读变量”,其值运行时仍可能被修改。例如通过指针强制类型转换可间接修改值,编译器仅警告而非报错。cconst int a = 10; int *p = (int*)&a; // C允许强制转换 *p = 20; // 输出结果可能不一致(如输出20或30,取决于优化)
- C++:
const
是真正的编译期常量,值不可修改。直接通过指针修改会导致未定义行为(UB),且编译器会严格报错。cppconst int a = 10; int *p = &a; // C++直接报错:无法从const int*转换到int*
在编译时期,编译器会将const
变量替换为其值,从而消除对const
变量的引用。但是也有例外的情况,在使用一个变量初始化另一个const
变量时,编译器会保留对const
变量的引用。
2. 初始化要求
- C:允许不初始化
const
变量(视为未定义值的只读变量)。cconst int b; // 合法,但后续无法赋值
- C++:必须初始化
const
变量,否则编译报错。cppconst int b; // 错误:未初始化的常量
3. 作用域与链接属性
- C:
const
变量默认是外部链接(全局可见),其他文件可通过extern
引用。 - C++:
const
变量默认是内部链接(仅本文件可见),若需全局可见需显式加extern
。cppextern const int g_value = 42; // 其他文件可通过extern引用
4. 类型系统严格性
- C:允许隐式类型转换,如
const
指针与非const
指针可互相赋值。 - C++:强制类型匹配,需显式使用
const_cast
才能绕过const
限定符。cppconst int a = 10; int *p = const_cast<int*>(&a); // 合法但危险
5. 内存处理机制
- C:
const
变量始终分配内存,即使未使用其地址。 - C++:采用 常量折叠(Constant Folding) 优化,
const
变量通常不分配内存,直接替换为字面值;仅当取地址或用于需要内存的操作时才会分配。cppconst int a = 10; int array[a] = {}; // 合法:a被替换为10,不分配内存
6. 应用扩展(C++独有)
- 函数参数与返回值:
- 使用
const
修饰参数防止意外修改(如void func(const int& x)
)。 const
返回值保护返回的指针或引用(如const int* getData()
)。
- 使用
- 类成员:
const
成员函数(如int get() const
)保证不修改对象状态。const
对象只能调用const
成员函数。
- 引用与指针:
- 常引用可绑定到临时对象(如
const int& r = 10;
)。
- 常引用可绑定到临时对象(如
加分项:实际陷阱与优化建议
- C++的未定义行为:通过指针强制修改
const
变量可能导致程序崩溃或数据不一致。 - 代码安全性:C++的
const
能显著减少逻辑错误,例如用于接口设计时明确参数权限。 - 与
constexpr
的关系:C++11引入的constexpr
进一步强化编译期计算能力,但需注意其与const
的区别(如constexpr
必须编译期确定值)。
满分答案结构示例 候选人可按照以下逻辑组织回答:
- 核心语义差异 → 2. 初始化与作用域 → 3. 类型检查严格性 → 4. 内存处理机制 → 5. C++扩展用法 → 6. 实际应用建议。
需结合代码示例(如指针操作、成员函数)和编译行为(如报错、优化)对比,体现对底层机制的理解。