首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

C++面试基础系列-volatile

  • 25-02-19 07:40
  • 2496
  • 5843
blog.csdn.net

系列文章目录

总目录链接


文章目录

  • 系列文章目录
    • 总目录链接
  • C++面试基础系列-volatile
    • 1.volatile核心规则
    • 2.C与C++中volatile区别
      • 2.1.C语言中的volatile
      • 2.2.C++中的volatile
      • 2.3.原子性和顺序
      • 2.4.易失性
      • 2.5.优化
      • 2.6.使用场景
      • 2.7.C++特有的特性
      • 2.8.C++20引入的变化(如果有)
    • 3.volatile常见面试问题
    • 4.volatile与const与指针
    • 5.应用场景
    • 6.应用示例
    • 关于作者


C++面试基础系列-volatile

1.volatile核心规则

  • volatile修饰变量或指针功能
    • 告诉编译器,被volatile修饰的变量或指针(寄存器或硬件寄存器),不要进行优化
    • 变量或指针在中断服务子程序,用户函数中同时调用,不要进行优化
    • 多线程,多任务同时调用变量或指针,使用volatile修饰,表示每次操作该变量或指针,都要从内存中取最新的值进行操作。
  • 简单来说
    • 编译器,不要优化
    • 从内存取值

2.C与C++中volatile区别

  • 在C和C++中,volatile关键字都用于告诉编译器一个变量可能会在程序的控制之外被改变,通常是由于硬件的作用。尽管C和C++都支持volatile,但它们在某些方面存在一些差异:

2.1.C语言中的volatile

  • 在C语言中,volatile主要用于多线程环境或中断服务例程(ISR)中,指示编译器该变量可能会在任何时候被改变,因此每次使用时都应该从内存中重新读取,而不是从寄存器中。
  • C标准并没有详细说明volatile的所有行为,尤其是在多线程同步方面。

2.2.C++中的volatile

  • C++中的volatile用法与C类似,但C++标准对volatile的行为有更明确的定义,特别是在多线程环境中。
  • 在C++11及以后的版本中,标准定义了内存模型,volatile变量的访问将遵循这些规则,以确保在多线程环境中对它们的访问是原子的(如果需要)。

2.3.原子性和顺序

  • 在C++11中,volatile关键字不保证操作的原子性,即使在多线程环境中也是如此。如果需要原子性,C++提供了std::atomic模板和相关的原子操作函数。
  • C++中的volatile也不保证编译器不会重排相关操作的顺序,这与C类似。

2.4.易失性

  • 在C和C++中,volatile都可以用来指示编译器一个变量是易失性的,即它可能会因为外部硬件事件而改变。

2.5.优化

  • 由于volatile告诉编译器变量可能在任何时候改变,编译器将不会对这个变量进行优化。

2.6.使用场景

  • 在嵌入式编程中,volatile经常用于访问内存映射的硬件寄存器,因为这些寄存器可能会被外部硬件改变。

2.7.C++特有的特性

  • C++中volatile的使用可能会受到C++特有的特性影响,如类和对象模型。例如,C++中的volatile成员变量需要特别注意,因为它们的行为可能不如预期。

2.8.C++20引入的变化(如果有)

  • C++20标准可能会对volatile有进一步的说明或改进,但截至知识截止日期,这些变化尚未明确。

总结来说,C和C++中的volatile在基本用途上相似,但C++标准提供了更明确的定义,尤其是在多线程环境中。在C++中,如果需要原子性或线程安全的同步,应该使用std::atomic或其他同步机制,而不是仅仅依赖volatile。

3.volatile常见面试问题

