C++常见面试题(11)——volatile_哎呦 - CSDN博客

文章推薦指數: 80 %
投票人數:10人

本文主要介绍在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中可以直接在变量名后面跟上初始化列表来进行对象的初始化,使用起来更加方便,例如: vectorvec; //C++98/03给vector对象的初始化方式 vec.push_back(1); vec.push_back(2); [面试/笔试系列10]C/C++面试题大汇总 11-17 1.用预处理指令#define声明一个常数,用以表明1年中有多少秒(忽略闰年问题) #defineSECONDS_PER_YEAR(60*60*24*365)UL 我在这想看到几件事情: 1)#define语法的基本知识(例如:不能以分号结束,括号的使用,等等) 2)懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多 少秒而不是计算出实际的值,是更清晰而没有代价的。

参与评论 您还未登录,请先 登录 后发表或查看评论 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<



請為這篇文章評分?