volatile用法的疑惑

时间:2021-10-08 20:56:14
volatile表示这个变量会被意想不到的改变,既然是意想不到,那怎么知道该给哪个设成volatile?你可以举例状态寄存器里的数(我刚看到的),但这些都是后验的吧?前人吃过亏我们才知道那个是会意想不到被更改,但既然是意想不到,任何都不能确保。

24 个解决方案

#1


volatile int a = 0;

printf("%d", a); // volatile的意义就是这个时候a可能就不是0了。

改变a的肯定是和当前线程异步的东西,比如另外一个线程也可能是硬件中断之类。如果不存在那些东西去修改a,a就没必要是volatile的。

#2


我认真学习过volatile关键字的使用。
“volatile表示这个变量会被意想不到的改变”这句话真心是句废话,真心迷惑新手,这句话的本意是说:那些可能会被其他线程改变的变量应该使用volatile关键字修饰。
下面的话很经典,很容易理解,理解了你就知道volatile关键字的使用了。
volatile关键字用来通知编译器此变量可能会被其他线程修改,每次对变量的访问都必须从内存重新读取。编译器不要对此变量进行优化。

//注意:请使用C++编译器,而不是C语言编译器!
#include <stdio.h>
int main()
{
    const int m = 10;
    int *p = (int*)&m;
    printf("%d\n",m);       //m等于几?
    *p = 12;
    printf("%d\n",m);       //m等于几?
    return 0;
}

#3


哈哈,貌似没几个人能猜对此程序运行的答案哦。

#4


引用 楼主 shaode01 的回复:
volatile表示这个变量会被意想不到的改变,既然是意想不到,那怎么知道该给哪个设成volatile?你可以举例状态寄存器里的数(我刚看到的),但这些都是后验的吧?前人吃过亏我们才知道那个是会意想不到被更改,但既然是意想不到,任何都不能确保。


最常见的例子:设备的寄存器,你去读状态寄存器的值的时候,读的过程中不用volatile可能读到cpu缓存的副本而不是去那个物理地址取出实际的值,因为设备的微观状态随时都可能变,所以你得保证不用缓存里面的副本.

#5


引用 2 楼 u010609597 的回复:
我认真学习过volatile关键字的使用。
“volatile表示这个变量会被意想不到的改变”这句话真心是句废话,真心迷惑新手,这句话的本意是说:那些可能会被其他线程改变的变量应该使用volatile关键字修饰。
下面的话很经典,很容易理解,理解了你就知道volatile关键字的使用了。
volatile关键字用来通知编译器此变量可能会被其他线程修改,每次对变量的访问都必须从内存重新读取。编译器不要对此变量进行优化。

//注意:请使用C++编译器,而不是C语言编译器!
#include <stdio.h>
int main()
{
    const int m = 10;
    int *p = (int*)&m;
    printf("%d\n",m);       //m等于几?
    *p = 12;
    printf("%d\n",m);       //m等于几?
    return 0;
}

额,你这里是const,只读噻,都是10吧

#6


http://blog.csdn.net/c_bg44/article/details/1538235

#7


volatile 是在C ,C++,Java 等中语言中的一种修饰关键字。
这个关键字在嵌入式系统中,是一个非常重要的一个使用。尽管在一般的Application 中,
可能很多人都不需要使用这个。但是在单片机中,如果不熟悉这个关键字,很有可能产生想
像不到的意外。
那么,我就来谈谈Volatile 的意义--
volatile 在ANSI C(C89)以后的C 标准规格和const 一起被包含在内。在标准C 中,这些关键
字是必定存在的。
关于volatile 的意义,根据标准C 的定义、
volatile 的目的是,避免进行默认的优化处理.比如说对于编译器优化的功能,如果从
编译器看来,有些多余的代码的话,编译器就会启动优化程序,并删除一些代码,但是这在
嵌入式系统中很有可能是关键性的处理,必须不能保证被编译器删掉,所以提供了Volitile
来声明,告诉编译器无论如何都不要删掉我。

#8


