当前位置: 首页 > news >正文

Linux | C语言中volatile关键字的理解

目录

前言

一、代码引入

二、现象解释

三、具体引用


前言

        本章主要讲解介绍volatile关键的作用与使用场合;深刻理解volatile关键字;本文你需要有信号相关的基础知识;

Linux | 信号-CSDN博客

一、代码引入

        首先,我们来查看下面这段代码;

#include <iostream>
#include <signal.h>// 定义全局变量
int flag = 1;void handler(int signum)
{(void)(signum); // 防止编译器警告std::cout << "change before flag:" << flag << std::endl;flag = 0;std::cout << "change after flag:" << flag << std::endl;
}int main()
{// 对2号信号捕捉signal(SIGINT, handler);// 死循环while(flag);std::cout << "run here..." << std::endl;return 0;
}

        当我们发送2号信号时,全局变量flag被改为了0,然后循环条件不满足,打印 run here 后退出;我们运行查看结果是否满足我们预期结果;如下所示;

        第一个红色框起来的是我们编译程序所用指令;第二个红色框起来的是当我们按下 ctrl + c 发送2号信号时,程序如我么预期所料;

        接下来,我们来介绍以下 gcc/g++ 的几个编译选项;如下图所示;

        -O1、-O2、-O3分别为编译时三个不同等级的优化,其中优化程度由低到高,我们选择最高等级,再次编译运行代码;如下所示;

        神奇的一幕发生了,我们发现我们无论按多少次 ctrl + c 都无法退出程序,我们发送2号信号,也被处理了,我们的全局变量flag不是被置为0了吗?为什么还是没有办法退出while循环呢?下面我们来仔细讲解这个神奇现象;

二、现象解释

        实际上,这就是跟我们的编译器优化有关,我们把视角拉到代码中;如下图所示;

        我们的while循环判断分为以上三个步骤,而当我们编译时对代码采用 O3 级别的优化时,我们的编译器检测到循环中没有对全局变量flag进行修改,因此直接将上面的步骤优化成了如下所示;

        故即使我们发送2号信号将内存中的flag更改,但是判断时时候,依旧直接判断寄存器中flag的那个值;所以才会看到上述那种神奇现象;

三、具体引用

        我们本文的主角volatile关键字就是为了防止这种编译器过度优化的现象,我们可以在定义flag变量的前面加上一个 volatile关键字,这样可以防止我们的变量flag参与被编译器编译的代码过度优化;

#include <iostream>
#include <signal.h>// 定义全局变量(增加volatile关键字)
volatile int flag = 1;void handler(int signum)
{(void)(signum); // 防止编译器警告std::cout << "change before flag:" << flag << std::endl;flag = 0;std::cout << "change after flag:" << flag << std::endl;
}int main()
{// 对2号信号捕捉signal(SIGINT, handler);// 死循环while(flag);std::cout << "run here..." << std::endl;return 0;
}

        代码几乎完全相同,就加入了一个volatile关键字,避免了这种编译器过度优化现象;

http://www.lryc.cn/news/236608.html

相关文章:

  • 汇编层面有三个主要的操作对象
  • React中的Redux:简介和实例代码
  • Modbus转Profinet网关在金银精炼控制系统中应用案例
  • 小程序商城免费搭建之java商城 电子商务Spring Cloud+Spring Boot+二次开发+mybatis+MQ+VR全景+b2b2c
  • Rabin加解密算法(python3)
  • 【带头学C++】----- 七、链表 ---- 7.5 学生管理系统(链表--上)
  • (四)什么是Vite——冷启动时vite做了什么(源码、middlewares)
  • Docker部署MinIO对象存储服务器结合Cpolar实现远程访问
  • C#入门(1):程序结构、数据类型
  • Scala---元组
  • 【Linux】冯诺依曼体系结构、操作系统、进程概念、进程状态、环境变量、进程地址空间
  • 【hive-解决】HiveAccessControlException Permission denied: CREATEFUNCTION
  • 内网穿透的应用-通过内网穿透快速搭建公网可访问的Spring Boot接口调试环境
  • 解决 uniapp 开发微信小程序 不能使用本地图片作为背景图 问题
  • 常用中间件封装思路粗记
  • 探索SPI:深入理解原理、源码与应用场景
  • Web3名词解释
  • Vatee万腾外汇市场新力量:vatee科技决策力
  • 【HarmonyOS开发】配置开发工具DevEco Studio
  • 探索亚马逊大语言模型:开启人工智能时代的语言创作新篇章
  • zabbix-proxy分布式监控
  • springboot生成PDF,并且添加水印
  • Tensorflow2.0:CNN、ResNet实现MNIST分类识别
  • 本地jar导入maven
  • 数据结构与算法【堆】的Java实现
  • 同创永益联合红帽打造一站式数字韧性解决方案
  • c++ call_once 使用详解
  • 【rosrun diagnostic_analysis】报错No module named rospkg | ubuntu 20.04
  • 高防CDN有什么作用?
  • 盛元广通开放实训室管理系统2.0