C语言中const
const修饰的变量是只读的, 本质还是变量
const修饰的局部变量在栈上分配空间
const修饰的全局变量在全局数据区分配空间
const只在编译期有用,在运行期无用
const修饰的变量不是真的常量,它只是告诉编译器该变量不能出现在赋值符号的左边。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <stdio.h> int main () { const int cc = 1 ; int *p = (int *)&cc; printf ("cc = %d\n" ,cc); *p = 2 ; printf ("cc = %d\n" ,cc); return 0 ; }
通过指针修改const局部变量 的值不会报错并且可以修改成功
1 2 3 4 fengyun@ubuntu:~/share$ gcc test.c -o test fengyun@ubuntu:~/share$ ./test cc = 1 cc = 2
在现代C语言编译器 中,修改const全局变量将导致程序崩溃
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <stdio.h> const int g_cc = 1 ;int main () { int *p = (int *)&g_cc; printf ("g_cc = %d\n" ,g_cc); *p = 2 ; printf ("g_cc = %d\n" ,g_cc); return 0 ; }
1 2 3 4 fengyun@ubuntu:~/share$ gcc test.c -o test fengyun@ubuntu:~/share$ ./test g_cc = 1 段错误 (核心已转储)
注意:标准C语言编译器 (比如bcc32编译器)不会将const修饰的全局变量 存储于只读存储区中,而是存储于可修改的全局数据区,其值依然可以改变。
结论:
C语言中的const使得变量具有只读属性
现代C编译器中的const将具有全局生命周期的变量存储于只读存储区 (比如static 修饰的局部变量,全局变量)
C语言中const不能定义真正意义上的常量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <stdio.h> const int g_array[5 ] = {0 };void modify (int * p, int v) { *p = v; } int main () { int const i = 0 ; const static int j = 0 ; int const array [5 ] = {0 }; modify((int *)&i, 1 ); modify((int *)&j, 2 ); modify((int *)&array [0 ], 3 ); modify((int *)&g_array[0 ], 4 ); printf ("i = %d\n" , i); printf ("j = %d\n" , j); printf ("array[0] = %d\n" , array [0 ]); printf ("g_array[0] = %d\n" , g_array[0 ]); return 0 ; }
修改全局变量和static变量,在gcc编译器下运行
1 2 3 fengyun@ubuntu:~/share$ gcc test.c -o test fengyun@ubuntu:~/share$ ./test 段错误 (核心已转储)
但如果在bcc32编译器中运行不会出错,因为bcc编译器很好地支持了C语言标准变量规范。
const修饰函数参数表示在函数体内不希望改变参数的值 const修饰函数返回值表示返回值不可改变,多用于返回指针的情形 小贴士:C语言中的字符串字面量存储于只读存储区中,在程序中需要使用const char*指针。
1 2 3 4 5 6 #include <stdio.h> int main () { const char * s = "feng yun" ; return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <stdio.h> const char * f (const int i) { return "feng yun" ; } int main () { char * pc = f(0 ); printf ("%s\n" , pc); pc[6 ] = '_' ; printf ("%s\n" , pc); return 0 ; }
如果直接编译会有一个警告,运行后会出现段错误,原因就是修改了const char*的值。
1 2 3 4 5 6 7 8 fengyun@ubuntu:~/share$ gcc test.c -o test test.c: In function ‘main’: test.c:12 :16 : warning: initialization discards ‘const ’ qualifier from pointer target type [-Wdiscarded-qualifiers] 12 | char * pc = f(0 ); | ^ fengyun@ubuntu:~/share$ ./test feng yun 段错误 (核心已转储)
volatile
volatile可理解为“编译器警告指示字”
volatile告诉编译器必须每次去内存中取变量值
volatile主要修饰可能被多个线程访问的变量
volatile也可以修饰可能被末知因数更改的变量
对于const volatile int i = 0
我们在内存里定义了一个只读变量i,不能出现是左值,编译器每次都到内存里面取值而不做任何优化。
小结
const使得变量具有只读属性
const不能定义真正意义上的常量
const将具有全局生命期的变量存储于只读存储区
volatile强制编译器减少优化,必须每次从内存中取值
C++中const C与C++
C语言中的const使得变量具有只读属性
const将具有全局生命周期的变量存储于只读存储区
const不能定义真正意义上的常量!
而我们在C语言中定义真正的常量只能用枚举enum。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> int main () { const int c = 0 ; int * p = (int *)&c; printf ("Begin...\n" ); *p = 5 ; printf ("c = %d\n" , c); printf ("*p = %d\n" , *p); printf ("End...\n" ); return 0 ; }
同一个程序在C语言和C++语言的运行结果竟然会截然不同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 fengyun@ubuntu:~/share$ gcc test.c -o test fengyun@ubuntu:~/share$ ./test Begin... c = 5 *p = 5 End... fengyun@ubuntu:~/share$ g++ test.c -o test fengyun@ubuntu:~/share$ ./test Begin... c = 0 *p = 5 End...
C语言const int c,c的值修改成功,但C++并没有成功。但是int* p = (int*)&c,指针p指向的值似乎都修改成功了。
这是什么原因?
C++在C的基础上对const进行了进化处理
-当碰见const声明时在符号表 中放入常量 -编译过程中若发现使用常量则直接以符号表中的值替换 -编译过程中若发现下述情况则给对应的常量分配存储空间 ●对const常量使用了 extern,是全局并且在其它文件中使用 ●对const常量使用&操作符 注意:C+ +编译器虽然可能为const常量分配空间,但不会使用其存储空间中的值。 这是为了兼容C语言,C语言可以通过指针访问甚至修改const变量,C++也可以通过指针访问const常量分配的空间,但是这个空间的值与const常量无关,const常量是存储在符号表内部。
const与define C+ +中的const常量类似于宏定义 一const int C = 5;≈#define C 5
C + +中的const常量在与宏定义不同 一const常量是由编译器 处理 编译器对const常量进行类型检查和作用域检查 -宏定义由预处理器 处理,单纯的文本替换,不占用任何内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <stdio.h> void f () { #define a 3 const int b = 4 ; } void g () { printf ("a = %d\n" , a); printf ("b = %d\n" , b); } int main () { 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 ; }
编译运行这个程序,可以看到
gcc编译器会出错,因为对于int array[A + B]
,A和B均为只读变量,两个变量的值只有在运行的时候才知道,编译器不知道值因此会报错
g++编译器不会出错,编译器会从内部的符号表取值,取得A=1,B=2,因此编译器不会出错
1 2 3 4 5 6 7 8 9 10 11 12 fengyun@ubuntu:~/share$ gcc test.c -o test test.c: In function ‘main’: test.c:19:5: error: variable-sized object may not be initialized 19 | int array[A + B] = {0}; | ^~~ test.c:19:25: warning: excess elements in array initializer 19 | int array[A + B] = {0}; | ^ test.c:19:25: note: (near initialization for ‘array’) fengyun@ubuntu:~/share$ g++ test.c -o test fengyun@ubuntu:~/share$
运行g++编译出来的程序
1 2 3 4 5 fengyun@ubuntu:~/share$ ./test array [0 ] = 0 array [1 ] = 0 array [2 ] = 0 a = 3
而注意#define a 3
宏是由预处理器处理,编译器根本不知道宏这个东西,a被全部替换,没有作用域和类型的概念 。
const引用【特殊】
在C++中可以声明const引用
const Type& name = var ;
const引用让变量别名拥有只读属性
1 2 3 4 5 6 7 int a = 4 ;const int & b = a; int * p = (int *) &b;b = 5 ; *p = 5 ;
注意const int& b = a;
让b拥有只读属性但不修改a的属性
当使用字面常量对const引用进行初始化时,C++编译器会为常量值分配空间,并将引用名作为这段空间的别名
1 2 3 4 5 6 const int & b = 1 ; int * p = (int *) &b;b = 5 ; *p = 5 ;
结论:使用常量对const引用初始化后将生成一个只读变量 ! ! !
const的一点疑问 const什么时候是常量?const什么时候是只读变量?
const常量的判别准则
只有用字面量 初始化的const常量才会进入符号表
使用其它变量初始化的const 常量仍然是只读变量
被volatile 修饰的const 常量不会进入符号表
在编译期间不能直接确定初始值 的const标识符,都被作为只读变量 处理。
const引用的类型与初始化变量的类型
相同:初始化变量成为只读变量
不同:生成一个新的只读变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <stdio.h> int main () { const int x = 1 ; const int & rx = x; int & nrx = const_cast <int &>(rx); nrx = 5 ; printf ("x = %d\n" , x); printf ("rx = %d\n" , rx); printf ("nrx = %d\n" , nrx); printf ("&x = %p\n" , &x); printf ("&rx = %p\n" , &rx); printf ("&nrx = %p\n" , &nrx); volatile const int y = 2 ; int * p = const_cast <int *>(&y); *p = 6 ; printf ("y = %d\n" , y); printf ("p = %p\n" , p); const int z = y; p = const_cast <int *>(&z); *p = 7 ; printf ("z = %d\n" , z); printf ("p = %p\n" , p); char c = 'c' ; char & rc = c; const int & trc = c; rc = 'a' ; printf ("c = %c\n" , c); printf ("rc = %c\n" , rc); printf ("trc = %c\n" , trc); return 0 ; }
const关键字能否修饰类的对象?如果可以,有什么特性?
const关键字能够修饰对象
const修饰的对象为只读对象
只读对象的成员变量不允许被改变
只读对象是编译阶段的概念 ,运行时无效
C++const成员函数
const对象只能调用const的成员函数
const成员函数中只能调用const成员函数
const成员函数中不能直接改写成员变量的值