举个例子--
■比如说下面条件的一段代码
extern int event_flag
void poll_event()
{
while (event_flag == 0) {
/* 不操作event_flag */
....
}
....
}
我们不再循环中改变这里的event_flag 的值,这样的话,event_flag 看起来就像是多余的,
因此单片机编译器可能把此程序看为下段程序
void poll_event()
{
if (event_flag == 0) {
while (1) {
/* 不对event_flag 操作*/
....
}
}
....
}
对于一般的编译器,一般都会把程序优化成上述程序。
这样的优化确实可以提高代码速度,比如while 循环中不再需要对条件的判断,所以很快,
但是这是正确的吗?
对于单线程的程序,这是没有问题的,因为event_flag 就永远不会改变,但是对于多线程程
序,RTOS 的多任务处理的话,event_flag 的值可能被其他线程改变,这样问题就来了,因
为被优化的代码并不具备对用event_flag 变化的能力。因此导致错误的意想不到的结果,如
果此代码在ECU 上执行的话,那我们的小命可就有可能没了。。。。
-----------------------------我是分割线-------------------------------------------
以上均转自网络,不是我写的,仅供参考

#9


比如从一个地址读数据,但这个地址并不是内存,而是别的芯片的寄存器。

#10


引用 2 楼 u010609597 的回复:
我认真学习过volatile关键字的使用。
“volatile表示这个变量会被意想不到的改变”这句话真心是句废话,真心迷惑新手,这句话的本意是说:那些可能会被其他线程改变的变量应该使用volatile关键字修饰。
下面的话很经典,很容易理解,理解了你就知道volatile关键字的使用了。
volatile关键字用来通知编译器此变量可能会被其他线程修改,每次对变量的访问都必须从内存重新读取。编译器不要对此变量进行优化。

//注意:请使用C++编译器,而不是C语言编译器!
#include <stdio.h>
int main()
{
    const int m = 10;
    int *p = (int*)&m;
    printf("%d\n",m);       //m等于几?
    *p = 12;
    printf("%d\n",m);       //m等于几?
    return 0;
}


C编译器下结果是 10 12
C++编译器下是 10 10 !!!

把m的类型定义为olatile的话,两者的结构就是一致的了。。

#11


我认同2楼的高见。

#12


建议帖主学学啥叫“中断”。

#13


计算机组成原理(中断、DMA、……)→DOS命令→汇编语言→C语言(不包括C++)、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言(包括C++)、架构……

#14


引用 10 楼 chenxwzcc 的回复:
Quote: 引用 2 楼 u010609597 的回复:

我认真学习过volatile关键字的使用。
“volatile表示这个变量会被意想不到的改变”这句话真心是句废话,真心迷惑新手,这句话的本意是说:那些可能会被其他线程改变的变量应该使用volatile关键字修饰。
下面的话很经典,很容易理解,理解了你就知道volatile关键字的使用了。
volatile关键字用来通知编译器此变量可能会被其他线程修改,每次对变量的访问都必须从内存重新读取。编译器不要对此变量进行优化。

//注意:请使用C++编译器,而不是C语言编译器!
#include <stdio.h>
int main()
{
    const int m = 10;
    int *p = (int*)&m;
    printf("%d\n",m);       //m等于几?
    *p = 12;
    printf("%d\n",m);       //m等于几?
    return 0;
}


C编译器下结果是 10 12
C++编译器下是 10 10 !!!

把m的类型定义为olatile的话,两者的结构就是一致的了。。

C编译器下结果是 10 12,这是为啥?是因为强制类型转换吗?那c++怎么没有强制类型转换?

#15


引用 12 楼 zhao4zhong1 的回复:
建议帖主学学啥叫“中断”。

楼主学过中断的说,下面那一串课程除了编译原理只听过头几次课...,我只是像2楼的童鞋那样对这个“意想不到”的说法不满,如果意想不到对于再牛的人都是意想不到,没法预料,这不是一件很令人沮丧的事么?如果是无法预料什么时候出错但能预料到会在什么情况下出错,那也不能算是“意想不到”。就像我虽然不知道什么时候下雨,但随身带把伞就行了,不能说“意想不到”啊

#16


意想不到是针对编译器和CPU而言,并非针对使用编译器和CPU的人而言。

#17


引用 5 楼 shaode01 的回复:
额,你这里是const,只读噻,都是10吧

