C语言进阶——const 和 volatile 分析09
const只读变量:
- const修饰的变量是只读的,本质还是一个变量
- const修饰的局部变量在栈上分配空间
- const修饰的全局变量在全局函数区分配资源空间
- const只在编译器有用,在运行期无用
注意:const修饰的变量不是真的常量,他只是告诉编译器该变量不能出现在赋值符号的左边
const全局变量的分歧:
- 在现代C语言编译器中,修改const全局变量将导致程序崩溃
- 标准C语言编译器不会将const修饰的全局变量存贮于只读存贮区中,而是存贮可修改的全局数据区,其值依然可以改变
程序实例1:
1 #include <stdio.h> 2 3 const int g_cc = 2; 4 5 int main() 6 { 7 const int cc = 1; 8 9 int* p = (int*)&cc; 10 11 printf("cc = %d\n", cc); 12 13 *p = 3; 14 15 printf("cc = %d\n", cc); 16 17 p = (int*)&g_cc; 18 19 printf("g_cc = %d\n", g_cc); 20 21 *p = 4; //error 22 23 printf("g_cc = %d\n", g_cc); 24 25 return 0; 26 }
这段代码说明,const修饰的局部变量可以使用指针修改,但是const修饰全局变量不可以通过指针进行修改,第21行代码试图修改const修饰全局变量,由于我使用的codeblocks,是一款现代编译器,所以编译阶段会报错,删掉21行代码程序就可以正常运行
const的本质:
- C语言的const使得变量具有只读属性
- 现代C编译器中的const将具有全局生命周期的变量存贮于只读存贮区
- const不能顶第一真正意义上的常量
程序示例2:
1 #include <stdio.h> 2 3 const int g_array[5] = {0}; 4 5 void modify(int* p, int v) 6 { 7 *p = v; 8 } 9 10 int main() 11 { 12 int const i = 0; 13 const static int j = 0; 14 int const array[5] = {0}; 15 16 modify((int*)&i, 1); // ok 17 modify((int*)&j, 2); // error 18 modify((int*)&array[0], 3); // ok 19 modify((int*)&g_array[0], 4); // error 20 21 printf("i = %d\n", i); 22 printf("j = %d\n", j); 23 printf("array[0] = %d\n", array[0]); 24 printf("g_array[0] = %d\n", g_array[0]); 25 26 return 0; 27 }
第3行和第13行声明了用const修饰的具有全局生命周期的变量,所以在试图用指针来修改他们的值的时候,编译器会报错
const修饰函数参数和返回值:
- const修饰函数参数表示在函数体内不希望改变参数的值
- const修饰函数返回值表示返回值不可改变,多用于返回指针的情形
- 小贴示:C语言的字符串字面量存贮于只读存贮区中,在程序中需要使用const char* 指针
程序实例3:
1 #include <stdio.h> 2 3 const char* f(const int i) 4 { 5 i = 5; 6 7 return "Delphi Tang"; 8 } 9 10 int main() 11 { 12 char* pc = f(0); 13 14 printf("%s\n", pc); 15 16 pc[6] = '_'; 17 18 printf("%s\n", pc); 19 20 return 0; 21 }
这段代码是错误的,但是我们来剖析一下,首先编译的时候第5行会出错,我们注释掉,接下来再编译,在12行会给出一个警告,我们用一个char *类型来接受了一个字面值常量,因为只是警告,所以我们可以运行一下,最后运行的时候还是出错了,因为第16行尝试修改了一个字面值常量
深藏不露的volatile:
- volatile 可以理解为 “编译器警告指示”
- volatile 告诉编译器必须每次去内存中去该变量值
- volatile 主要修饰可能被多个线程访问的变量
- volatile 也可以修饰可能被未知因数更改的变量
示例程序4:
int boj = 10; //编译器在编译的时候发现obj int a=0; int b=0; a= obj; //“聪明“的编译器不会到内存中去找obj对应的值,而是直接替换为10 sleep(100); //在另一个线程里面可能已经改变了obj的值 b= obj; //这个时候编译器还是自作聪明的用10来代替obj,而不去内存中去找obj对应的值
这段代码只是一个小的片段,但是已经可以帮助我们理解有的时候编译器的自作聪明可能会导致某些bugconst volatile int i = 0;但是如果在定义一个变量的时候我们加上volatile属性,编译器每次就会去内存中去找到变量对应的值。
小结:
- const 使得变量具有只读属性
- const 不能定义真正意义上的常量
- const将具有全局生命周期的变量存贮于只读存贮区
- volatile 强制编译器减少优化,必须每次从内存中取值