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

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
故障分析 | 记一次 MySQL 主从双写导致的数据丢失问题
作者:戴骏贤 网易游戏 技术部资深数据库系统工程师。 本文来源:原创投稿 *爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。 一、问题起源 不久前用户反馈部门的 MySQL 数据库发生了数据更新丢失。为了解决这个问题,当时对用户使用的场景进行了分析。发现可能是因为用户在两台互为主从的机器上都进行了写入导致的数据丢失。 如图所示,是正常和异常情况下应用写入数据库的示例。随后在更加深入调查问题的过程中,DBA 发现了故障引起数据丢失的原因: 如图 1-2 所示为故障具体过程的还原。从图中可以看出在第 3 步 DP 上的写入操作,在恢复 DA 到 DP 的同步之后,覆盖了第 4 步 DA 上的写入。因此导致了最终两台机器数据不一致,并且有一部分数据更新丢失。 在这里相信读者都会有一个疑问, 在第 4 步之后数据变成了(id : 1 ,name : name4),那么第 3 步操作的时候写入的语句是update t20200709 set name = 'name3' where id =1 and name='name2',在第 5 步恢复同步的时候这条语句在 D...
- 下一篇
【Flutter 实战】一文学会20多个动画组件
老孟导读:此篇文章是 Flutter 动画系列文章第三篇,后续还有动画序列、过度动画、转场动画、自定义动画等。 Flutter 系统提供了20多个动画组件,只要你把前面【动画核心】(文末有链接)的文章看明白了,这些组件对你来说是非常轻松的,这些组件大部分都是对常用操作的封装。 显示动画组件 回顾上一篇【动画核心】的文章中创建动画三个必须的步骤: 创建 AnimationController。 监听 AnimationController,调用 setState 刷新UI。 释放 AnimationController。 看第二步,每个动画都需要这个步骤,因此对其封装,命名为 MyAnimatedWidget: class MyAnimatedWidget extends StatefulWidget { final AnimationController controller; final Widget child; const MyAnimatedWidget( {Key key, @required this.controller, @required this.child}) :...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Red5直播服务器,属于Java语言的直播服务器
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Linux系统CentOS6、CentOS7手动修改IP地址
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS关闭SELinux安全模块
- 2048小游戏-低调大师作品