那那你再看看以下这段C++程序的运行结果吧:
#include <stdio.h>
int main()
{
    volatile const int m = 10;
    int *p = (int*)&m;
    printf("%d\n",m);       //m等于几?
    *p = 12;
    printf("%d\n",m);       //m等于几?
    return 0;
}

想一想,这段代码的执行结果会是多少吧,与上面的代码进行对比,认真想想,你就彻底知道volatile到底是干啥的啦。

#18


引用 2 楼 u010609597 的回复:
我认真学习过volatile关键字的使用。
“volatile表示这个变量会被意想不到的改变”这句话真心是句废话,真心迷惑新手,这句话的本意是说:那些可能会被其他线程改变的变量应该使用volatile关键字修饰。
下面的话很经典,很容易理解,理解了你就知道volatile关键字的使用了。
volatile关键字用来通知编译器此变量可能会被其他线程修改,每次对变量的访问都必须从内存重新读取。编译器不要对此变量进行优化。

//注意:请使用C++编译器,而不是C语言编译器!
#include <stdio.h>
int main()
{
    const int m = 10;
    int *p = (int*)&m;
    printf("%d\n",m);       //m等于几?
    *p = 12;
    printf("%d\n",m);       //m等于几?
    return 0;
}

这程序有两个问题:
1、const简单类型常量,编译器实现方式有两种。。
一是把它和变量一起分配内存,只是检查对其的直接赋值,这时如此糊弄编译器,改它的值没有问题。
二是把它放在只读数据段,这时此程序会出现运行时错误。可以预见,在某些编译器上可能会出现运行时错误。
2、如果没有运行时错误,输出结果是什么?其实也是不确定的,这和编译器具体采用的优化策略有关啊。。语言规范不可能去规定优化策略的。。m是一个只读变量,编译器可以优化么?可能有的会,有的不会,甚至一个编译器在不同优化选项下都可以有不一样的结果吧。。

#19


我觉得楼上说的在理

#20


引用 17 楼 u010609597 的回复:
Quote: 引用 5 楼 shaode01 的回复:

额,你这里是const,只读噻,都是10吧

那那你再看看以下这段C++程序的运行结果吧:
#include <stdio.h>
int main()
{
    volatile const int m = 10;
    int *p = (int*)&m;
    printf("%d\n",m);       //m等于几?
    *p = 12;
    printf("%d\n",m);       //m等于几?
    return 0;
}

想一想,这段代码的执行结果会是多少吧,与上面的代码进行对比,认真想想,你就彻底知道volatile到底是干啥的啦。


在VS2008中, 加不加volatile都是 10, 12
这也符合正常的理解.
虽然你对m加了const, 但是后面的(int*)&m强制转换已经破坏了const保护, 所以*p这里肯定会修改m的值.
这也就是指针的厉害之处.

只是在多线程编程中, 如果没有加 volatile 的变量 , 可能被编译器优化成一个寄存器变量, 耍不是从固定地址读取奇值, 这时, 如果有线程改变了地址里的值, 但寄存器变量中就没有被更新, 就会导致差异性.

#21


引用 3 楼 u010609597 的回复:
哈哈,貌似没几个人能猜对此程序运行的答案哦。

你还别臭美,这种修改常量值的方法是不安全的,虽然确实可以修改

#22


不要纠结各种常量了,这个世界上唯一不变的就是变化。用API WriteProcessMemory还能修改正运行的其它进程的内存里面的所谓常量呢!

不要迷信书、考题、老师、回帖;
要迷信CPU、编译器、调试器、运行结果。
并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。
任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实!

#23


volatile的意思是易变的,加这个关键词的作用是通知编译器甚至cache不要试图去做基于当前值不会改变的优化,每次访问的时候都需要去内存重新读取。这个volatile一般是给硬件的寄存器使用的,因为硬件寄存器的修改不是由cpu决定的,而是由硬件自己决定。

#24


引用 23 楼 HUANGFEIDIAN 的回复:
volatile的意思是易变的,加这个关键词的作用是通知编译器甚至cache不要试图去做基于当前值不会改变的优化,每次访问的时候都需要去内存重新读取。这个volatile一般是给硬件的寄存器使用的,因为硬件寄存器的修改不是由cpu决定的,而是由硬件自己决定。

