在 C 语言中,我们经常会见到 const 和 volatile 这两个关键字,那么我们今天就来介绍下这两个关键字。
先来介绍 const 关键字。提起 const 关键字,我们可能首先想到的是经过它修饰的变量便是常量了。其实我们这种想法是错误的,其实 const 修饰的变量是只读的,其本质还是变量。它修饰的局部变量是在栈上分配空间的,它修饰的全局变量在全局数据区分配空间(也就是我们平时所说的 read-only data 段),const 只在编译期有用,在运行期无效。注意:const 修饰的变量不是真的常量,它只是告诉编译器该变量不能出现在赋值符号的左边。
在现代的 C 语言编译器中,修改 const 全局变量将导致程序崩溃。但是得注意标准 C 语言编译器不会将 const 修饰的全局变量存储于只读存储区,而是存储于可修改的全局数据区,其值依然可以改变。
下来我们来做个示例代码,验证下我们所说的,代码如下:
#include <stdio.h> const int g_cc = 2; int main() { const int cc = 1; int* p = (int*)&cc; printf("cc = %d\n", cc); *p = 3; printf("cc = %d\n", cc); p = (int*)&g_cc; printf("g_cc = %d\n", g_cc); *p = 4; printf("g_cc = %d\n", g_cc); return 0; }
我们来分析下这个示例代码,cc 是 const 修饰的局部变量,是分配在栈上的。也就意味着它的属性是只读的,其本质还是变量。因而我们可以通过第13行的指针操作来改变它的值,所以第11行和第15行打印出的值分被为 1 和 3;而 g_cc 不同,因为它是个 const 修饰的全局变量,因而会被放在只读数据段。所以它的值不能被改变。以上的分析是基于现代的 C 语言编译器,如果是以前的 C 语言编译器(如 BCC)则不会这样处理,会将全局变量存储于可修改的全局数据区,其值依然可以改变。我们分别用现代编译器和 BCC 编译器编译,图一为现代 C 语言编译器gcc编译的结果,图二为 BCC 编译的结果。
图一
图二
我们可以看到同一份代码在两个编译器上竟然出现了两个结果,在 gcc 中出现了段错误,而在 BCC 中则通过指针顺利的改变了由 const 修饰的 g_cc 全局变量变量。由此证明了我们的分析是对的。
C语言中的 const 使得变量具有只读属性,现代 C 编译器中的 const 将具有全局生命周期的变量存储于只读存储区,const 不能定义真正意义上的常量!!!
下面我们来分析这个示例代码,代码如下:
#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); // ok modify((int*)&j, 2); // error modify((int*)&array[0], 3); // ok modify((int*)&g_array[0], 4); // error 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; }
在这份代码中,我们发现变量 i 的定义竟然是 int const 的,其实这样也就相当于 const int i;由于它是局部变量,分配在栈上。因而在被 const 修饰后,就具有只读属性,所以还是可以通过指针来改变的。但是变量 j 就不一样啦,它是由 static 修饰的,其具有全局的生命周期,所以是不能通过指针来改变的,因而第17行代码将会报错。第18行我们可以通过指针来改变 array 数组第一个元素,而 g_array 数组是全局的,所以就不能通过指针来改变它的值,而已第19行会报错。同样的上面的分析我们是基于现代的编译器,在 BCC 中不适用。下来我们就来编译下,看看是否如我们所分析的那样。
我们用 gcc 编译直接报段错误,那么我们来把第17和19行注释掉,再次编译。
下来我们再用 BCC 来编译下,结果如下
那么我们发现代码直接通过,而且值也都改变了。由此可以看出现代的编译器越来越强大了,已经将 const 的意思表达的更加完善了。下面这张图更好的阐释了 const 的意思
那么 const 修饰函数参数又是什么意思呢?它表示在函数体内不希望改变参数的值,const 修饰函数返回值表示返回值不可改变,多用于返回指针的情形。C 语言中的字符串字面量存储于只读存储区,在程序中需要使用 const char* 指针。下来我们来分析个示例,代码如下:
#include <stdio.h> const char* f(const int i) { i = 5; return "hello world"; } int main() { const char* pc = f(0); printf("%s\n", pc); pc[6] = '_'; printf("%s\n", pc); return 0; }
我们可以看出变量 i 是由 const 修饰的,因而第5行的代码肯定会报错。在第16行我们想要将中间的那个空格改变成“_”,但是返回的字符字面量是 const 型的,因而不能改变,在这块也会报错。我们编译下看看结果是否如此
我们可以看出这两行代码已经报错。我们注释掉这两行代码,再次编译,结果如下
我们接下来再来分析下 volatile 关键字。它可理解为“编译器警告指示字”,告诉编译器必须每次去内存中取变量值,主要修饰可能被多个线程访问的变量同时也可以修饰可能被未知因数更改的变量。我们在嵌入式的开发中,常常会用的多线程。那么在这个线程里一个变量的值被改变,我们在另一个线程里需要用到它原来的值,但是已经被改变了,这时怎么办呢?只能去内存中读取这个变量的值,这时用 volatile 修饰就OK了。在每次需要用到这个变量时,便会去内存中读取,虽然比较耗时一点,但是是安全的!
那么我们今天学习了 const 和 volatile 的相关知识,总结如下:1、const 使得变量具有只读属性;2、它不能定义真正意义上的常量;3、const 将具有全局生命期的变量存储于只读存储区;4、volatile 强制编译器减少优化,必须每次从内存中取值。后面我们会继续对 C 语言的学习。
欢迎大家一起来学习 C 语言,可以加我QQ:243343083。
原文地址:http://blog.51cto.com/12810168/2095264