在面试中,volatile 关键字是一个常见的话题,以下是一些可能会问到的问题以及相关的答案:

  1. volatile 关键字是什么?
    volatile 是一种类型修饰符,用于告诉编译器该变量可能会在程序的控制之外被改变,例如由中断服务程序、多线程或硬件修改。使用 volatile 可以防止编译器对这些变量进行优化,确保每次访问都直接从内存中读取或写入,从而保证数据的一致性。
  2. volatile 变量的可见性如何保证?
    volatile 变量保证了对所有线程的可见性。当一个线程修改了一个 volatile 变量时,其他线程能够立即看到这个修改,这是因为 volatile 变量的写操作会立即刷新到主内存中,而读操作会从主内存中读取最新值。
  3. volatile 能否保证原子性?
    volatile 关键字不能保证操作的原子性。它只能确保单次的读/写操作具有原子性,但对于复合操作,如自增(i++),volatile 无法保证整个操作的原子性。因此,对于需要原子性保证的操作,应该使用其他同步机制,如 synchronized 或 AtomicInteger。
  4. volatile 变量在多线程中的使用场景有哪些?
    volatile 变量适用于多线程环境下的某些特定场景,例如:
    • 作为状态标志,用于控制线程间的协调,如退出循环的条件。
    • 在没有其他同步机制的情况下,用于保护简单的状态变量,以确保变量的可见性和有序性。
    • 与锁结合使用,实现读写锁模式,提高性能。
  5. volatile 与 const 的区别是什么?
    const 关键字用于定义常量,即值不能被修改的变量。而 volatile 用于定义可能会在程序的控制之外被改变的变量。const 保证的是值的不变性,而 volatile 保证的是变量的可见性和禁止指令重排序。
  6. volatile 指针是什么?
    volatile 指针是指指向的变量可能会在程序的控制之外被改变的指针。它可以是指向 volatile 变量的指针,也可以是指针本身的值是 volatile 的。在使用 volatile 指针时,编译器不会对通过该指针进行的操作进行优化。
  7. 在嵌入式系统中,volatile 的作用是什么?
    在嵌入式系统中,volatile 通常用于声明与硬件寄存器相关的变量,以确保编译器不会对与硬件交互的代码进行优化。这是因为硬件寄存器的值可能会由硬件本身的操作所改变,需要每次访问时都从内存中读取最新值。
  8. volatile 能否替代 synchronized?
    volatile 在某些情况下可以作为 synchronized 的轻量级替代,特别是在读操作远多于写操作的场景下。但是,由于 volatile 不能保证复合操作的原子性,它不能完全替代 synchronized。在需要原子性保证的情况下,仍然需要使用 synchronized 或其他同步机制。

理解 volatile 的作用和限制对于编写正确的多线程程序至关重要。在面试中,展示对 volatile 的深入理解可以体现候选人的专业知识和经验。

4.volatile与const与指针

  • (1)一个参数既可以是const还可以是volatile吗?为什么?
    可以。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
  • (2)一个指针可以是volatile吗?为什么?
    可以。尽管这并不常见。一个例子是当一个中断服务子程序修改一个指向一个buffer的指针时。

5.应用场景

在嵌入式系统开发中,正确地使用 volatile 关键字对于避免硬件中断对程序执行的影响至关重要。以下是一些关于如何在嵌入式系统中使用 volatile 的关键点:

  • 防止编译器优化:volatile 告诉编译器,即使在代码中看似没有改变,变量的值也可能在任何时候改变,因此编译器不应进行优化
  • 中断服务程序中的变量:在中断服务程序(ISR)中,经常需要访问或修改一些变量。如果这些变量在主程序中也被访问,那么它们应该被声明为 volatile,以确保每次访问时都能获取最新的值
  • 多线程共享变量:在多线程环境中,如果多个任务共享某些变量,并且这些变量的值可能被任何一个任务改变,那么这些变量也应该被声明为 volatile,以确保所有任务都能看到其他任务对共享变量的最新修改
  • 硬件寄存器访问:在嵌入式编程中,硬件寄存器的值可能会被硬件本身的操作所改变。使用 volatile 修饰硬件寄存器可以确保每次访问都是直接从硬件寄存器中读取,而不是从CPU缓存中
  • 保证内存顺序:volatile 还可以防止编译器和处理器对指令的重排序,确保指令按照代码中的顺序执行,这对于中断和主程序之间的同步尤为重要
  • 使用场景:volatile 适用于并行设备的硬件寄存器、中断服务子程序中访问的非自动变量、多线程应用中被多个任务共享的变量,以及需要防止编译器优化的情况,如for循环延时程序
  • 注意限制:虽然 volatile 可以确保变量的可见性,但它不保证操作的原子性。在多线程环境中,如果需要原子性,还需要使用其他同步机制,如互斥锁
  • 性能影响:使用 volatile 可能会降低程序的性能,因为它阻止了编译器进行某些优化。因此,只有在必要时才应该使用 volatile

