volitile变量的使用

volatile 影响编译器编译的结果,指出 volatile 变量每次使用时都需要去内存里重新读取它的值,而一旦修改此变量就直接写入内存,而不是操作cache中的变量。 volatile 变量有关的运算,不要进行特殊优化。

{

int I = 10;

int j = I;

int k=I;

}

对于上面的代码段,优化的作法是,编译器发现两次从 i 读数据的代码之间的代码没有对 i 进行过操作,它会从 cache 中把上次读的数据 i 放在 k 中。而不是重 新从 i 的内存里面读。这样以来,如果 i 是一个寄存器变量或者表示一个端口数据就容易出错,所以说 volatile 可以保证对特殊地址的稳定访问,不会出 错。

我们来看另一个例子

int volatile I;

while(i)

{

    do_something();

}

如果 i 没有被 volatie 修饰,当 while 循环执行时,另一段程序并发的执行了 i=0 ,这个循环仍不会退出,因为每次循环都是检查寄存器中的值。 如果有 volatie 修饰,那么循环结束,因为循环每次检查 i 的时候,会先从内存把 i 读入寄存器,这个时候 i 在其它地方被赋 0 ,则循环结束。    

对于程序中存在多个执行流程访问同一全局变量的情况, volatile 限定符是必要的。

此外,虽然程序只有单一的执行流程,但是变量属于以下情况之一的,也需要 volatile 限定。

1 变量的内存单元中的数据不需要写操作就可以自己发生变化,每次读上来的值都可能不一样

2 即使多次向变量的内存单元中写数据,只写不读,也并不是在做无用功,而是有特殊意义的

什么样的内存单元会具有这样的特性呢?肯定不是普通的内存,而是映射到内存地址空间的硬件寄存器,例如串口的接收寄存器属于上述第一种情况,而发送寄存器属于上述第二种情况。      

因此建议使用 volatile 变量的场所:

1 并行设备的硬件寄存器

一个中断服务子程序中会访问到的非自动变量(全局变量)

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

4 longjmp跳回之后需要保持修改的变量

 

上面的代码分别在非优化和优化条件下将得到不同的结果。

在嵌入式或多线程的环境下,不懂得 voliatile 可能会导致灾难。我们通过下面几个问题来加深对其理解。

关键字 volatile 有什么含意 ? 并给出三个不同的例子
         1) 一个参数既可以是 const 还可以是 volatile 吗?解释为什么。

是的。一个例子是只读的状态寄存器。它是 volatile 因为它可能被意想不到地改变。它是 const 因为程序不应该试图去修改它。(也就是说, const 指定了我们的程序代码中是不可以改变这个变量的, 但是 volatile 指出,可以是由于硬件的原因,在代码意外更改这个值,但是我们的代码同时会更新使用这个最新的数值)

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

是的。一个例子是当一个中服务子程序修该一个指向一个 buffer 的指针时。(把指针声明为 volatile 的类型,可以保证指针所指向的地址随时发生变化)

3) 下面的函数有什么错误:

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

这段代码的目的是用来返指针 *ptr 指向值的平方,但是,由于 *ptr 指向一个 volatile 型参数,编译器将产生类似下面的代码:
         int square(volatile int *ptr){
              int a,b;
              a = *ptr;
              b = *ptr;
             return a * b;

}

由于 *ptr 的值可能被意想不到地该变,因此 a b 可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
         int square(volatile int *ptr){
                 int a;
                 a = *ptr;
                return a * a;
         }

signal.h 头文件中声明了一种 sig_atomic_t 的类型,当把变量声明为该类型是,则会保证该变量在使用或赋值时, 无论是在 32 位还是 64 位的机器上都能保证操作是原子的, 它会根据机器的类型自动适应。 通常情况下, int 类型的变量通常是原子访问的,在 linux 里这样定义: typedef int __sig_atomic_t; 另外 gnu c 的文档也说比 int 短的类型通常也是具有原子性的,例如 short 类型。同时,指针(地址)类型也一定是原子性的。在 32 位的平台下,访问 64 位的变量,就不是原子的,而需要两次读内存操作。 sig_atomic_t 类型的变量应该总是加上 volatile 限定符 ,因为要使用 sig_atomic_t 类型的理由也正是要加 volatile 限定符的理由。  

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值