volatile
可能很多人都没用过C/C++中的这个关键词,甚至不知道它的存在,本人以前也只是有所耳闻,但似懂非懂。
这是一个类型修饰符,位置同const、static等。一个使用volatile修饰的变量,比如volatile int i; 每次对该变量的直接引用,都会访问内存,而不是从寄存器中读取(如果其已经在寄存器中)。这样一来,volatile似乎没什么用处,反倒会使数据的读取相对变慢很多。但是,如果没有volatile,编译器可能会优化你的程序,使得数据从寄存器中读取,从而加快程序的运行,但如果这个变量是同其它进程/线程共享的,就可能造成数据的不一致。多线程情况下,你可以使用互斥机制来保证对共享数据访问的原子性。但是,在单片机等嵌入式环境中,硬件经常不会有这种互斥机制的支持,这时某些共享的数据(比如端口)就可能会产生不一致的情况。而使用volatile就会使编译器不对代码进行优化,每次对该变量的访问都会从内存中读取。
下面通过观察使用volatile前后编译产生的汇编代码的不同,来加深对volatile关键词的理解。
对于下面的C代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <stdio.h> int foo(void) { int i = 0; while(i++ < 20); return i; } int main(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 |
产生的汇编代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | [ ... ] .LC0: .string "%d, %d, %d\n" [ ... ] main: [ ... ] call foo # 调用函数foo,返回值保存至寄存器eax movl %eax, 16(%esp) # 参数k入栈 movl %eax, 12(%esp) # 参数j入栈 movl %eax, 8(%esp) # 参数i入栈 movl $.LC0, 4(%esp) # 格式字符串地址入栈 movl $1, (%esp) call __printf_chk movl $0, %eax leave ret [ ... ] |
可以看到,对j,k的赋值都是由寄存器eax进行的。下面把代码中的int i = foo();换成volatile int i = foo();,编译得到的汇编代码是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | [ ... ] main: [ ... ] call foo # 调用函数foo,返回值保存至寄存器eax movl %eax, 44(%esp) # 为i赋值 movl 44(%esp), %edx # 读取i值 movl 44(%esp), %ecx # 读取i值 movl 44(%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_chk movl $0, %eax leave ret [ ... ] |
可以看到,每次对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是一定会占用内存的。这一点,亦可以通过观察相应的汇编代码来验证。
你好!除了代码,此处没有多少原创之物,皆为本人搜集、整理、总结之记录与心得,欢迎转载分享!转载时请尽量注明出处,将不胜感激。祝你健康、快乐!
看你的博客很久了 。呵呵
一个小错误,foo()返回值应该为21,i++ >= 20时跳出循环,i应该为21。。。