C++常见面试题(11)——volatile_哎呦 - CSDN博客
文章推薦指數: 80 %
本文主要介绍在C/C++语言中,volatile关键字的相关内容。
1 概述1.1 why volatilevolatile 关键词,最早出现于19世纪70年代, ...
C++常见面试题(11)——volatile
哎呦,帅小伙哦
于 2021-08-2411:23:17 发布
129
收藏
分类专栏:
常见面试题
原文链接:https://blog.csdn.net/liitdar/article/details/86063883
版权
常见面试题
专栏收录该内容
54篇文章
0订阅
订阅专栏
本文主要介绍在C/C++语言中,volatile关键字的相关内容。
1概述1.1whyvolatile
volatile关键词,最早出现于19世纪70年代,被用于处理MMIO(Memory-mappedI/O)带来的问题。
在引入MMIO之后,一块内存地址既有可能是真正的内存,也有可能是映射的一个I/O端口。
因此,读/写一个内存地址,既有可能是真正地操作内存,也有可能是读/写一个I/O设备。
那么MMIO为什么需要引入volatile关键词呢?我们结合下面这段示例代码进行解释:
unsignedint*p=FunB();
unsignedinta;
unsignedintb;
a=*p;//语句1
b=*p;//语句2
*p=a;//语句3
*p=b;//语句4
在上述代码片段中,指针p既有可能指向一个内存地址,也有可能指向一个I/O设备。
如果指针p指向的是I/O设备,那么语句1和语句2中的变量a和变量b,就会接收到I/O设备的连续两个字节。
但是,指针p也有可能指向内存地址,这种情况下,编译器就会进行语句优化,编译器的优化策略会判断变量a和变量b同时从同一个内存地址读取数据,因此在执行完语句1之后,直接将变量a赋值给变量b。
对于指针p指向I/O设备的这种情况,就需要防止编译器进行此优化,即不能假设指针b指向的内容不变(对应volatile的易变性特性)。
同样,语句3和语句4也有类似的问题,编译器发现将变量a和b同时赋值给指针p是无意义的,因此可能会优化语句3中的赋值操作,而仅仅保留语句4。
对于指针p指向I/O设备的情况,也需要防止编译器将类似的写操作给优化消失了(对应volatile的不可优化特性)。
对于I/O设备,编译器不能随意交互指令的顺序,因为指令顺序一变,写入I/O设备的内容也就发生变化了(对应volatile的顺序性)。
为了满足MMIO的这三点需求,就有了volatile关键字。
1.2INC/C++
在C/C++语言中,使用volatile关键字声明的变量(或对象)通常具有与优化、多线程相关的特殊属性。
通常,volatile关键字用来阻止(伪)编译器对其认为的、无法“被代码本身”改变的代码(变量或对象)进行优化。
如在C/C++中,volatile关键字可以用来提醒编译器使用volatile声明的变量随时有可能改变,因此编译器在代码编译时就不会对该变量进行某些激进的优化,故而编译生成的程序在每次存储或读取该变量时,都会直接从内存地址中读取数据。
相反,如果该变量没有使用volatile关键字进行声明,则编译器可能会优化读取和存储操作,可能暂时使用寄存器中该变量的值,而如果这个变量由别的程序(线程)更新了的话,就会出现(内存中与寄存器中的)变量值不一致的现象。
在C/C++语言中,使用volatile关键字声明的变量具有三种特性:易变的、不可优化的、顺序执行的。
下面分别对这三种特性进行介绍。
2易变的
volatile在词典中的主要释义就是“易变的”。
在C/C++语言中,volatile的易变性体现在:假设有读、写两条语句,依次对同一个volatile变量进行操作,那么后一条的读操作不会直接使用前一条的写操作对应的volatile变量的寄存器内容,而是重新从内存中读取该volatile变量的值。
上述描述的(部分)示例代码如下:
volatileintnNum=0;//将nNum声明为volatile
intnSum=0;
nNum=FunA();//nNum被写入的新内容,其值会缓存在寄存器中
nSum=nNum+1;//此处会从内存(而非寄存器)中读取nNum的值
3不可优化的
在C/C++语言中,volatile的第二个特性是“不可优化性”。
volatile会告诉编译器,不要对volatile声明的变量进行各种激进的优化(甚至将变量直接消除),从而保证程序员写在代码中的指令一定会被执行。
上述描述的(部分)示例代码如下:
volatileintnNum;//将nNum声明为volatile
nNum=1;
printf("nNumis:%d",nNum);
在上述代码中,如果变量nNum没有声明为volatile类型,则编译器在编译过程中就会对其进行优化,直接使用常量“1”进行替换(这样优化之后,生成的汇编代码很简介,执行时效率很高)。
而当我们使用volatile进行声明后,编译器则不会对其进行优化,nNum变量仍旧存在,编译器会将该变量从内存中取出,放入寄存器之中,然后再调用printf()函数进行打印。
4顺序执行的
在C/C++语言中,volatile的第三个特性是“顺序执行特性”,即能够保证volatile变量间的顺序性,不会被编译器进行乱序优化。
说明:C/C++编译器最基本优化原理:保证一段程序的输出,在优化前后无变化。
为了对本特性进行深入了解,下面以两个变量(nNum1和nNum2)为例(既然存在“顺序执行”,那描述对象必然大于一个),结合如下示例代码,介绍volatile的顺序执行特性。
intnNum1;
intnNum2;
nNum2=nNum1+1;//语句1
nNum1=10;//语句2
在上述代码中:
当nNum1和nNum2都没有使用volatile关键字进行修饰时,编译器会对“语句1”和“语句2”的执行顺序进行优化:即先执行“语句2”、再执行“语句1”;
当nNum2使用volatile关键字进行修饰时,编译器也可能会对“语句1”和“语句2”的执行顺序进行优化:即先执行“语句2”、再执行“语句1”;
当nNum1和nNum2都使用volatile关键字进行修饰时,编译器不会对“语句1”和“语句2”的执行顺序进行优化:即先执行“语句1”、再执行“语句2”;
说明:上述论述可通过观察代码的生成的汇编代码进行验证。
5volatile与多线程语义
对于多线程编程而言,在临界区内部,可以通过互斥锁(mutex)保证只有一个线程可以访问该临界区的内容,因此临界区内的变量不需要是volatile的;而在临界区外部,被多个线程访问的变量应声明为volatile的,这也符合了volatile的原意:防止编译器缓存(cache)了被多个线程并发用到的变量。
不过,需要注意的是,由于volatile关键字的“顺序执行特性”并非会完全保证语句的顺序执行(如volatile变量与非volatile变量之间的操作;又如一些CPU也会对语句的执行顺序进行优化),因此导致了对volatile变量的操作并不是原子的,也不能用来为线程建立严格的happens-before关系。
对于上述描述,示例代码如下:
intnNum1=0;
volatileboolflag=false;
thread1()
{
//somecode
nNum1=666;//语句1
flag=true;//语句2
}
thread2()
{
//somecode
if(true==flag)
{
//语句3:按照程序设计的预想,此处的nNum1的值应为666,并据此进行逻辑设计
}
}
在上述代码中,我们的设计思路是先执行thread1()中的“语句1”、“语句2”、再执行thread2()中的“语句3”,不过实际上程序的执行结果未必如此。
根据volatile的“顺序性”,非volatile变量nNum1和volatile变量flag的执行顺序,可能会被编译器(或CPU)进行乱序优化,最终导致thread1中的“语句2”先于“语句1”执行,当“语句2”执行完成但“语句1”尚未执行时,此时thread2中的判断语句“if(true==flag)”是成立的,但实际上nNum1尚未进行赋值为666(语句1尚未执行),所以在判断语句中针对nNum1为666的前提下进行的相关操作,就会有问题了。
这是一个在多线程编程中,使用volatile不容易发现的问题。
实际上,上述多线程代码想实现的就是一个happens-before语义,即保证thread1代码块中的所有代码,一定要在thread2代码块的第一条代码之前完成。
使用互斥锁(mutex)可以保证happens-before语义。
但是,在C/C++中的volatile关键词不能保证这个语义,也就意味着在多线程环境下使用C/C++的volatile关键词,如果不够细心,就可能会出现上述问题。
说明:由于Java语言的volatile关键字支持Acquire、Release语义,因此Java语言的volatile能够用来构建happens-before语义。
也就是说,前面提到的C/C++中volatile在多线程下使用出现的问题,在Java语言中是不存在的。
————————————————版权声明:本文为CSDN博主「liitdar」的原创文章,遵循CC4.0BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/liitdar/article/details/86063883
哎呦,帅小伙哦
关注
关注
0
点赞
踩
0
评论
0
收藏
扫一扫,分享内容
点击复制链接
专栏目录
面试题|C++11新特性
weixin_54707168的博客
10-06
1235
问题
C++11新增了很多新特性,这也成为了面试中非常常见的问题,这里介绍一些常用的新特性。
C++11新特性有很多,这里就简单整理几个很常见的,应该足以应对面试中的问题了。
C++11新特性
●初始化列表
初始化列表,即用花括号来进行初始化。
C++11中可以直接在变量名后面跟上初始化列表来进行对象的初始化,使用起来更加方便,例如:
vector
参与评论
您还未登录,请先
登录
后发表或查看评论
C语言相关常见面试题:volatile、static、extern、const关键字
树下等苹果的博客
10-26
98
1、volatile
一个变量可能是供多方使用的,那么就有可能在某一个程序运行时,这个变量的值被其他程序改变(内存地址中的值被改变),但是读取这个变量时,可能是直接从寄存器中读取,而此时寄存器中的值还是变量改变之前的值。
这就会导致得出意想不到的结果。
使用volatile修饰变量,表示声明这个变量是“易变的”,每次读取这个变量值都要从内存中读取,而不是从寄存器中读取。
1.1volatile的作用:
确保本条指令不会因编译器的优化而省略,使用volatile声明的变量的值的时候,系统总是重新从它所在的内
50个C/C++面试题
沙漠中的一粒沙
04-11
3771
面试题1:变量的声明和定义有什么区别
为变量分配地址和存储空间的称为定义,不分配地址的称为声明。
一个变量可以在多个地方声明,但是只在一个地方定义。
加入extern修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。
说明:很多时候一个变量,只是声明不分配内存空间,直到具体使用时才初始化,分配内存空间,如外部变量。
面试题2:写出bool、int、float、
c++基础11
yhc166188的博客
07-03
378
gcc和g++的区别
简单来说,gcc与g++都是GNU(组织)的一个编译器。
需要注意以下几点:
gcc与g++都可以编译c代码与c++代码。
但是:后缀为.c的,gcc把它当做C程序,而g++当做是C++程序;后缀为.cpp的,两者都会认为是C++程序。
编译阶段,g++会调用gcc,对于c++代码,两者是等价的,但是因为gcc命令不能自动和C++程序使用的库联接,所以通常用g++来完成...
C++中智能指针shared_ptr类的实现
weixin_45645810的博客
07-29
43
(1)shared_ptr类的实现
实现原理:采用引用计数器的方法,允许多个智能指针指向同一个对象,每当多一个指针指向该对象时,指向该对象的所有智能指针内部的引用计数加1,每当减少一个智能指针指向对象时,引用计数会减1,当计数为0的时候会自动的释放动态分配的资源。
1)智能指针将一个计数器与类指向的对象相关联,引用计数器跟踪共有多少个类对象共享同一指针;
2)每次创建类的新对象时,初始化指针并将引用计数置为1;
3)当对象作为另一对象的副本而创建时,拷贝构
五、C++11常见面试题
最新发布
zdb
05-06
664
五、C++11
(1)请问C++11有哪些新特性?
1.auto关键字:编译器可以根据初始值自动推导出类型。
//但是不能用于函数传参以及数组类型的推导
2.nullptr关键字:nullptr是一种特殊类型的字面值,它可以被转换成任意其它的指针类型。
而NULL一般被宏定义为0,在遇到重载时可能会出现二义性问题。
3.智能指针:C++11新增了shared_ptr,weak_ptr,unique_ptr,auto_ptr用来管理内存
4.初始化列表:使用初始化列表来对类进
面试题之c++11新特性,你真的会答吗
qunsorber的博客
01-24
650
也许你看过类似“c++11必背十大新特性”之类的文章,所以背起来毫不费劲。
但一顿输出之后,发现面试官的表情如同一潭死水,没有丝毫波澜。
是的,这种答案过于稀松平常,并不能为你的面试增色多少,只能保证不减分。
那么怎样的回答可以让面试官有一些意外和惊喜?请看以下分析。
c++11是在c++98/03之后一次比较大的改进,大大小小的新特性一百多个,我们在有限的时间只能选择性的回答一些比较常用,同时能避免同质性的一些性质。
什么是同质性?就是你背了auto又背decltype,背了shared_ptr又背unique_
C++11volatile类型
heshaai6843的博客
07-13
1189
volatile作用:作为指令关键字,确保本条指令不会受到编译器的优化而省略,而且要求每次直接读值。
定义:
volatileintnTest;
volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。
遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
...
Java-多线程中的原子性问题
浮华′的博客
12-12
166
文章目录一、volatile关键字二、原子性三、Atomic包1.Atomic包概述2.AtomicInteger的常用方法3.AtomicInteger内存解析四、悲观锁和乐观锁
一、volatile关键字
当A线程修改了共享数据时,B线程没有及时获取到最新的值,如果还在使用原先的值,就会出现问题
堆内存是唯一的,每一个线程都有自己的线程栈。
每一个线程在使用堆里面变量的时候,都会先拷贝一份到变量的副本中。
在线程中,每一次使用是从变量的副本中获取的。
Volatile关键字:强制
C面试题中volatile
FDmitnick的博客
03-27
507
volatile(1)(百度百科)volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
所以volatile关键字的目的是防止被编译器优化。
例子:XBYTE[2]=0x55;XBYTE[2]=0x56;XBYTE[2]=0x57;XBYTE[2]=0x58;对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,但是编译器却会对上述四条语句...
常用的C++11特性(面试易考)
xy913741894的博客
06-02
9513
大二的时候看过《C++Primer》,了解过C++11,因此就在简历上写上了解C++11,结果就是频频被问到有关C++11。
。
。
发现自己答的并不算太好~因此,简单总结一下,我在找实习的过程被问到的C++11特性。
1.nullptr注意在C++中NULL仅仅是defineNULL0的一个宏定义,因此,有时候会产生歧义比如f(char*)和f(int),参数传NULL的话到底该调用哪个?事实上,在
JVM的高效并发
萝卜头柯克船长的博客
11-19
115
文章目录1.为什么要并发、并发导致了什么问题2.内存模型2.1可见性和有序性2.2volatile2.3Happens-Before3.线程3.1Java线程的实现3.2线程安全3.3锁优化(synchronized)参考
1.为什么要并发、并发导致了什么问题
CPU、内存、IO设备三者之间的速度差异一直是一个核心矛盾,为了解决缓解这一差异,计算机系统架构上作出了以下优化:
CPU增加多级高速缓存,作为内存和CPU之间的缓冲,以缓解CPU和内存之间的速度差异;
操作系统增加进程、线程
C和C++中的volatile、内存屏障和CPU缓存一致性协议MESI
一见
01-27
8696
目录
1.前言2
2.结论2
3.volatile应用场景3
4.内存屏障(MemoryBarrier)4
5.setjmp和longjmp4
1)结果1(非优化编译:g++-g-oxx.cpp-O0)5
2)结果2(优化编译:g++-g-oxx.cpp-O2)6
6.不同CPU架构的一致性模型6
7.x86-TSO7
...
C++入门——volatile的使用
beilizhang的博客
07-05
35
volatile的使用时机
编译器在把源程序编译为目标代码的时候,通常都会做一些优化。
例如,对于一些变量的存放而言,为了提高存取的效率,编译器有时会先把变量读取到一个寄存器中缓存起来,当以后再取变量值时,就直接从寄存器中取值,而不需要再从内存中去读取。
在单线程环境下,这样的优化能显著提高程序的运行效率;但是,在多线程环境下就会有潜在的风险。
假设一个线程1和线程2共享的变量sharedObj,如果在线程1中把sharedObj缓存在寄存器中,那么当线程2对内存中sharedObj的值进行修改后,线程1无法读取
C++/MFC面试题(三、C++11与STL)
LyRics1996的博客
09-23
246
一、STL
标准模板库(StandardTemplateLibrary)包含了很多实用的组件,利用这些组件,程序员编程更加方便高效
1.1STL的基本组成部分是什么?
容器、迭代器、算法是STL的三个基本组成部分
容器:是对象的集合,举例:vector、list、stack、queue、set、map、deque
迭代器:面向对象版本的指针,STL算法通过迭代器在容器上进行操作
算法:对容器进程处理的函数,如:for_each、copy、sort、merge、search
1.2vector容器
与
C++volatile
sinat_31608641的博客
08-04
1714
一、背景
Volatile,词典上的解释为:易失的;易变的;易挥发的。
那么用这个关键词修饰的C/C++变量,应该也能够体现出”易变”的特征。
大部分人认识Volatile,也是从这个特征出发。
olatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从内存中读取数据。
如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象易变性。
Volatile关键词的第二个特性
详解C/C++中volatile关键字
热门推荐
小白的逆袭
06-19
7万+
一、volatile介绍
volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。
如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。
下面举例说明。
在DSP开发中,经常需要等待某个事件的触发,所以经常会写出这样的程序:
这段...
面试常见问题,c++11新特性
Pretender_1的博客
04-03
1860
新特性
auto关键字,编译器根据上下文情况确定auto的真正类型
decltype,有点类似于auto的反函数,能够获取变量类型inta=1;decltype(a)b=a;
nullptr,空指针,为了解决原来C++中NULL的二义性问题而引进的一种新的类型,因为NULL实际上代表的是0
voidF(inta){
cout<
延伸文章資訊
- 1面试官最爱的volatile 关键字,这些问题你都搞懂了没?
volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。 传递性:如果(A)happens-before(B),且(B)hap...
- 2【C/C++面試必備】volatile 關鍵字 - 有解無憂
volatile 意思是易變的,是一種型別修飾符,在C/C++中用來阻止編譯器因誤認某段代碼無法被代碼本身所改變,而造成的過度優化,編譯器每次讀取volatile ...
- 3C語言中volatile關鍵字詳解以及常見的面試問題 - tw511教學網
C語言中volatile關鍵字的作用. volatile int a; main() { a=0; b = a; printf("b = %d\n", b); }. volatile的意思是「多...
- 4volatile关键字有什么作用- C语言面试宝典 - 黑马程序员教程
黑马程序员教程前端面试宝典模块专注提供各类C语言开发面试题及答案,致力收集C语言 ... C/C++ Volatile变量,与非Volatile变量之间的操作,是可能被编译器交换顺序的。
- 5面試C/C++ 觀念整理
這裡有些題目是來自網路上MTK面試的C考古題,另外又加入了一些易搞混的觀念 ... 一個定義為volatile 的變量是說這變量可能會被意想不到地改變(尤其在 ...