C语言中volatile关键字的作用及说明

 更新时间:2024年10月29日 14:31:05   作者:莱特昂  
文中主要介绍了C语言中volatile关键字的含义和使用场景,volatile是一个类型修饰符,主要用来修饰被不同线程访问和修改的变量,它的作用是防止编译器对代码进行优化,确保每次直接读取原始内存地址的值

volatile关键字的作用

在看C语言基础知识的时候看到了volatile关键字,不是很理解,所以查了资料,总结一下。

volatile译为:易变的

  • volatile是一个类型修饰符(type specifier),就像我们熟悉的const一样,它是被设计用来修饰被不同线程访问和修改的变量;
  • volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。

简单地说就是防止编译器对代码进行优化。

比如如下程序:

XBYTE[2]=0x55;
XBYTE[2]=0x56;
XBYTE[2]=0x57;
XBYTE[2]=0x58;

对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,但是编译器却会对上述四条语句进行优化,认为只有XBYTE[2]=0x58(即忽略前三条语句,只产生一条机器代码)。

如果键入volatile,则编译器会逐一地进行编译并产生相应的机器代码(产生四条代码)。

典型的例子

for(int i=0; i<100000; i++);

这个语句用来测试空循环的速度的

但是编译器肯定要把它优化掉,根本就不执行

如果你写成

for(volatile int i=0; i<100000; i++);

它就会执行了

volatile的本意是“易变的”

下面是volatile变量的几个例子:

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

2)一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

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

一些相关的面试题

1)一个参数既可以是const还可以是volatile吗?解释为什么。

2)一个指针可以是volatile 吗?解释为什么。

3)下面的函数被用来计算某个整数的平方,它能实现预期设计目标吗?如果不能,试回答存在什么问题:

int square(volatile int *ptr)
{
   return ((*ptr) * (*ptr));
}

下面是答案:

1)是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

2)是的。尽管这并不很常见。一个例子是当一个中断服务子程序修改一个指向一个buffer的指针时。

3)这段代码是个恶作剧。这段代码的目的是用来返指针ptr指向值的平方,但是,由于ptr指向一个volatile型参数,编译器将产生类似下面的代码:

int square(volatile int* &ptr)//这里参数应该申明为引用,不然函数体里只会使用副本,外部没法更改

{
    int a,b;
    a = *ptr;
    b = *ptr;
    return a*b;
}

由于ptr的值可能在两次取值语句之间发生改变,因此a和b可能是不同的。

结果,这段代码可能返回的不是你所期望的平方值!

正确的代码如下:

long square(volatile intptr)
{
    int a;
    a = *ptr;
    return a*a;
}

另外,还可能出现在debug版中看不出来,在release版中才显现的问题:

#include<stdio.h>
void main(int argc,char *argv[])
{
 int i = 10;
 int a = i;
 printf("i=%d",a);
 //下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道
__asm
    {
        mov dword ptr[ebp-4],20h
    }
    int b = i;
    printf("i=%d",b);
}

然后,在调试版本模式运行程序,输出结果如下:

i = 10
i = 32

然后,在release版本模式运行程序,输出结果如下:

i = 10
i = 10

输出的结果明显表明,release模式下,编译器对代码进行了优化,第二次没有输出正确的i值。

我们把 i的声明加上volatile关键字,看看有什么变化:

#include<stdio.h>
void main(int argc,char *argv[])
{
    volatile int i = 10;
    int a = i;
    printf("i=%d",a);
    __asm
    {
    `    mov dword ptr[ebp-4],20h
    }
    int b = i;
    printf("i=%d",b);
}

分别在调试版本和release版本运行程序,输出都是:

i = 10
i = 32

这说明这个关键字发挥了它的作用!

volatile应该解释为“直接存取原始内存地址”比较合适,“易变的”这种解释简直有点误导人;

比如多线程的程序,共同访问的内存当中,多个程序都可以操纵这个变量

你自己的程序,是无法判定何时这个变量会发生变化

还比如,他和一个外部设备的某个状态对应,当外部设备发生操作的时候,通过驱动程序和中断事件,系统改变了这个变量的数值,而你的程序并不知道。

对于volatile类型的变量,系统每次用到他的时候都是直接从对应的内存当中提取,而不会利用cache当中的原有数值,以适应它的未知何时会发生的变化,系统对这种变量的处理不会做优化——显然也是因为它的数值随时都可能变化的情况。

使用地方

一般说来,volatile用在如下的几个地方:

1、中断服务程序中修改的供其它程序检测的变量需要加volatile;

2、多任务环境下各任务间共享的标志应该加volatile;

3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义;

另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2 中可以禁止任务调度,3中则只能依靠硬件的良好设计了。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 项目之C++如何实现数据库连接池

    项目之C++如何实现数据库连接池

    这篇文章主要介绍了项目之C++如何实现数据库连接池问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • C++结构体案例练习分享

    C++结构体案例练习分享

    这篇文章主要和大家分享几个C++ 结构体的案例练习,帮助大家更好的理解和学习c++,感兴趣的朋友可以了解下,希望能够给你带来帮助
    2022-04-04
  • C语言中的多行输入问题及说明

    C语言中的多行输入问题及说明

    这篇文章主要介绍了C语言中的多行输入问题及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • 数据结构 双机调度问题的实例详解

    数据结构 双机调度问题的实例详解

    这篇文章主要介绍了数据结构 双机调度问题的实例详解的相关资料,双机调度问题,又称独立任务最优调度:用两台处理机A和B处理n个作业的实例,需要的朋友可以参考下
    2017-08-08
  • 详解C++中递增运算符重载的实现

    详解C++中递增运算符重载的实现

    本文主要详解运算符重载里的递增运算符重载;递增和递减原理是一样的,这里就只分享递增的重载;提到递增递减,我们都知道又前置和后置两种方法, 那今天就详解一下前置递增和后置递增的细节,拿捏递增运算符重载
    2022-06-06
  • C语言实现绘制绕线画的示例代码

    C语言实现绘制绕线画的示例代码

    绕线画简单点来说,就是在木板上钉一圈钉子,通过绕线进行构图,最终呈现出一幅图像。本文将用C语言实现这一效果,感兴趣的小伙伴可以尝试一下
    2022-11-11
  • QT圆形图像剪切功能实现

    QT圆形图像剪切功能实现

    这篇文章主要介绍了QT圆形图像剪切,实现代码包括剪切代码,完整QML源码,C++代码,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-10-10
  • C语言实现单链表反转

    C语言实现单链表反转

    这篇文章主要介绍了C语言实现单链表反转,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • C++深入探究引用的使用

    C++深入探究引用的使用

    引用是C++一个很重要的特性,顾名思义是某一个变量或对象的别名,对引用的操作与对其所绑定的变量或对象的操作完全等价,这篇文章主要给大家总结介绍了C++中引用的相关知识点,需要的朋友可以参考下
    2022-05-05
  • C++实现LeetCode(157.用Read4来读取N个字符)

    C++实现LeetCode(157.用Read4来读取N个字符)

    这篇文章主要介绍了C++实现LeetCode(157.用Read4来读取N个字符),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07

最新评论