那这个意想不到就是说cpu意想不到而不是我们意想不到咯?

#1


volatile int a = 0;

printf("%d", a); // volatile的意义就是这个时候a可能就不是0了。

改变a的肯定是和当前线程异步的东西,比如另外一个线程也可能是硬件中断之类。如果不存在那些东西去修改a,a就没必要是volatile的。

#2


我认真学习过volatile关键字的使用。
“volatile表示这个变量会被意想不到的改变”这句话真心是句废话,真心迷惑新手,这句话的本意是说:那些可能会被其他线程改变的变量应该使用volatile关键字修饰。
下面的话很经典,很容易理解,理解了你就知道volatile关键字的使用了。
volatile关键字用来通知编译器此变量可能会被其他线程修改,每次对变量的访问都必须从内存重新读取。编译器不要对此变量进行优化。

//注意:请使用C++编译器,而不是C语言编译器!
#include <stdio.h>
int main()
{
    const int m = 10;
    int *p = (int*)&m;
    printf("%d\n",m);       //m等于几?
    *p = 12;
    printf("%d\n",m);       //m等于几?
    return 0;
}

#3


哈哈,貌似没几个人能猜对此程序运行的答案哦。

#4


引用 楼主 shaode01 的回复:
volatile表示这个变量会被意想不到的改变,既然是意想不到,那怎么知道该给哪个设成volatile?你可以举例状态寄存器里的数(我刚看到的),但这些都是后验的吧?前人吃过亏我们才知道那个是会意想不到被更改,但既然是意想不到,任何都不能确保。


最常见的例子:设备的寄存器,你去读状态寄存器的值的时候,读的过程中不用volatile可能读到cpu缓存的副本而不是去那个物理地址取出实际的值,因为设备的微观状态随时都可能变,所以你得保证不用缓存里面的副本.

#5


引用 2 楼 u010609597 的回复:
我认真学习过volatile关键字的使用。
“volatile表示这个变量会被意想不到的改变”这句话真心是句废话,真心迷惑新手,这句话的本意是说:那些可能会被其他线程改变的变量应该使用volatile关键字修饰。
下面的话很经典,很容易理解,理解了你就知道volatile关键字的使用了。
volatile关键字用来通知编译器此变量可能会被其他线程修改,每次对变量的访问都必须从内存重新读取。编译器不要对此变量进行优化。

//注意:请使用C++编译器,而不是C语言编译器!
#include <stdio.h>
int main()
{
    const int m = 10;
    int *p = (int*)&m;
    printf("%d\n",m);       //m等于几?
    *p = 12;
    printf("%d\n",m);       //m等于几?
    return 0;
}

额,你这里是const,只读噻,都是10吧

#6


http://blog.csdn.net/c_bg44/article/details/1538235

#7


volatile 是在C ,C++,Java 等中语言中的一种修饰关键字。
这个关键字在嵌入式系统中,是一个非常重要的一个使用。尽管在一般的Application 中,
可能很多人都不需要使用这个。但是在单片机中,如果不熟悉这个关键字,很有可能产生想
像不到的意外。
那么,我就来谈谈Volatile 的意义--
volatile 在ANSI C(C89)以后的C 标准规格和const 一起被包含在内。在标准C 中,这些关键
字是必定存在的。
关于volatile 的意义,根据标准C 的定义、
volatile 的目的是,避免进行默认的优化处理.比如说对于编译器优化的功能,如果从
编译器看来,有些多余的代码的话,编译器就会启动优化程序,并删除一些代码,但是这在
嵌入式系统中很有可能是关键性的处理,必须不能保证被编译器删掉,所以提供了Volitile
来声明,告诉编译器无论如何都不要删掉我。

#8


