Chinaunix首页 | 论坛 | 博客
  • 博客访问: 158157
  • 博文数量: 24
  • 博客积分: 3133
  • 博客等级: 中校
  • 技术积分: 206
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-24 11:08
文章分类

全部博文(24)

文章存档

2010年(4)

2009年(20)

分类: C/C++

2009-05-19 11:18:56

volatile修饰的确实是一个变量,而且是一个“容易变”的变量。在每次取这个变量值的时候,要求不是取它上次在某个时候取的临时缓存变量(比如说暂存在某个寄存器中),而是直接到内存中取。

volatile变量能防止优化,别如说你在某个地方可能连续调用了好几次这个函数,于是编译器优化后,可能就调用一次,其他几次就采用这一次调用的返回值,而volatile修饰后,要让每一次都进行函数调用,
而不采用暂存值。
 
volatile修饰不返回函数,比如函数体里面有exit或者死循环之类的东西。这样该函数被调用的时候不用把返回地址压入堆栈,(当然还可能有其他),代码得到优化。这个是 GCC 的扩展
 
volatile在嵌入式里使用较多。有时硬件动作会影响某存储空间的内容,对这些变量,必须加volatile修饰。
 
编译器的优化可以把本来在内存中进行访问的数据装入(缓存)到寄存器中以提高访问效率。然而这种优化在一些情况下可带来问题;此时,应该用 volatile 告诉编译器不要进行这种优化以避免问题的出现。
每次操作前从内存取值
 
有volatie修饰的变量,每次操作时遵循下面动作:
从内存取值 ---> 放入寄存器 ---> 操作 --->写回内存
没有volatie修饰的变量,操作可能遵循(可能就是不是所有情况都如此):
从内存取值 ---> 放入寄存器 ---> 第一次操作 ---> 第二次操作(此时仍操作寄存器中的值) …… --->第N次操作 --->写回内存
举个例子论述两者关系:
    int volatie i; //全局变量,在其它地方会被修改
    while (i){
        do_somethings();
    }
如果i没有被volatie修饰,当while循环执行时,另一段程序并发的执行了i=0,这个循环仍不会退出,因为每次循环都是检查寄存器中的值。
如果有volatie修饰,那么循环结束,因为循环每次检查i的时候,会先从内存把i读入寄存器,这个时候i在其它地方被赋0,则循环结束。
 
关于 volatile 我觉得这样的解析最容易理解:
如果编译器在代码中发现对同一地址的两次访问之间,没有对该地址进行写操作,那么编译器将优化为第一次寻址读该地址时取得的值作为第二次寻址的值,而并不是再做第二次物理上的 I/O 寻址操作。volatile 关键字指示编译器进行强制 I/O 寻址,因为编译器那样的优化,可能并不是我们真正期望的,譬如那个地址上连接着一个传感器上的寄存器,那么实际上,可能该寄存器的值是被传感器自身 不断刷新的。因此,我们必要要求CPU每次都进行 I/O 操作。
 
volatile 跟以前的 register 相反. register 告诉编译器尽量将变量放到寄存器中使用, 而volatile 强制将更改后的值写回内存(无论是cache还是内存). 如果不写回内存, 对于一些全局共享的变量, 可能导致不一致问题.
 
                               C语言中的volatile关键字 
   volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就
不再进行优化,从而可以提供对特殊地址的稳定访问。
 
    使用该关键字的例子如下:
 
    int volatile nVint;
   当要求使用volatile声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。
 
例如:
 
   volatile int i=10;int a = i;...//其他代码,并未明确告诉编译器,对i进行过操作int b = i;
  volatile指出i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在b中。而优化做法是,由于编译器发现两次从i读数据的
代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在b中。而不是重新从i里面读。这样以来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说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因为程序不应该试图去修改它。(也就是说,const指定了我们的程序代码中是不可以改变这个变量的,
但是volatile指出,可以是由于硬件的原因,在代码意外更改这个值,但是我们的代码同时会更新使用这个最新的数值)
    2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。(把指针声明为volatile的类型,可以保证指针所指向的地址随时发生变化)
    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;
     }
阅读(2205) | 评论(0) | 转发(0) |
0

上一篇:BitTorrent Specification

下一篇:网络协议

给主人留下些什么吧!~~