C语言中volatile关键字详解以及常见的面试问题

2023-11-19

编译器的优化

    程序运行的优化可以分为硬件和软件。硬件上是在CPU和内存中间增加cache,来解决CPU和内存之间运行速率差异过大的问题。软件上则分为编译器优化和程序员优化:程序员优化是程序员在编写代码时,对代码的逻辑顺序进行合理安排,提升效率;编译器优化则是程序员写好的代码,在编译链接时由编译器进行优化,会调整代码的执行顺序或者删掉一些无用的语句。
    编译器优化常用的方法有:将内存变量缓存到寄存器;调整指令顺序充分利用CPU指令流水线,常见的是重新排序读写指令。因为CPU和内存的读写速度差异过大,所以编译器会尽量减少读写内存的操作,比如把中间变量或者刚读取的数据存在cache、寄存器中。编译器有一种技术叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以消除一些代码,换句话说就是程序员写的代码不是都会执行的,有时候编译器会删除掉一些编译器认为无意义的代码。

C语言中volatile关键字的作用

volatile int a;
main()
{
	a=0;
	b = a;
	printf("b = %d\n", b);
}

volatile的意思是“多变的”,被volatile修饰的变量则表示该变量会被意想不到的改变,每次使用到该变量时都要去该变量的内存地址处读取,而不要去使用暂存的该变量的值。比如上面的代码,当执行b=a语句时,编译器会判断在a赋值以后就没有改变a的值,因此会把寄存器中暂存的a的值给b,而不是去a的内存地址处去读取a再赋值给b。一般情况这样是没问题的,但是有中断程序或者别的进程去修改了a的值,那寄存器里暂存的a就和内存里存放的a不一致了。不加volatile修饰,编译器还是会将寄存器暂存的a赋值给b,此时寄存器里a的值已经不再是最新的a的值,赋值是有问题的;加了volatile修饰,每次使用a时都会去a的内存地址处读取a,这样能保证每次读取的a都是最新的值,但是会导致效率降低,因为读取内存的速度远低于读取寄存器的速度。简单来说:加了volatile修饰,每次使用该变量都要去内存地址处读取,程序员写的每一行代码都要执行,不要编译器做优化,因为编译器在优化时会将编译器认为无用的代码删掉。

volatile关键字的应用实例:

1.中断服务程序中修改的某个变量值是供其他程序检测的状态值

static int status;

int main(void)

{
     while (1)
     {
		if (status) 
		{
			printf("status = 1\n");
			status = 0;
		}
	}
}
//中断服务程序
void ISR_fun(void)

{
      status=1;
}

代码解析:在上面的程序中,中断服务程序会改变status的值,目的是让主程序打印一次"status = 1\n"。主程序并不知道会有中断服务程序去修改status的值,经过编译器优化后,主程序第一次读取status的值是从内存处读取,以后使用status值时都用的之前读取的那份;那中断服务程序就算执行并改变了status的值,主程序也不会再去内存处读取,在主程序中status的值永远不会变,这样程序就出错了。如果加上volatile修饰status变量,那主程序每次使用status值时都会去内存地址处读取,这样就能保证中断服务程序修改status的值后,能真正的起到作用。

2.操作硬件设备的某些寄存器时需要加volatile修饰:

volatile int  *sequenceInit = (unsigned  int *)0xfffff000;//定义一个寄存器操作指针

int   init(void)

{
      int i;
      for(i=0;i< 10;i++)
      {
         *sequenceInit = i;
	}
}

代码解析:ARM芯片是统一编址,操作寄存器就和读取内存地址是一样的,假设上面的程序是ARM芯片对内存的初始化代码。ARM芯片初始化内存就是通过操作内存控制器去初始化内存,按照数据手册的初始化说明向内存控制器的寄存器依次写入特定值。假设for循环是时序的初始化,要依次向地址为0xfffff000的寄存器写入0到9。如果不加volatile修饰,编译器会认为依次向寄存器写入0到9和直接写入9效果是一样的,那整个for循环会被替换成sequenceInit =9,显然这样是不能初始化内存的,初始化会失败。以上分析可知,此时的sequenceInit 必须加volatile修饰。

volatile常见的面试问题:

1.一个参数既可以是const还可以是volatile吗?

可以的,例如只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

2.一个指针可以是volatile 吗?

可以,当一个中服务子程序修该一个指向一个buffer的指针时。指针是一种普通的变量,从访问上没有什么不同于其他变量的特性。其保存的数值是个整形数据,和整型变量不同的是,这个整型数据指向的是一段内存地址。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言中volatile关键字详解以及常见的面试问题 的相关文章

