volatile

来源:互联网 发布:如何评价武林外传知乎 编辑:程序博客网 时间:2024/04/29 22:49

volatile

  可能很多人都没用过C/C++中的这个关键词,甚至不知道它的存在,本人以前也只是有所耳闻,但似懂非懂。
  这是一个类型修饰符,位置同const、static等。一个使用volatile修饰的变量,比如volatile int i; 每次对该变量的直接引用,都会访问内存,而不是从寄存器中读取(如果其已经在寄存器中)。这样一来,volatile似乎没什么用处,反倒会使数据的读取相对变慢很多。但是,如果没有volatile,编译器可能会优化你的程序,使得数据从寄存器中读取,从而加快程序的运行,但如果这个变量是同其它进程/线程共享的,就可能造成数据的不一致。多线程情况下,你可以使用互斥机制来保证对共享数据访问的原子性。但是,在单片机等嵌入式环境中,硬件经常不会有这种互斥机制的支持,这时某些共享的数据(比如端口)就可能会产生不一致的情况。而使用volatile就会使编译器不对代码进行优化,每次对该变量的访问都会从内存中读取。
  下面通过观察使用volatile前后编译产生的汇编代码的不同,来加深对volatile关键词的理解。
  对于下面的C代码:

1234567891011121314151617
#include <stdio.h> int foo(void){    int i = 0;    while(i++ < 20);    return i;}intmain(int argc, char **argv){    int i = foo();    int j = i;    int k = i;    printf("%d, %d, %d/n", i, j, k);    return 0;}

  代码中,使用foo()的返回值(20)来初始化整型变量i。之所以不直接赋值,是为了防止编译器优化,下面对j,k的赋值将直接使用20,而不是i,也不是寄存器。使用下面的命令编译:

1
$ gcc -S -O1 main.c # 优化等级1

  产生的汇编代码:

1234567891011121314151617
[ ... ].LC0:.string"%d, %d, %d/n"[ ... ]main:[ ... ]callfoo # 调用函数foo,返回值保存至寄存器eaxmovl%eax, 16(%esp) # 参数k入栈movl%eax, 12(%esp) # 参数j入栈movl%eax, 8(%esp) # 参数i入栈movl$.LC0, 4(%esp) # 格式字符串地址入栈movl$1, (%esp)call__printf_chkmovl$0, %eaxleaveret[ ... ]

  可以看到,对j,k的赋值都是由寄存器eax进行的。下面把代码中的int i = foo();换成volatile int i = foo();,编译得到的汇编代码是:

1234567891011121314151617
[ ... ]main:[ ... ]callfoo # 调用函数foo,返回值保存至寄存器eaxmovl%eax, 44(%esp) # 为i赋值movl44(%esp), %edx # 读取i值movl44(%esp), %ecx # 读取i值movl44(%esp), %eax # 读取i值movl%ecx, 16(%esp) # 参数k入栈,使用i值movl%edx, 12(%esp) # 参数j入栈,使用i值movl%eax, 8(%esp) # 参数i入栈movl$.LC0, 4(%esp) # 格式字符串地址入栈call__printf_chkmovl$0, %eaxleaveret[ ... ]

  可以看到,每次对i的访问都是从内存(栈)中读取的。

volatile vs. const

  正像上面所描述的那样,volatile的意思是“易变的”。C/C++中还有另外一个关键词叫const,用来限定变量不可被改变。似乎volatile和const意思相反,水火不容。但volatile const int N = 0;这样的语句是允许存在的。为什么呢?其实,这个语句可以这样来理解:变量N在本程序(或者设备)中是不可(禁止)改变的,但它可能会被其它程序(或者设备)改变。你或许知道,const只是编译器的限定,而没有执行期的检查,因此const也只是限于单个进程,因此volatile和const这两个关键词并不冲突。
  另外,普通的整型const变量,比如const int N = 10; int a[N]; 中,变量N可能并不会被分配内存(除非程序里面有显式使用N地址的表达式)。但是,根据volatile变量的特性,volatile const int N = 10;中变量N是一定会占用内存的。这一点,亦可以通过观察相应的汇编代码来验证。

原创粉丝点击