C和C++中的register变量和volatile变量理解
一、register关键字修饰变量
首先register是关键字,关键字意味着这样的变量是由编译器处理的。他的作用就是尽量让这个被修饰的变量存放在CPU的寄存器中供程序进行读写,因为他的值很少被修改,直接通过寄存器访问,就能提高程序的性能。因为对变量的访问是直接访问的寄存器,而不是比寄存器还要慢的内存。但是这里有几个点需要注意:
1、一个变量被修饰为一个register变量,意味着这个变量是频繁变化和访的。意味着他的生命周期很短暂,但是又要频繁访问。这个是register变量的特点。如果register去修饰一个全局变量的话,因为全局变量生命周期是整个程序作用阈的,也就是程序完成了生命周期才结束。那么就要让这个全局变量长期或者一直占据某个CPU寄存器,这个显然违背计算机设计且不合理。因此register变量只能修饰局部变量和形参(函数定义时候的参数)。
2、因为一般CPU的寄存器个数只有10多个(比如16个),因此如果要想定义成register的的所有变量都一定放在CPU寄存器中,显然是不太可能,而这个谁来决定,取决于编译这个程序的编译器。编译器会根据当前CPU寄存器使用情况来决定那些变量可以放在寄存器中进行处理。所以即便register定义了变量,不代表这个变量一定放在寄存器中进行处理。如果没有放在寄存器中处理,则这个变量和普通的临时变量没有区别,还是放在内存中进行处理。
3、因为寄存器变量是放在寄存器中处理的,而不是放在内存中处理的,所以对寄存器变量取地址显然是不合理的,因为寄存器压根就没有地址一说,地址都是对内存而言的。因此对一寄存器变量取地址就是错误的。gcc编译的时候会报错。
但是g++可以正确处理这个编译,是可以编译通过的,而且能把相应的地址取出来。
4、同时寄存器一般都是32位的,所以不能把一个64位的变量定义为一个寄存器变量,因为这个变量根本放不下在一个寄存器中。----但是现在的CPU貌似都可以在gcc和g++编译器下编译通过,且可以正常运行。
二、volatile关键字修饰变量
和register修饰变量正好相反,这个关键字是告诉编译器这个变量是经常会变的,所谓的变化是指的他的值在内存中不停的变化。那么对这个值的访问就不能只是在CPU寄存器中访问了,则需要到外面内存中去取最新的值来访问保证数据一致性。他修饰变量时候主要是告诉编译起不要进行代码优化,而且读取这个变量的值要去内存中读取,而不是从register中读取。举个例子:
int a ;
a = 100;
a = 200;
a = 300;
a = 400;
如果是在gcc -O3优化设置下,上述四行代码只会被优化位一行机器命令,也就是只有a=400那条指令会被计算机处理。如果我们1,2,3,4步骤是要去做一件事件,那么效果就是只会做4步的事情。而1,2,3步都不会做,那么如果我们把int a 变成volatile int a;则就会告诉编译起这个变量每次都从内存中获取数据,这样其实每一步都会执行。其实在软中断中,因为共享的变量在中断前被赋值,然后中断后进行处理的话,有可能由于代码优化就会使中断处理的代码中使用到不符合预期的变量值,导致错误。其实这个在实际的工作中遇到过,此时需要对这个变量进行volatile修饰。这样编译器就不会对中断开始和中断后的代码进行优化了。程序执行就会严格按照代码逻辑进行。
还有一种场景就是多线程模型下,其他线程对变量的更改发生在内存中,而某个线程由于频发访问这个变量,导致该线程的CPU把这个变量放在了寄存器中,因此如果其他线程修改了这个变量在内存的值的话,那么该线程就还是读取到的旧的值。这样数据就不一致了。
其实解决中这样的问题的办法还有一个办法就是通过内存屏障的办法解决,本质也是在中断前和告诉编译器不能优化中断前和中断后的代码逻辑顺序,此时程序就会严格按照中断的逻辑对代码进行处理,同时对其后面的变量访问也会从内存中进行获取。从而保证数据的一致性。这里内存屏障其实就是用了一个volatile修饰;
#define set_mb(var, value) do { var = value; mb(); } while (0)#define mb() __asm__ __volatile__ ("" : : : "memory")
- __asm__用于指示编译器在此插入汇编语句
- volatile用于告诉编译器,严禁将此处的汇编语句与其它的语句重组合优化。即:原原本本按原来的样子处理这这里的汇编。
- memory 强制 gcc 编译器假设 RAM 所有内存单元均被汇编指令修改,这样 cpu 中的 registers 和 cache中已缓存的内存单元中的数据将作废。cpu 将不得不在需要的时候重新读取内存中的数据。这就阻止了 cpu 又将 registers,cache 中的数据用于去优化指令,而避免去访问内存。
- “”::: 表示这是个空指令。barrier()不用在此插入一条串行化汇编指令。在后文将讨论什么叫串行化指令。
C和C++中的register变量和volatile变量理解相关推荐
- java中的volatile变量
同步与线程间通信: 通信 通信是指消息在两条线程之间传递. 既然要传递消息,那接收线程 和 发送线程之间必须要有个先后关系,此时就需要用到同步.通信和同步是相辅相成的. 同步 同步是指,控制多条 ...
- 从缓存行出发理解volatile变量、伪共享False sharing、disruptor
volatile关键字 当变量被某个线程A修改值之后,其它线程比如B若读取此变量的话,立刻可以看到原来线程A修改后的值 注:普通变量与volatile变量的区别是volatile的特殊规则保证了新值能 ...
- Java 基础系列之volatile变量(一)
一.锁 两种特性:互斥性(mutual exclusion).可见性(visibility).原子性(atomic) 互斥性就是一次只有一个线程可以访问该共享数据,可见性就是释放锁之前,对共享数据的修 ...
- volatile变量能确保线程安全性吗?为什么?
1. volatile是什么? 在谈及线程安全时,常会说到一个变量--volatile.在<Java并发编程实战>一书中是这么定义volatile的--"Java语言提供了一种稍 ...
- C002--c语言中的标识符,关键字及变量常量的声明和使用
因为我第一个学习的计算机语言是java,因此我在学习一门新的语言时,或多或少会用学习过的语言与新学习的语言进行对比,然后找到它们之间的不同,如果没有学习过其他语言,第一次接触的计算机语言就是c语言的话 ...
- C语言中变量存储类别——自动变量,寄存器变量,静态外部链接;
c提供了多种不同模型或存储类别在内存中存储数据. 作用域: 作用域描述程序中可访问标识符的区域. 作用域描述了程序中可以访问一个标识符的一个或多个区域.即变量的可见性. 一个变量的作用域可以是代码块作 ...
- switch语句中在case块里声明变量会遇到提示“Expected expression before...的问题
switch语句中在case块里声明变量会遇到提示"Expected expression before..."的问题 例如在如下代码中 1 case constant: 2 in ...
- R语言构建回归模型并进行模型诊断(线性关系不满足时)、进行变量变换(Transforming variables)、使用car包中的boxTidwell函数对预测变量进行Box–Tidwell变换
R语言构建回归模型并进行模型诊断(线性关系不满足时).进行变量变换(Transforming variables).使用car包中的boxTidwell函数对预测变量进行Box–Tidwell变换 目 ...
- 已知某班学生的英语成绩按学号(从1开始)从小到大的顺序排列在tab表中,要查的学生学号放在变量no中,查表结果放在变量english中。
已知某班学生的英语成绩按学号(从1开始)从小到大的顺序排列在tab表中, 要查的学生学号放在变量no中,查表结果放在变量english中. data segmenttab db 68,78,42,84 ...
最新文章
- 启动脚本gameserver
- oschina android版源码中的颜色值
- 局域网QQ第三版(V1.4)
- Luogu1613 跑路
- Hadoop基础-HDFS数据清理过程之校验过程代码分析
- java Thread.yield()用法详解
- android 辅助功能_关于辅助功能的9个神话
- c语言的链表ppt,C语言链表详解.ppt
- 微信小程序获取手机号php
- centos7该如何限制IP访问?
- 求数组内子数组最大的和(Maximum Subarray )
- File类,字节字符输入输出流,缓冲流,标准流,对象序列化流
- DM数据库外键设置的解决方式
- flowable6.4 并行网关 驳回 跳转 回退 多实例加签减签
- 记录 支付宝口碑--商户会员卡 开发历程
- 【iOS】对于iPhone5分辨率兼容性调整有关问题
- 10G网络变压器厂家告诉你10G以太网主要有哪些特点
- windows日志和审核
- html网页制作favicon页面,图片存在网页显示
- chevereto图床程序的安装与迁移