随机推荐

  • Unity卡死情况

    今天遇到了Unity点击播放后卡死 用任务管理器强行关闭后重开 打不开项目的情况 解决方案 检查USB接口设备 有些设备可能会影响Unity工程启动 比如VR头盔
  • 使用java代码给Excel加水印,代码全,进阶版

    以下代码 亲测可以跑通 1 上一篇博客用了Apache POI库3 8的版本的形式对Excel加了水印 但是最近主线版本用了4 1 2的形式 由于为了保持版本的兼容性 下面有开发了Apache POI的4 1 2的版本号的方案 pom文件为
  • 使用selenium IDE开始简易自动化测试

    使用selenium IDE开始简易自动化测试 火狐浏览器有个很好用的selenium插件 可以自动录制页面动作 selenium IDE 下载地址 下载安装好 笔者下载的2 9 0 我们以在百度搜索selenium为例 首先启动IDE 点
  • 链式二叉树的基本操作(C语言实现)

    目录 一 链式二叉树的创建 1 1 定义节点结构 1 2 节点的创建 1 3 节点的链接 二 树的深度遍历 1 前序 中序 后序遍历 1 2 三种方式的遍历顺序图 2 代码实现 3 遍历检测 三 树的层序遍历 3 1 层序遍历 3 2 完全
  • Loadrunner录制登录,验证是否登录成功的几种方法

    1 利用web reg find 优先使用 2 利用web image check 其次 3 利用web get int property函数 4 利用提交的status状态 5 利用对数据的操作查看是否登录 1 利用添加检查点web re
  • 拼多多招收java开发的三轮面试题,你能撑到第几轮?

    我相信 面试一直是大家关注的问题 包括最近有很多刚毕业或者刚实习的小伙伴跟我讲投了很多简历出去 但却都像泥牛入海一样了无音讯了 确实出于程序员的直觉 今年着实是要比往年要更冷一些 对于面试来说 我相信大家都听过一个说法就是 金九银十 但是现
  • c++/cuda并行累计求和

    文章目录 代码 CMakeLists txt 结果 代码 include
  • 设计模式-建造者模式

    文章目录 建造者模式 创建复杂对象的优雅方式 什么是建造者模式 建造者模式的使用场景 优缺点 示例 使用建造者模式创建电脑对象 建造者模式 创建复杂对象的优雅方式 在软件开发中 有时候我们需要创建具有复杂结构和多个组件的对象 直接在客户端代
  • 【Pytorch异常笔记】Error #15: Initializing libiomp5md.dll, but found libiomp5md.dll already initialized.

    文章目录 异常描述 解决方法 开发环境 异常描述 OMP Error 15 Initializing libiomp5md dll but found libiomp5md dll already initialized OMP Hint
  • git 和远程服务器关联,Git远程服务器连接被拒绝

    我对这个错误感到疯狂 2天后我没有发现我的系统出了什么问题 我相信修复它非常容易 Git远程服务器连接被拒绝 当我尝试使用Git功能我得到的消息 连接到我的git服务器 无法打开连接 主机不存在 致命的 无法从远程存储库中读取 请确保您拥有
  • Android JNI1--C语言基础

    1 include 相当于java的导包操作 例如 include
  • vue中computed的属性对data中的属性赋值为undefined的原因

    场景 我在computed中return了一个值 然后在data中直接将它复制给另一个属性 结果data中的属性值为undefined 代码示例 timer为undefined 原因 在这里很容易想到是执行顺序的问题 computed中的属
  • sql server 中的日期计算,如当天周的第一天,当前月的第一天

    根据给定的日期 计算该日期在本月所在周数 每周的第一天为周日 但是在月末需要与下个月进行衔接 如 图2012年2月份 3月份的1 2 3号为2月份的第4周 而2月份的1 2 3 4为1月份的最后一周 第五周 declare datetime
  • linux线程间通讯----管道、信号

    进程间通讯机制 unix继承 管道 信号 system V IPC对象 共享内存 消息队列 信号灯集 1 管道 管道分为无名管道和有名管道 区别在于创建的管道是否在文件系统中不可见 无名不可见 有名可见 1 无名管道 特点 1 在创建之后再
  • 系统调用详解

    1 系统调用 1 系统调用就是为了让应用程序可以访问系统资源 每个操作系统都会提供一套接口供应用程序使用 这些接口通常通过中断来实现 例如在windows中是0x2E号中断作为系统调用入口 linux使用0x80号中断作为系统的调用的入口
  • IC数字后端

    在 innovus 里面 有时候我们需要控制 tie cell 的 fanout 和 net length 来避免 tie cell 可能出现 max transition 或者 max fanout 的违例 一般来说 只要 fanout
  • typeScritp的高级函数

    1 交叉类型 交叉类型是将多个类型合并为一个类型 这让我们可以把现有的多种类型叠加到一起成为一种类型 它包含了所需的所有类型的特性 例如 Person Serializable Loggable同时是 Person 和 Serializab
  • BSN唐斯斯:区块链是“新基建中的基建”

    5月16日 19 00 陀螺财经特别邀请到国家信息中心智慧城市发展研究中心副主任 区块链服务网络发展联盟常务副秘书长唐斯斯 为大家带来关于新基建下的区块链新机遇的思考 直播主题 新基建下的区块链新机遇 嘉宾 国家信息中心智慧城市发展研究中心
  • 智慧城管项目可行性研究报告

    后台回复 230915 可获得下载资料的方法 点击文后阅读原文 可获得下载资料的方法 欢迎加入智能交通技术群 联系方式 微信号18515441838
  • C语言中volatile关键字详解以及常见的面试问题

    编译器的优化 程序运行的优化可以分为硬件和软件 硬件上是在CPU和内存中间增加cache 来解决CPU和内存之间运行速率差异过大的问题 软件上则分为编译器优化和程序员优化 程序员优化是程序员在编写代码时 对代码的逻辑顺序进行合理安排 提升效