【软件开发底层知识修炼】二十八 C/C++中volatile的作用

  • 上一篇文章学习了C/C++中的指针与数组的区别,点击链接进行查看:【软件开发底层知识修炼】二十七 C/C++中的指针与数组是不同的
  • 本篇文章将学习volatile关键字在C/C++中的作用

文章目录

  • 1 实例代码分析
  • 2 问题分析
  • 3 解决方案
  • 4 拓展: const和volatile
  • 4 总结

1 实例代码分析

在讲解volatile关键字的作用之前,我们先来看一个代码的例子,代码如下:

  • main.c
#include <stdio.h>
#include <pthread.h>extern const int g_ready;int main()
{launch_device();while( g_ready == 0 ){sleep(1);printf("main()  : g_ready = %d\n", g_ready);}printf("main()  : g_ready = %d\n", g_ready);return 0;
}
  • device.c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>int g_ready = 0;void* th_fn(void* args)
{sleep(5);g_ready = 1;printf("th_fn()  : g_ready = %d\n", g_ready);
}void launch_device()
{pthread_t tid = 0;pthread_create(&tid, NULL, th_fn, NULL);
}

上面的代码,是非常容易懂的,这里就不再多说。我们直接编译运行看看:

  • gcc -pthread main.c device.c -o test.out
  • ./test.out

运行结果如下:

在这里插入图片描述

这个结果对于我们来说其实挺容易懂的。就是main中的while循环先每隔一秒打印执行一次,然后5秒后th_fn函数开始执行,th_fn函数将g_ready变量变为1,然后打印一个语句。最后回到main中的while循环,由于while中上次最后一秒现在才执行完,打印一句,然后由于此时g_ready为1,所以退出循环,然后打印循环外的一个语句,然后结束程序的运行。这种结果,其实是比较容易理解的。也是比较普遍的结果。

但是如果我们再编译上述代码的时候,加上了-O3选项,该选项是优化选项,且级别比较高。编译运行结果如下:

  • gcc -O3 -pthread main.c device.c -o test.out
  • ./test.out

运行结果如下动态图:
在这里插入图片描述

由以上结果我们可以看到,程序陷入了死循环,g_ready没有被改变一直都是0,所以main中的while循环一直在执行。对于这个结果,可能并不是所有人都知道为什么。下面,我们就要来讲解为什么会产生上面的运行结果。

2 问题分析

-O3选项是让编译器对代码进行优化。

  • 编译优化时,编译器根据当前文件进行优化
  • 为了效率上的提高,优化时编译器将变量值从内存中读取进入寄存器
  • 每次要访问变量时直接从寄存器读取。毕竟寄存器的存取比内存的存取快很多。

那么,我们明白了优化对变量的影响。对于上述的代码,如果在编译时加了-O3选项。编译优化时,编译器会将变量g_ready的值放到寄存器中,以后每次使用该变量就从寄存器中取出g_ready的值使用即可。这样虽然是速度快了,但是当在th_fn函数中改变了g_ready的值后,在内存中确实g_ready的值已经变为1了,但是在刚刚那个寄存器中,最开始将g_ready的值也就是0存进去的,一直都没有改变过,但是呢,由于编译器的优化,每次在使用g_ready的变量的时候,都是从寄存器中直接取出值来使用…

说到这里,应该都能够明白了,此时从寄存器中取出的值就一直都是0.然后while一直循环。

3 解决方案

编译的时候使用-O3选项是很常用的。那么如何才能既使用这个-O3选项,又使得上述程序按照我们的意愿来执行呢?volatile就此出场。

使用volatile关键字修饰可能被意外修改的变量(内存),从而禁止编译器对该变量进行优化。

  • volatile一般修饰的是一种易变的变量
  • volatile可理解为一种编译器警告指示字,它告诉编译器必须每次都直接去内存中取变量值。

那么在上述的代码中,我们将main.c中的extern const int g_ready; 改为:extern volatile const int g_ready; 再重新进行优化的编译,然后运行,结果就是正确的运行。可以自己尝试做实验!

4 拓展: const和volatile

