首页 > 代码库 > volatile用法

volatile用法

volatile表示编译器不要优化代码 一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:1) 并行设备的硬件寄存器(如:状态寄存器)2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)3) 多线程应用中被几个任务共享的变量回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道,所有这些都要求用到volatile变量。不懂得volatile的内容将会带来灾难。假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。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 int *ptr){int a;a = *ptr;return a * a;}CONST一.一般应用1.const修饰各种变量的用法.   a.取代define     #define D_INT 100     #define D_LONG 100.29     ………     const int D_INT = 100;     const D_INT = 100;     //如果定义的int类型,可省略int.     const long D_LONG = 100.29;     ………     const int& a = 100;      const替代define虽然增加分配空间,可它却保证了类型安全.在C标准中,const定义的数据相当于全局的,而C++中视声明的位置而定.   b.修饰指针相关的变量以三组简单的定义示意:     Group1:        int a = 0;         const int* b = &a;------------  [1]                     int const *b = &a;------------  [2]                          const int* const b = &a;---- [4]        Group2:       const char *p = "const";--------------[1]      char const *p = "const";--------------[2]        char* const p = "const";--------------[3]        const char * const p = "const";----[4]           Group3:      int a=0;        const int &b = a;---------------[1]     int const &b = a;---------------[2]       int & const b = a;--------------[3]  //--->修饰引用时,const被忽略     const int & const b = a;-----[4]总结:     1.如果const位于星号左侧,则const用来修饰指针所指向的变量,即指针指向的为不可变的.     2.如果const位于星号右侧,const就是修饰指针本身,即指针本身是不可变的.因此,[1]和[2]的情况相同,指针所指向内容不可变(const放在变量声明符的位置无关),这种情况下不允许对内容进行更改,如不能*a = 3 ;     3.[3]中指针本身是不可变的,而指针所指向的内容是可变的,这种情况下不能对指针本身进行更改操作,如a++是错误的     4.[4]中指针本身和指向的内容均为常量.(引用特殊:引用在使用增加遇义时,增加它代表的变量.所以qualifiers on reference are ignoredv.延伸点: 注意示例:       1.const int& reference = 1000;        2.char* p = "const"         char*& q ;2.const在函数环境下的各种应用常用法示例如下:   const A&  _Fun(const  A& _in);  //修饰引用型传入参数   // A  _Fun(const A& _in);   //A& _Fun(const A& _in);   //上面的两种,在函数内部有特殊的步骤,这里不详提了…..   const  A*  _Fun( const  A* _in);   //修饰指针型传入参数   void _Fun( ) const;   //修饰class成员函数   const  A&  _Fun(A& _in );  //修饰返回值   const A & operator(const A& _in);  //同时修饰传入参数和返回值   a.修饰参数如void _Fun(const A* _in)或 void _Fun(const A& _in);它们被修饰后,在函数执行期间行为特性同于上面的讲解,注意:这不会改变原来数据的是否是const的属性.   b.修饰函数返回值    const A&  _Fun( )    const A*   _Fun( );注意:由于生命期不同步的问题,不可将局部的变量的指针或引用返回(static除外).另外,传出来的视情况,代表不同的意思…对于A&返回类型,你若将之赋与其它变量,那么它实际执行的是将返回的变量    (或引用)代表的数据赋出..而你若将其它值赋予之,那么被赋予的是变量或引用代表的数据. 而const A& 一般是防止之做为左值被赋值.这个地方还有很多的细节问题(譬如在连续赋值、返回的临时对象的处理、重载的const和非cosnt运算符等等),读者自己在实践中需要多多总结.使用可变(mutable)成员隐藏实现细节作者: Builder.com键字 mutable 是一个奇怪的修饰符(specifier),它只能够用于一个类的非静态数据成员。下面我将讨论 mutable 的语义和用法,但是首先我要解释一下 C++ 对象模型的一个关键概念。键字 mutable 是一个奇怪的修饰符(specifier),它只能够用于一个类的非静态数据成员。下面我将讨论 mutable 的语义和用法,但是首先我要解释一下 C++ 对象模型的一个关键概念。对象的状态一个对象的状态由其非静态数据成员的值构成,因此,修改一个数据成员将会改变整个对象的状态。将一个成员函数声明为 const 能够保证它不会改变对象的状态。然而在一些情况下,对象的逻辑状态与基物理状态之间可能有差别。例如,对于一个表示绘画图像的对象就存在这种情况。如果图像还没有更改,那么我们就认为其状态没有发生变化。然而,从底层实现方面来说,如果大对象在一段时间没有活动,那么它们的内存通常会被交换到一个文件中。交换一个图像并不会真地影响其状态,但是对象的一些数据成员可能会发生变化,在这里可能会发生变化的是指针、标志位等。在用户调用一个诸如 Redraw() 之类的 const 成员函数时,他们并不关心这个函数在内部是如何实现的。从他们的角度来说,这个函数并不改变对象的逻辑状态,因此被声明为 const。Redraw() 有可能修改对象的物理状态这一事实是一个他们不应该关心的实现细节。例如:int Image::Redraw() const{ if (isLoaded==false) {  //..read image data from a disk into a local buffer  isLoaded=true; //changing a data member‘s value }  //..paint image in the screen}class Image可变(mutable)数据成员如果尝试编译这段代码,你会得到一个编译错误。虽然 Redraw() 声明为 const,但是它修改了一个数据成员。解决这个编译错误的方法是将 isLoaded 声明为一个 mutable 数据成员:class Image{public: int Redraw() const; //..private: mutable bool isLoaded;//can be changed by a const function};不像普通的数据成员,const 成员函数可以修改 mutable 数据成员。Mutable 数据成员的使用看上去像是骗术,因为它能够使 const 函数修改对象的数据成员。然而,明智地使用 mutable 关键字可以提高代码质量,因为它能够让你向用户隐藏实现细节,而无须使用不确定的东西,比如 const_cast<>。对象的状态一个对象的状态由其非静态数据成员的值构成,因此,修改一个数据成员将会改变整个对象的状态。将一个成员函数声明为 const 能够保证它不会改变对象的状态。然而在一些情况下,对象的逻辑状态与基物理状态之间可能有差别。例如,对于一个表示绘画图像的对象就存在这种情况。如果图像还没有更改,那么我们就认为其状态没有发生变化。然而,从底层实现方面来说,如果大对象在一段时间没有活动,那么它们的内存通常会被交换到一个文件中。交换一个图像并不会真地影响其状态,但是对象的一些数据成员可能会发生变化,在这里可能会发生变化的是指针、标志位等。在用户调用一个诸如 Redraw() 之类的 const 成员函数时,他们并不关心这个函数在内部是如何实现的。从他们的角度来说,这个函数并不改变对象的逻辑状态,因此被声明为 const。Redraw() 有可能修改对象的物理状态这一事实是一个他们不应该关心的实现细节。例如:int Image::Redraw() const{ if (isLoaded==false) {  //..read image data from a disk into a local buffer  isLoaded=true; //changing a data member‘s value }  //..paint image in the screen}class Image可变(mutable)数据成员如果尝试编译这段代码,你会得到一个编译错误。虽然 Redraw() 声明为 const,但是它修改了一个数据成员。解决这个编译错误的方法是将 isLoaded 声明为一个 mutable 数据成员:class Image{public: int Redraw() const; //..private: mutable bool isLoaded;//can be changed by a const function};不像普通的数据成员,const 成员函数可以修改 mutable 数据成员。Mutable 数据成员的使用看上去像是骗术,因为它能够使 const 函数修改对象的数据成员。然而,明智地使用 mutable 关键字可以提高代码质量,因为它能够让你向用户隐藏实现细节,而无须使用不确定的东西,比如 const_cast<>。volatile关键字  volatile是c/c++中一个鲜为人知的关键字,该关键字告诉编译器不要持有变量的临时拷贝,它可以适用于基础类型  如:int,char,long......也适用于C的结构和C++的类。当对结构或者类对象使用volatile修饰的时候,结构或者  类的所有成员都会被视为volatile.  使用volatile并不会否定对CRITICAL_SECTION,Mutex,Event等同步对象的需要  例如:  int i;  i = i + 3;  无论如何,总是会有一小段时间,i会被放在一个寄存器中,因为算术运算只能在寄存器中进行。一般来说,volatitle  关键字适用于行与行之间,而不是放在行内。  我们先来实现一个简单的函数,来观察一下由编译器产生出来的汇编代码中的不足之处,并观察volatile关键字如何修正  这个不足之处。在这个函数体内存在一个busy loop(所谓busy loop也叫做busy waits,是一种高度浪费CPU时间的循环方法) oid getKey(char* pch)  {  while (*pch == 0)  ;  }  当你在VC开发环境中将最优化选项都关闭之后,编译这个程序,将获得以下结果(汇编代码)  ;       while (*pch == 0)  $L27  ; Load the address stored in pch  mov eax, DWORD PTR _pch$[ebp]  ; Load the character into the EAX register  movsx eax, BYTE PTR [eax]  ; Compare the value to zero  test eax, eax  ; If not zero, exit loop  jne $L28  ;  jmp $L27  $L28  ;}  这段没有优化的代码不断的载入适当的地址,载入地址中的内容,测试结果。效率相当的低,但是结果非常准确  现在我们再来看看将编译器的所有最优化选项开关都打开以后,重新编译程序,生成的汇编代码,和上面的代码  比较一下有什么不同  ;{   ; Load the address stored in pch  mov eax, DWORD PTR _pch$[esp-4]  ; Load the character into the AL register  movsx al, BYTE PTR [eax]  ; while (*pch == 0)  ; Compare the value in the AL register to zero  test al, al  ; If still zero, try again  je SHORT $L84  ;  ;}使用可变(mutable)成员隐藏实现细节作者: Builder.com键字 mutable 是一个奇怪的修饰符(specifier),它只能够用于一个类的非静态数据成员。下面我将讨论 mutable 的语义和用法,但是首先我要解释一下 C++ 对象模型的一个关键概念。键字 mutable 是一个奇怪的修饰符(specifier),它只能够用于一个类的非静态数据成员。下面我将讨论 mutable 的语义和用法,但是首先我要解释一下 C++ 对象模型的一个关键概念。对象的状态一个对象的状态由其非静态数据成员的值构成,因此,修改一个数据成员将会改变整个对象的状态。将一个成员函数声明为 const 能够保证它不会改变对象的状态。然而在一些情况下,对象的逻辑状态与基物理状态之间可能有差别。例如,对于一个表示绘画图像的对象就存在这种情况。如果图像还没有更改,那么我们就认为其状态没有发生变化。然而,从底层实现方面来说,如果大对象在一段时间没有活动,那么它们的内存通常会被交换到一个文件中。交换一个图像并不会真地影响其状态,但是对象的一些数据成员可能会发生变化,在这里可能会发生变化的是指针、标志位等。在用户调用一个诸如 Redraw() 之类的 const 成员函数时,他们并不关心这个函数在内部是如何实现的。从他们的角度来说,这个函数并不改变对象的逻辑状态,因此被声明为 const。Redraw() 有可能修改对象的物理状态这一事实是一个他们不应该关心的实现细节。例如:int Image::Redraw() const{ if (isLoaded==false) {  //..read image data from a disk into a local buffer  isLoaded=true; //changing a data member‘s value }  //..paint image in the screen}class Image可变(mutable)数据成员如果尝试编译这段代码,你会得到一个编译错误。虽然 Redraw() 声明为 const,但是它修改了一个数据成员。解决这个编译错误的方法是将 isLoaded 声明为一个 mutable 数据成员:class Image{public: int Redraw() const; //..private: mutable bool isLoaded;//can be changed by a const function};不像普通的数据成员,const 成员函数可以修改 mutable 数据成员。Mutable 数据成员的使用看上去像是骗术,因为它能够使 const 函数修改对象的数据成员。然而,明智地使用 mutable 关键字可以提高代码质量,因为它能够让你向用户隐藏实现细节,而无须使用不确定的东西,比如 const_cast<>。对象的状态一个对象的状态由其非静态数据成员的值构成,因此,修改一个数据成员将会改变整个对象的状态。将一个成员函数声明为 const 能够保证它不会改变对象的状态。然而在一些情况下,对象的逻辑状态与基物理状态之间可能有差别。例如,对于一个表示绘画图像的对象就存在这种情况。如果图像还没有更改,那么我们就认为其状态没有发生变化。然而,从底层实现方面来说,如果大对象在一段时间没有活动,那么它们的内存通常会被交换到一个文件中。交换一个图像并不会真地影响其状态,但是对象的一些数据成员可能会发生变化,在这里可能会发生变化的是指针、标志位等。在用户调用一个诸如 Redraw() 之类的 const 成员函数时,他们并不关心这个函数在内部是如何实现的。从他们的角度来说,这个函数并不改变对象的逻辑状态,因此被声明为 const。Redraw() 有可能修改对象的物理状态这一事实是一个他们不应该关心的实现细节。例如:int Image::Redraw() const{ if (isLoaded==false) {  //..read image data from a disk into a local buffer  isLoaded=true; //changing a data member‘s value }  //..paint image in the screen}class Image可变(mutable)数据成员如果尝试编译这段代码,你会得到一个编译错误。虽然 Redraw() 声明为 const,但是它修改了一个数据成员。解决这个编译错误的方法是将 isLoaded 声明为一个 mutable 数据成员:class Image{public: int Redraw() const; //..private: mutable bool isLoaded;//can be changed by a const function};不像普通的数据成员,const 成员函数可以修改 mutable 数据成员。Mutable 数据成员的使用看上去像是骗术,因为它能够使 const 函数修改对象的数据成员。然而,明智地使用 mutable 关键字可以提高代码质量,因为它能够让你向用户隐藏实现细节,而无须使用不确定的东西,比如 const_cast<>。volatile关键字  volatile是c/c++中一个鲜为人知的关键字,该关键字告诉编译器不要持有变量的临时拷贝,它可以适用于基础类型  如:int,char,long......也适用于C的结构和C++的类。当对结构或者类对象使用volatile修饰的时候,结构或者  类的所有成员都会被视为volatile.  使用volatile并不会否定对CRITICAL_SECTION,Mutex,Event等同步对象的需要  例如:  int i;  i = i + 3;  无论如何,总是会有一小段时间,i会被放在一个寄存器中,因为算术运算只能在寄存器中进行。一般来说,volatitle  关键字适用于行与行之间,而不是放在行内。  我们先来实现一个简单的函数,来观察一下由编译器产生出来的汇编代码中的不足之处,并观察volatile关键字如何修正  这个不足之处。在这个函数体内存在一个busy loop(所谓busy loop也叫做busy waits,是一种高度浪费CPU时间的循环方法) oid getKey(char* pch)  {  while (*pch == 0)  ;  }  当你在VC开发环境中将最优化选项都关闭之后,编译这个程序,将获得以下结果(汇编代码)  ;       while (*pch == 0)  $L27  ; Load the address stored in pch  mov eax, DWORD PTR _pch$[ebp]  ; Load the character into the EAX register  movsx eax, BYTE PTR [eax]  ; Compare the value to zero  test eax, eax  ; If not zero, exit loop  jne $L28  ;  jmp $L27  $L28  ;}  这段没有优化的代码不断的载入适当的地址,载入地址中的内容,测试结果。效率相当的低,但是结果非常准确  现在我们再来看看将编译器的所有最优化选项开关都打开以后,重新编译程序,生成的汇编代码,和上面的代码  比较一下有什么不同  ;{   ; Load the address stored in pch  mov eax, DWORD PTR _pch$[esp-4]  ; Load the character into the AL register  movsx al, BYTE PTR [eax]  ; while (*pch == 0)  ; Compare the value in the AL register to zero  test al, al  ; If still zero, try again  je SHORT $L84  ;  ;}嵌入式编程中经常用到 volatile这个关键字,在网上查了下他的用法可以归结为以下两点:一:告诉compiler不能做任何优化   比如要往某一地址送两指令:     int   *ip   =...;   //设备地址     *ip   =   1;   //第一个指令     *ip   =   2;   //第二个指令     以上程序compiler可能做优化而成:     int   *ip   =   ...;     *ip   =   2;     结果第一个指令丢失。如果用volatile, compiler就不允许做任何的优化,从而保证程序的原意:     volatile   int   *ip   =   ...;     *ip   =   1;     *ip   =   2;     即使你要compiler做优化,它也不会把两次付值语句间化为一。它只能做其它的优化。这对device   driver程序员很有用。二:表示用volatile定义的变量会在程序外被改变,每次都必须从内存中读取,而不能把他放在cache或寄存器中重复使用。   如  volatile   char   a;          a=0;         while(!a){            //do   some   things;         }         doother();     如果没有   volatile   doother()不会被执行volatile 这个ANSI C 关键字在经典的C 教程中很少提及,高层编程的人也可能永远都不会用到,但是作为嵌入式开发者来说,这个关键字使用频率应该很高。volatile 的字面意思为“不稳定的,易变的”。一般用它定义一些IO 端口的变量。现在假定我们要对一个设备进行初始化,此设备的某一个寄存器地址为0xff800000。我们先看一段程序:int *output = (int *)0xff800000; /* 定义一个IO 端口*/int init(void){int i;for(i=0; i<10; i++){*output = i;}}一般的编译器都带有优化功能,那么这段代码被优化会是什么结果呢?编译器认为前面循环半天都是废话,对最后的结果毫无影响,因为最终只是将output 这个指针赋值为9,所以编译器最后给你编译的代码结果相当于为:int init(void){*output = 9;}试想一下,如果你对此外部设备进行初始化的过程是必须是像上面代码一样顺序的对其赋值,显然优化后的程序并不能达到目的。反之如果你不是对此端口反复写操作,而是反复读操作,其结果是一样的,编译器在优化后,也许你的代码对此地址的读操作了只做了一次。然而从代码角度看是没有任何问题的。这时候就是volatile 出场的时候了,volatile 就是通知编译器,这个声明的变量是一个不稳定的,在遇到此变量时候不要优化。对于上面的代码只需在声明时加上volatile 即可。volatile int *output = (volatile int *)0xff800000; /* 定义一个IO 端口*/volatile 影响编译器编译的结果,指出,volatile 变量是随时可能发生变化的,与volatile变量有关的运算,不要进行编译优化,以免出错,(VC++ 在产生release版可执行码时会进行编译优化,加volatile关键字的变量有关的运算,将不进行编译优化。)。 例如: volatile int i=10; int j = i; ... int k = i; volatile 告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的可执行码会重新从i的地址读取数据放在k中。 而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中。而不是重新从i里面读。这样以来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问,不会出错。/**********************一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: 1) 并行设备的硬件寄存器(如:状态寄存器) 2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 3) 多线程应用中被几个任务共享的变量 回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道,所有这些都要求用到volatile变量。不懂得volatile的内容将会带来灾难。假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。 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 int *ptr) { int a; a = *ptr; return a * a; } 位操作(Bit manipulation)关键字const有什么含意? 我只要一听到被面试者说:“const意味着常数”,我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着“只读”就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。) 如果应试者能正确回答这个问题,我将问他一个附加的问题: 下面的声明都是什么意思? const int a; int const a; const int *a; int * const a; int const * a const; /******/ 前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字 const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由: •; 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。) •; 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。 •; 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。 Volatile 8. 关键字volatile有什么含意?并给出三个不同的例子。 一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: •; 并行设备的硬件寄存器(如:状态寄存器) •; 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) •; 多线程应用中被几个任务共享的变量 回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道,所有这些都要求用到volatile变量。不懂得volatile的内容将会带来灾难。 假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。 •; 一个参数既可以是const还可以是volatile吗?解释为什么。 •; 一个指针可以是volatile 吗?解释为什么。 •; 下面的函数有什么错误: int square(volatile int *ptr) { return *ptr * *ptr; } 下面是答案: •; 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。 •; 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。 •; 这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码: int square(volatile int *ptr) { int a,b; a = *ptr; b = *ptr; return a * b; } 由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下: long square(volatile int *ptr) { int a; a = *ptr; return a * a; } volatile的本意是“易变的” 由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:static int i=0;int main(void){...while (1){if (i) dosomething();}}/* Interrupt service routine. */void ISR_2(void){i=1;}程序的本意是希望ISR_2中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被调用。如果将将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。一般说来,volatile用在如下的几个地方:1、中断服务程序中修改的供其它程序检测的变量需要加volatile;2、多任务环境下各任务间共享的标志应该加volatile;3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。上午在CSDN的C++版逛时,发现有人在问volatile关键字的用义和用法。于是乎便写了这篇BLOG。  如果你懂一点点的编译器的知识我想你都会知道编译器在编译你的代码的时候,用进行自动优化的,用以产生优化指令。同上操作系统和一些线程同样也会对你所定义的一些变量做出一些你所不知道的更改。这样的更改我们称为,隐式修改,因为你不知道,编译器在什么情况下,在那里做出了优化,甚至你都不知道,或是不能肯定编译器到底有没有对你的代码做出优化。  直接点把你看看下面的例子  #include <iostream>  void main()  {  int i=10;  int a = i;  printf("i= %d\n",a);  __asm {  mov dword ptr [ebp-4], 50h  }  //下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道,来隐式的修改了变量。  int b = i;  printf("i= %d\n",b);  }  然后,在调试版本(debug)模式运行程序,输出结果如下:  i = 10  i = 80  然后,在release版本模式运行程序,输出结果如下:  i = 10  i = 10  呵呵结果看到了吗?输出的结果明显表明,release模式下,编译器对代码进行了优化,第二次没有输出正确的i值。所以得出一个结论在VC中release模式编译代码时编译器会自动对你的代码来做起优化的。而调试版本(debug)模式下便不会。  废话说了好多啊呵呵下面继续说说 volatile  下面,我们把 i的声明加上volatile关键字,看看有什么效果:  #include <iostream>  void main()  {  volatile int i=10;  int a = i;  printf("i= %d\n",a);  __asm {   mov dword ptr [ebp-4], 50h  }  int b = i;  printf("i= %d\n",b);  }  这下你再在调试版本和release版本运行程序,看看输出结果是不是都是:  i = 10  i = 32  估计大家看到这里便会明白了,volatile这个关键字最最主要的意思是做什么的了。  在MSDN中volatile是一个限定符,也称为keyword或描述符,"volatile 关键字指示字段可由操作系统、硬件或并发执行的线程在程序中进行修改。"  当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。  一般说来,volatile用在如下的几个地方:   1、中断服务程序中修改的供其它程序检测的变量需要加volatile;   2、多任务环境下各任务间共享的标志应该加volatile;   3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;   声明方式为volatile declaration  备注  系统总是在 volatile 对象被请求的那一刻读取其当前值,即使上一条指令从同一对象请求值。而且,该对象的值在赋值时立即写入。  volatile 修饰符通常用于由多个线程访问而不使用 lock 语句来序列化访问的字段。使用 volatile 修饰符能够确保一个线程检索由另一线程写入的最新值。  备注部分由MSDN原文所说。  本文参考:关于volatile关键字的说明以及测试(作者:iwaswzq)有关volatile (函数前加volatile) 和inline的用法(作者:kobefly)一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:1). 并行设备的硬件寄存器(如:状态寄存器)2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)3). 多线程应用中被几个任务共享的变量回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。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 int *ptr){int a;a = *ptr;return a * a;}http://baike.baidu.com/view/608706.htm?fr=aladdin

 

volatile用法