举个例子--
■比如说下面条件的一段代码
extern int event_flag
void poll_event()
{
while (event_flag == 0) {
/* 不操作event_flag */
....
}
....
}
我们不再循环中改变这里的event_flag 的值,这样的话,event_flag 看起来就像是多余的,
因此单片机编译器可能把此程序看为下段程序
void poll_event()
{
if (event_flag == 0) {
while (1) {
/* 不对event_flag 操作*/
....
}
}
....
}
对于一般的编译器,一般都会把程序优化成上述程序。
这样的优化确实可以提高代码速度,比如while 循环中不再需要对条件的判断,所以很快,
但是这是正确的吗?
对于单线程的程序,这是没有问题的,因为event_flag 就永远不会改变,但是对于多线程程
序,RTOS 的多任务处理的话,event_flag 的值可能被其他线程改变,这样问题就来了,因
为被优化的代码并不具备对用event_flag 变化的能力。因此导致错误的意想不到的结果,如
果此代码在ECU 上执行的话,那我们的小命可就有可能没了。。。。
-----------------------------我是分割线-------------------------------------------
以上均转自网络,不是我写的,仅供参考

#9


比如从一个地址读数据,但这个地址并不是内存,而是别的芯片的寄存器。

#10


引用 2 楼 u010609597 的回复:
我认真学习过volatile关键字的使用。
“volatile表示这个变量会被意想不到的改变”这句话真心是句废话,真心迷惑新手,这句话的本意是说:那些可能会被其他线程改变的变量应该使用volatile关键字修饰。
下面的话很经典,很容易理解,理解了你就知道volatile关键字的使用了。
volatile关键字用来通知编译器此变量可能会被其他线程修改,每次对变量的访问都必须从内存重新读取。编译器不要对此变量进行优化。

//注意:请使用C++编译器,而不是C语言编译器!
#include <stdio.h>
int main()
{
    const int m = 10;
    int *p = (int*)&m;
    printf("%d\n",m);       //m等于几?
    *p = 12;
    printf("%d\n",m);       //m等于几?
    return 0;
}


C编译器下结果是 10 12
C++编译器下是 10 10 !!!

把m的类型定义为olatile的话,两者的结构就是一致的了。。

#11


我认同2楼的高见。

#12


建议帖主学学啥叫“中断”。

#13


计算机组成原理(中断、DMA、……)→DOS命令→汇编语言→C语言(不包括C++)、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言(包括C++)、架构……

#14


引用 10 楼 chenxwzcc 的回复:
Quote: 引用 2 楼 u010609597 的回复:

我认真学习过volatile关键字的使用。
“volatile表示这个变量会被意想不到的改变”这句话真心是句废话,真心迷惑新手,这句话的本意是说:那些可能会被其他线程改变的变量应该使用volatile关键字修饰。
下面的话很经典,很容易理解,理解了你就知道volatile关键字的使用了。
volatile关键字用来通知编译器此变量可能会被其他线程修改,每次对变量的访问都必须从内存重新读取。编译器不要对此变量进行优化。

//注意:请使用C++编译器,而不是C语言编译器!
#include <stdio.h>
int main()
{
    const int m = 10;
    int *p = (int*)&m;
    printf("%d\n",m);       //m等于几?
    *p = 12;
    printf("%d\n",m);       //m等于几?
    return 0;
}


C编译器下结果是 10 12
C++编译器下是 10 10 !!!

把m的类型定义为olatile的话,两者的结构就是一致的了。。

C编译器下结果是 10 12,这是为啥?是因为强制类型转换吗?那c++怎么没有强制类型转换?

#15


引用 12 楼 zhao4zhong1 的回复:
建议帖主学学啥叫“中断”。

楼主学过中断的说,下面那一串课程除了编译原理只听过头几次课...,我只是像2楼的童鞋那样对这个“意想不到”的说法不满,如果意想不到对于再牛的人都是意想不到,没法预料,这不是一件很令人沮丧的事么?如果是无法预料什么时候出错但能预料到会在什么情况下出错,那也不能算是“意想不到”。就像我虽然不知道什么时候下雨,但随身带把伞就行了,不能说“意想不到”啊

#16


意想不到是针对编译器和CPU而言,并非针对使用编译器和CPU的人而言。

#17


引用 5 楼 shaode01 的回复:
额,你这里是const,只读噻,都是10吧

那那你再看看以下这段C++程序的运行结果吧:
#include <stdio.h>
int main()
{
    volatile const int m = 10;
    int *p = (int*)&m;
    printf("%d\n",m);       //m等于几?
    *p = 12;
    printf("%d\n",m);       //m等于几?
    return 0;
}

