C语言之const和volatile"究极"学习
关于const的用法,现在大概前前后后应该写了有两篇文章,以前学习的时候,用法体会不是那么深刻,为啥这么说呢,因为在学习c++的时候,会发现const关键字有新的玩法,关于这个新的玩法,大家可以去看最近学习总结写的c++文章专辑。 一、const的用法: 1、const只读变量: const修饰的变量是只读的,本质上还是变量 const修饰的局部变量在栈上分配空间 const修饰的全局变量在全局数据区分配空间 const只在编译期有用,在运行期没有用 注:const修饰的变量不是真的常量,它只是告诉编译器该变量不能出现在赋值符号的左边 2、const全局变量的分歧: 在现代c语言编译器中,修改const全局变量将导致程序崩溃 标准c语言编译器不会将const修饰的全局变量存储于只读存储区中,而是存储于可修改的全局数据区,其值依然可以改变 3、代码示例: (1)只读变量代码示例: #include<stdio.h>intmain(){constinta=10;printf("a=%d\n",a);a=20;printf("a=%d\n",a);return0;} 运行结果: test.c:Infunction‘main’:test.c:8:4:error:assignmentofread-onlyvariable‘a’a=20;^ 注解:显示这个结果很正常,变量a被const修饰了,它就成了只读的。 (2)如果对变量a的值进行修改: #include<stdio.h>intmain(){constinta=10;int*p=(int*)&a;printf("a=%d\n",a);*p=20;printf("a=%d\n",a);return0;} 运行结果: root@txp-virtual-machine:/home/txp#./a.outa=10a=20 注解:通过指针的方式,就能够把a的值进行修改,这也论证了“const修饰的变量是只读的,本质上还是变量”这句话 (3)const修饰全局变量: 代码版本一 #include<stdio.h>constintb=40;intmain(){printf("b=%d\n",b);b=20;printf("b=%d\n",b);return0;} 输出结果: root@txp-virtual-machine:/home/txp#gcctest.ctest.c:Infunction‘main’:test.c:10:4:error:assignmentofread-onlyvariable‘b’b=20;^ 注解:跟const修饰栈上的变量用法一样 代码版本二 #include<stdio.h>constintb=40;intmain(){int*p=(int*)&b;printf("b=%d\n",b);*p=20;printf("b=%d\n",b);return0;} 运行结果: root@txp-virtual-machine:/home/txp#./a.outb=40Segmentationfault(coredumped) 注解:这里出现了段错误,这也验证了我们上面所说的“修改const全局变量将导致程序崩溃”。 同时为了验证“标准c语言编译器不会将const修饰的全局变量存储于只读存储区中,而是存储于可修改的全局数据区,其值依然可以改变”这句话,我把这段代码放到dev c++上进行试验: 说明:我这个版本的编译器支持标准c语言,所以没导致程序崩溃,能够正常运行 4、const的本质 c语言中的const使得变量具有只读属性 现代c编译器中的const将具有全局生命周期的变量存储于只读存储区,不是放在全局数据区 注:const不能定义真正意义上的常量;同时这里注意static关键字修饰的变量,它的生命周期和全局变量一样。 代码示例: #include<stdio.h>constintArray[5]={0};voidfun(int*p,intv){*p=v;}intmain(){intconsti=1;conststaticintj=2;intconstarray[5]={0};fun((int*)&i,1);fun((int*)&j,2);fun((int*)&array[2],3);fun((int*)&Array[1],4);return0;} 输出结果: root@txp-virtual-machine:/home/txp#./a.outSegmentationfault(coredumped) 注解:这里会有段错误,错误出现在const+static修饰的j变量对其进行修改,还有const修饰的全局数组。 5、const修饰函数参数和返回值 const修饰函数参数表示在函数体内不希望改变参数的值 const修饰函数返回值表示返回值不可改变,多用于返回指针的情形 在c语言中的字符串字面量存储于只读存储区中,在程序中需要使用const char* 指针,例如: constchar*s="TXP嵌入式";//字符串字面量 代码示例: #include<stdio.h>constchar*fun(constinti){i=8;return"TXP";}intmain(){constchar*p=fun(0);printf("%s\n",p);p[1]='_';printf("%s\n",p);return0;} 输出结果: root@txp-virtual-machine:/home/txp#gcctest.ctest.c:Infunction‘fun’:test.c:5:4:error:assignmentofread-onlyparameter‘i’i=8;^test.c:Infunction‘main’:test.c:12:5:error:assignmentofread-onlylocation‘*(p+1u)’p[1]='_';^ 注解:上面这样写,肯定有问题。 代码进化: #include<stdio.h>constchar*fun(constinti){//i=8;return"TXP";}intmain(){constchar*p=fun(0);printf("%s\n",p);//p[1]='_';//printf("%s\n",p);return0;} 输出结果: root@txp-virtual-machine:/home/txp#./a.outTXP 二、volatile的用法 老实说,这个关键字在面试题目里面经常会出现,但是平时学习的时候,如果你没有真正理解这其中的含义,在笔试的时候,脑袋里面可能依稀是记得有那么几个结论,但是有时候吧,一紧张就把结论给忘了,也不是不可能,所以说,咋们还是老实一点,得真正把它原理搞明白才行,这样上来战场就不怕了,以后写代码也就少一点bug。 1、volatile的常用结论(volatile英文本意就是易变的意思) 这里我先给结论,然后再给一个例子,把这个例子的讲明白,所有结论就都明白了。 volatile可理解为“编译器警告指示字” volatile告诉编译器必须每次去内存中取变量值 volatile主要修饰可能被多个线程访问的变量 volatile也可以修饰可能被未知因素更改的变量 volatile可以修饰一个中断子程序中会访问到的非自动变量 2、分析原理 大家可能平时在博客学习,都会发现讲解编译器优化的,然后加了volatile关键来修饰变量,就告诉编译器不要去优化这个变量了,那么这里的优化到底是什么意思呢? 从字面上来理解优化两个字,意思就是最优值(变量的值不会改变),这里我用一个简单代码来说明一下: #include<stdio.h>intmain(){inta=1;//volatileinta=0;while(a){}} 说明:上面的代码,如果变量a没有加volatile修饰的话,编译器就会优化它(也就是a的值一直不变),所以while就一直死循环;然后我如果加了volatile来修饰的话,编译器就不会去优化变量a,不优化的意思就是说,变量a的值可能就会改变,while就不会一直死循环。 当然这里为了好理解,我说的不是很专业,没有从寄存器和内存的角度去说。(我也不想那么去讲解,简单理解了就行) 总之一句话:上面的结论中,volatile修饰的都是变量,变量就可能改变,不会被编译器优化;只是说我们上面的结论应用场景不同而已。 三、总结 const使得变量具有只读属性 const不能定义真正意义上的常量 const将具有全局生命周期的变量存储于只读存储区 volatile强制编译器减少优化,必须每次从内存中取值 好了,今天的分享就到这里,如果文章中有错误或者不理解的地方,可以交流互动,一起进步。我是txp,下期见! 本文分享自微信公众号 - TXP嵌入式(txp1121518wo-)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。