关键字const经常在面试和源码中出现的关键字,我们在平时的编程中经常使用,所以专门剖析一下在C语言和C++中他们的区别和联系。
目录
1、复习巩固一下const在C中的知识点
1)、本质:Const 修饰的只读变量,本质是变量,会分配空间,不是真正意义上的常量,不能够作为左值使用。不算常量,和常量有区别;
2)、Const只在编译期有效,在运行期无用;
3)、Const修饰的变量不是真正的常量,告诉编译器该变量不能出现在赋值符号的左边。到了运行的时候就管不住了。
const修饰的目标对象 | 在何处分配空间 | 注意事项 |
①、局部变量 | 栈上 | |
②、全局生命周期的变量 (包括全局变量和static局部变量) | 只读存储区 | 修改const只读变量将导致程序奔溃(标准C语言编译器(bcc)不会将const修饰的全局变量存储于只读存储区,而是存储于可修改的全局数据区,其值依然可以改变。) |
③、函数返回值或者函数参数 | 1、修饰函数参数表示在函数体内不希望改变参数的值; 2、修饰函数返回值表示返回值不可改变,多用于返回指针的情形 |
注:c语言中的字符串字面量存储于只读存储区中,在程序中需要使用const char* 指针。
拓展:C语言中如何定义真正意义上的常量?(重要)
答:其一通过枚举enum;其二通过宏定义。
2、实验对比分析
1 #include <stdio.h> // 实验一 : C 运行 Gcc编译
2
3 int main()
4 {
5 const int a = 0 ;
6 int* p = (int*)&a ;
7 printf("C Language Test Begin...\n");
8 printf( "a = %d \n" , a ) ;
9 printf("Operate...\n") ;
10 *p = 5;
11 printf("a = %d\n", a ) ;
12 printf("C Language Test End...\n") ;
13 return 0 ;
14 } // a的值被改变,说明const修饰的局部变量a并不是真正意义上的常量,值仍然可以改变
结果:
1 #include <stdio.h> // 实验二 :C++ 运行 G++编译
2 int main()
3 {
4 const int a = 0 ;
5 int* p = (int*)&a ;
6 printf("C++ Language Test Begin...\n");
7 printf( "a = %d \n" , a ) ;
8 printf("Operate...\n") ;
9 *p = 5;
10 printf("a = %d\n", a ) ;
11 printf("C++ Language Test End...\n") ;
12 return 0 ;
13 } // 运行后c的值仍旧为0,也就是说第九行没有产生作用
结果:
由实验一和实验二可见,同一个程序在不同的编译器编译过后产生不同的结果。在C语言编译器和C++编译器编译过后的结果截然不同。
#include <stdio.h> // 实验三 : C语言 增加输出*p的值
int main()
{
const int a = 0 ;
int* p = (int*)&a ;
printf("C Language Test Begin...\n");
printf( "a = %d , *p = %d \n" , a , *p ) ;
printf("Operate...\n") ;
*p = 5;
printf( "a = %d , *p = %d \n" , a , *p ) ;
printf("C Language Test End...\n") ;
return 0 ;
} //
结果:
#include <stdio.h> // 实验四 :C++ 运行 G++编译 增加*p变量的输出
int main()
{
const int a = 0 ;
int* p = (int*)&a ;
printf("C++ Language Test Begin...\n");
printf( "a = %d , *p = %d \n" , a , *p ) ;
printf("Operate...\n") ;
*p = 5;
printf( "a = %d , *p = %d \n" , a , *p ) ;
printf("C++ Language Test End...\n") ;
return 0 ;
}
结果:
由实验三和实验四可见,同一个程序在不同的编译器编译过后产生不同的结果。在C语言编译器和C++编译器编译过后的结果截然不同。a变量的值虽然不同,但取*p变量的值确是相同的。
分析:const这个关键字从C语言到C++语言的进化过程当中得到了很大的改变,得到了质的变化。接下来我们进行详细的讲解。
3、C++ 中的const
C++编译器在编译的过程中当碰到const修饰的标识符时,就会把这个标识符放到符号表(编译器内部的数据结构)中,之后继续向下编译;如果在后面发现有用到这个标识符时,就从符号表中将值取出来就行替换。(C++天生的使命就是兼容c语言,所以在c++中可能会给const修饰的标识符分配空间(如果会分配空间,但不会使用这个空间中的值,使用的是符号表里面的那个值,分配空间只是为了兼容c语言。1、const修饰的标识符为全局,并且在其他文件中使用它,也就是说用extern修饰一个全局的常量时,就会分配存储空间 ; 2、使用了&取地址符对const常量取地址时))的。为什么不使用这个空间?==>为了兼容C语言,之前在C语言编译器编译的程序在C++编译器编译也要通过。
拓展:符号表是编译器在编译的过程中产生的一张表,这张表是编译器内部的数据结构。(也就是编译器在编译的过程中会产生各种各样的数据结构,其中一张就算符号表)。
C++中const变量的演绎过程:
C++中const从只读变量成为了真正的常量了
感觉上C++中的const常量类似于宏定义
本质是不同的:因为const常量是被编译器处理的,编译器就会对const进行类型的检查和作用域的检查
宏定义是被预处理器处理(文本替换)的;宏替换之后得到是就是一个字面量,没有类型,也没有作用域,编译器根本就没有宏的定义,不知道它的存在
分析:以下程序有什么问题吗?
#include <stdio.h> // 实验五 C运行 gcc编译
void f()
{
#define a 3
✹ const int b = 4; // 作用域在f()内部有效,在f()之外的地方是看不到b的
}
void g()
{
printf("a = %d\n", a);
//printf("b = %d\n", b);
}
int main()
{
printf(" C Languege Test --------") ;
const int A = 1;
const int B = 2;
int array[A + B] = {0};
// const 修饰是只读变量,数组的大小到运行的时候才知道,数组大小我根本就不知道,让我怎么编译下去
int i = 0;
for(i=0; i<(A + B); i++)
{
printf("array[%d] = %d\n", i, array[i]);
}
f();
g();
return 0;
}
运行结果:
编译出错,提示数组没有初始化,数组大小到运行的时候才知道,编译器就不乐意了
#include <stdio.h> // 实验六 C++运行 g++ 编译
void f()
{
#define a 3
✹ const int b = 4; // 作用域在f()内部有效,在f()之外的地方是看不到b的
}
void g()
{
printf("a = %d\n", a);
// 宏被预处理器处理,直接进行文本替换编译器看到代码为printf("a = %d\n", 3);
//printf("b = %d\n", b); // error
}
int main()
{
printf("C++ Language Test ------\n") ;
const int A = 1;
const int B = 2;
int array[A + B] = {0}; //
int i = 0;
for(i=0; i<(A + B); i++)
{
printf("array[%d] = %d\n", i, array[i]);
}
f();
g();
return 0;
}
运行结果:
编译通过,这时候的数组的大小是由两个真正意义上的常量运算而成的;编译器编译到定义数组大小的地方就从符号表中取值去了,A为1,B为2;数组大小为3了。从这里看出,C++中const定义的是真正意义上的常量了。
C原因还只不过是只读变量。
这个其实就是const从C语言到C++语言得到本质上的变化
在我的另一篇博文中有特别详细的介绍C++中const的实质,请点击 -> 深入浅出的剖析C++中引用的本质
如有错误,请屏幕前的你给予指正,谢谢!