想一想,这段代码的执行结果会是多少吧,与上面的代码进行对比,认真想想,你就彻底知道volatile到底是干啥的啦。

#18


引用 2 楼 u010609597 的回复:
我认真学习过volatile关键字的使用。
“volatile表示这个变量会被意想不到的改变”这句话真心是句废话,真心迷惑新手,这句话的本意是说:那些可能会被其他线程改变的变量应该使用volatile关键字修饰。
下面的话很经典,很容易理解,理解了你就知道volatile关键字的使用了。
volatile关键字用来通知编译器此变量可能会被其他线程修改,每次对变量的访问都必须从内存重新读取。编译器不要对此变量进行优化。

//注意:请使用C++编译器,而不是C语言编译器!
#include <stdio.h>
int main()
{
    const int m = 10;
    int *p = (int*)&m;
    printf("%d\n",m);       //m等于几?
    *p = 12;
    printf("%d\n",m);       //m等于几?
    return 0;
}

这程序有两个问题:
1、const简单类型常量,编译器实现方式有两种。。
一是把它和变量一起分配内存,只是检查对其的直接赋值,这时如此糊弄编译器,改它的值没有问题。
二是把它放在只读数据段,这时此程序会出现运行时错误。可以预见,在某些编译器上可能会出现运行时错误。
2、如果没有运行时错误,输出结果是什么?其实也是不确定的,这和编译器具体采用的优化策略有关啊。。语言规范不可能去规定优化策略的。。m是一个只读变量,编译器可以优化么?可能有的会,有的不会,甚至一个编译器在不同优化选项下都可以有不一样的结果吧。。

#19


我觉得楼上说的在理

#20


引用 17 楼 u010609597 的回复:
Quote: 引用 5 楼 shaode01 的回复:

额,你这里是const,只读噻,都是10吧

那那你再看看以下这段C++程序的运行结果吧:
#include <stdio.h>
int main()
{
    volatile const int m = 10;
    int *p = (int*)&m;
    printf("%d\n",m);       //m等于几?
    *p = 12;
    printf("%d\n",m);       //m等于几?
    return 0;
}

想一想,这段代码的执行结果会是多少吧,与上面的代码进行对比,认真想想,你就彻底知道volatile到底是干啥的啦。


在VS2008中, 加不加volatile都是 10, 12
这也符合正常的理解.
虽然你对m加了const, 但是后面的(int*)&m强制转换已经破坏了const保护, 所以*p这里肯定会修改m的值.
这也就是指针的厉害之处.

只是在多线程编程中, 如果没有加 volatile 的变量 , 可能被编译器优化成一个寄存器变量, 耍不是从固定地址读取奇值, 这时, 如果有线程改变了地址里的值, 但寄存器变量中就没有被更新, 就会导致差异性.

#21


引用 3 楼 u010609597 的回复:
哈哈,貌似没几个人能猜对此程序运行的答案哦。

你还别臭美,这种修改常量值的方法是不安全的,虽然确实可以修改

#22


不要纠结各种常量了,这个世界上唯一不变的就是变化。用API WriteProcessMemory还能修改正运行的其它进程的内存里面的所谓常量呢!

不要迷信书、考题、老师、回帖;
要迷信CPU、编译器、调试器、运行结果。
并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。
任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实!

#23


volatile的意思是易变的,加这个关键词的作用是通知编译器甚至cache不要试图去做基于当前值不会改变的优化,每次访问的时候都需要去内存重新读取。这个volatile一般是给硬件的寄存器使用的,因为硬件寄存器的修改不是由cpu决定的,而是由硬件自己决定。

#24


引用 23 楼 HUANGFEIDIAN 的回复:
volatile的意思是易变的,加这个关键词的作用是通知编译器甚至cache不要试图去做基于当前值不会改变的优化,每次访问的时候都需要去内存重新读取。这个volatile一般是给硬件的寄存器使用的,因为硬件寄存器的修改不是由cpu决定的,而是由硬件自己决定。

那这个意想不到就是说cpu意想不到而不是我们意想不到咯?