总结来说,volatile 在嵌入式系统中是一个关键的关键字,用于确保变量的值能够反映出最新的状态,特别是在中断服务程序和多线程环境中。然而,开发者应当谨慎使用,避免滥用,同时注意它并不能替代其他同步机制来保证操作的原子性。

6.应用示例

(1)并行设备的硬件寄存器(如状态寄存器)。

  • 假设要对一个设备进行初始化,此设备的寄存器为0x0x80008004。
unsigned int  *output = (unsigned  int *)0x0x80008004; //定义一个IO端口;  
int main(void)  
{  
    int i;  
    for(i=0;i< 10;i++)
    {  
      *output = i;  
    }  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 如果开启的 -O3 优化,那么经过编译器优化后,编译器认为前面循环,对最后的结果毫无影响。
  • 最终只是将output这个指针赋值为 9,所以汇编后的程序相当于:
int  init(void)  
{  
    *output = 9;  
}
  • 1
  • 2
  • 3
  • 4
  • 如果你需要程序完全按照你所写程序运行,那就用volatile修饰变量。
  • 通知编译器这个变量是一个不稳定的,在遇到此变量时候不要优化。

(2)一个中断服务子程序中访问到的变量;

static int i=0;

int main()
{
    while(1)
    {
    if(i) dosomething();
    }
}

/* Interrupt service routine */
void IRS()
{
 i=1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 上面示例程序的本意是产生中断时,由中断服务子程序IRS响应中断,变更程序变量i,使在main函数中调用dosomething函数,
  • 但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远不会被调用。
  • 如果将变量i加上volatile修饰,则编译器保证对变量i的读写操作都不会被优化,从而保证了变量i被外部程序更改后能及时在原程序中得到感知。

(3)多线程应用中被多个任务共享的变量。

  • 当多个线程共享某一个变量时,该变量的值会被某一个线程更改,应该用 volatile 声明。
  • 作用是防止编译器优化把变量从内存装入CPU寄存器中,当一个线程更改变量后,未及时同步到其它线程中导致程序出错。
  • volatile的作用是让编译器每次操作该变量时一定要从内存中真正取出,而不是使用已经存在寄存器中的值。示例如下:
volatile  bool bStop=false;  //bStop 为共享全局变量  
//第一个线程
void threadFunc1()
{
    ...
    while(!bStop){...}
}
//第二个线程终止上面的线程循环
void threadFunc2()
{
    ...
    bStop = true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 要想通过第二个线程终止第一个线程循环,如果bStop不使用volatile定义,那么这个循环将是一个死循环,因为bStop已经读取到了寄存器中,寄存器中bStop的值永远不会变成FALSE,
  • 加上volatile,程序在执行时,每次均从内存中读出bStop的值,就不会死循环了。

关于作者

  • 微信公众号:WeSiGJ
  • GitHub:https://github.com/wesigj/cplusplusboys
  • CSDN:https://blog.csdn.net/wesigj
  • 微博:
  • -版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
WeSiGJ
微信公众号
共同分享,共同交流, 共同学习!
注:本文转载自blog.csdn.net的WeSiGJ的文章"https://blog.csdn.net/wesigj/article/details/141346972"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2491) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top