细心的朋友会发现,在main.c程序中,我们改完后,成这样了:extern volatile const int g_ready; 又是const又是volatile的。我们上面说过,volatile修饰的是意变的变量,而const修饰的变量是不能被修改的,有的人叫它常量,不可变的。而实际上在编程语言中的解释是这样的:const修饰的变量在当前文件中不能够出现在赋值符号的左边。 所以我们可以看到,在main.c这个文件中g_ready这个变量,并没有出现在赋值符号的左边,所以是没有问题。但是在device.c文件中g_ready是没有被const修饰的,它就可以出现在赋值符号的左边,可以被改变。这样一来,在device.c中修改g_ready这个变量,在main.c中的g_ready也间接被改变了。所以,我觉得说const修饰的是常量这个说法不够准确,说它是变量但是不能够出现在赋值符号的左边更加准确。

下面就总结一下const和volatile关键字:

  • const表示修饰的变量在当前文件中不能出现在赋值符号的左边,不能直接被改变,但是可以间接被改变
  • volatile表示修饰的变量每次使用的时候直接从内存中读取
  • const和volatile同时修饰变量时互不影响。

在这里插入图片描述

4 总结

  • 编译优化时,编译器仅根据当前文件进行优化
  • 编译器的优化策略可能造成一些意外
  • volatile强制编译器必须从内存中取变量值
  • const和volatile同时修改变量时,互相不影响彼此的含义。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/422868.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算char,short,int,long类型变量的取值范围

源自《The C Programming Language》P28 pr2-1&#xff1a; 编写一个程序以确定分别由signed及unsigned限定的char&#xff0c;short&#xff0c;int&#xff0c;long类型变量的取值范围。 参考代码&#xff1a; main.c 1 #include <stdio.h>2 #include <limits.h>…

EtherCAT主站实时性分析

转载自&#xff1a;https://blog.csdn.net/ethercat_i7/article/details/54018036 一、实时性的意义 在主从DC同步模式下&#xff0c;主站需要以非常精准的时间发送过程数据&#xff0c;如下图所示&#xff1a; 二、实时性的关键 如下图所示&#xff0c;影响实时性的关键因素是…

VNC源码研究(一)

VNC采用RFB通信协议。RFB ("remote 帧缓存 ") 是一个远程图形用户的简单协议&#xff0c;因为它工作在帧缓存级别上&#xff0c;所以它可以应用于所有的窗口系统&#xff0c;例如&#xff1a;X 11,Windows 和 Mac 系统。 独特的计算环境。 RFB 协议可进行可靠的传输…

枚举的一些常用操作

本章将介绍以下几点&#xff1a; 1、如何把其它类型转换为枚举类型&#xff1f; 2、如何把枚举中的值添加到下拉菜单中&#xff1f; 一、如何把其它类型转换为枚举类型&#xff1f; 我们回顾一下有关字符串与数字之间的转换&#xff0c;如&#xff1a; string strValue&quo…

10个开源免费的电子商务平台(转自伯乐在线)

如今&#xff0c;人们几乎可以在网络上购买到绝大部分东西&#xff0c;从电子产品、衣服&#xff0c;到机票预订和订餐。购物已转移到互联网&#xff0c;你所做的&#xff0c;只是需要付钱。当然&#xff0c;消费者会 非常注重网站的用户体验。所以&#xff0c;一个整洁安全的平…

【Git、GitHub、GitLab】五 git中裸仓库.git下的内容

上一篇文章学习了git的文件重命名与git -log 的系列命令的使用方法。点击链接查看上一篇文章&#xff1a;【Git、GitHub、GitLab】四 Git文件重命名的简单方法以及使用git log查看版本演变历史 本篇文章学习git中&#xff0c;在没有远端服务器的情况下&#xff0c;裸仓库.git中…

【Git、GitHub、GitLab】六 GIT中commit、tree和blob三个对象之间的关系

上一篇文章学习了git裸仓库.git中的内容&#xff0c;点击查看上一篇文章&#xff1a;【Git、GitHub、GitLab】五 git中裸仓库.git下的内容 本篇文章记录学习git中commit、tree和blob三个对象之间的关系。 首先需要会使用下面的命令&#xff1a; cat 命令&#xff0c; 功能&am…

【Git、GitHub、GitLab】十 将git仓库备份到本地

上一篇文章学习记录了工作中常用的一些git命令&#xff0c;点击链接查看&#xff1a;【Git、GitHub、GitLab】九 工作中非常重要的一些git用法 文章目录1 git的传输协议2 如何将git仓库备份到本地2.1 使用哑协议备份2.2 使用智能协议备份1 git的传输协议 哑协议与智能协议的区别…

java-XML

XML不再多说&#xff0c;XML 约束也不用说了&#xff0c;这里讲讲java如何对XML操作。 java中使用XML&#xff0c;目前常用的就是Jaxp(sun)和dom4j了&#xff0c;这里先讲讲java自带的Jaxp包 JAXP 开发包是J2SE的一部分&#xff0c;它由javax.xml、org.w3c.dom 、org.xml.sax 包…

【C语言进阶深度学习记录】一 数据类型的本质与变量的本质

今天学习C语言中的数据类型的本质与变量的本质 文章目录1 什么是数据类型2 变量的本质3 数据类型与变量的关系4 自定义数据类型与创建变量5 总结1 什么是数据类型 数据类型可以理解为固定内存大小的别名数据类型是创建变量的模子 如同下面的图示&#xff0c;各个数据类型是代…

使用第三方Markdown编辑器编辑为知笔记

前言 为知笔记默认的编辑器并没有预览功能&#xff0c;而提供的MD编辑器插件也并不是很好用&#xff0c;但为知笔记提供了可以使用第三方编辑器的功能&#xff0c;Typora编辑器是目前最优秀的Markdown编辑器之一&#xff0c;可以很好作为第三方编辑器。 Typora介绍 Typora是…

【C语言进阶深度学习记录】二 有符号与无符号

今天学习C语言中的有符号与无符号 文章目录1 计算机中的符号位1.1 有符号数的表示法1.2 无符号数的表示法1.3 signed 和 unsigned2 实验-当有符号数与无符号数进行运算3 错误的使用了unsigned4 总结1 计算机中的符号位 C语言中&#xff0c;数据类型的最高位&#xff0c;用于标…

【C语言进阶深度学习记录】三 浮点数(float) 在内存中的表示方法

相信大多数人知道整形数在内存中的分布方式&#xff0c;而且也能很容易写出其二进制的形式&#xff0c;但是对于浮点数&#xff0c;估计知道的人并不是很多今天学习在C语言中浮点数在内存中的表示方法 文章目录1 浮点数在内存中的存储方式1.1 浮点数的转换步骤1.2 浮点数的转换…

【C语言进阶深度学习记录】四 C语言中的类型转换

今天学习C语言中的类型转换&#xff0c;包括隐式类型转换和显示类型转换 文章目录1 C语言中的数据类型转换1.1 强制类型转换1.11 强制类型转换代码分析1.&#xff12; 隐式类型转换1.21 隐式类型转换代码分析2 总结1 C语言中的数据类型转换 C语言中&#xff0c;可以进行数据类…

【C语言进阶深度学习记录】五 C语言中变量的属性

上一篇文章学习了C语言中的类型转换&#xff0c;点击链接查看&#xff1a;【C语言进阶深度学习记录】四 C语言中的类型转换. 文章目录1 C语言的变量属性1.1 auto关键字1.2 register关键字1.3 static 关键字1.4 代码案例分析1.5 extern 关键字1.6 代码案例分析2 总结1 C语言的变…

【C语言进阶深度学习记录】六 C语言中的分支语句

文章目录1 if 语句的分析1.1 if 语句中零值比较的注意点2 switch 语句的分析3 if 与switch语句使用代码案例分析4 if语句与switch语句的互换5 总结1 if 语句的分析 if 语句根据条件选择执行语句else 不能独立存在&#xff0c;且总是与距离它最近的if匹配else 语句可以连接其他…

【C语言进阶深度学习记录】七 C语言中的循环语句

文章目录1 循环语句分析1.1 do...while循环1.2 while循环1.3 for循环1.4 三种循环语句使用对比2 break和continue的区别3 总结1 循环语句分析 C语言中的循环语句主要有for循环&#xff0c;while循环和do…while循环。 循环语句的基本工作方式&#xff1a; 通过条件表达式判断…

【C语言进阶深度学习记录】八 C语言中void的分析

文章目录1 void的意义1.1 不存在void变量1.2 C标准1.3 void指针的意义1.4 通过void* 实现memset函数2 总结1 void的意义 void修饰函数的参数和返回值的时候&#xff1a; 如果函数没有返回值应该将其返回值声明为void如果函数没有参数&#xff0c;应该将函数的参数声明为void如…

【C语言进阶深度学习记录】九 C语言中const的详细分析

文章目录1 const的分析2 const本质的分析实验2.1 代码案例分析3 const修饰函数参数和返回值时的情况3.1 代码案例分析4 总结1 const的分析 不管是C语言还是C语言&#xff0c;const都是一个非常重要的关键字。今天这篇文章着重学习记录C语言中的const。C语言中稍有不同。 在C语…