c语言中volatile作用,C语言中volatile
日期:2018.12.11为什么用volatile?
C/C中的volatile关键字对应的是const,用于修改变量,通常用于建立语言级的内存屏障。这是BS在《C编程语言》中对volatile修饰符的解释:
可变说明符是对编译器的一个提示,即对象可能会以语言未指定的方式更改其值,因此必须避免激进的优化。
volatile关键字是一个类型修饰符,它声明的类型变量可以被编译器的一些未知因素改变,比如操作系统、硬件或者其他线程。当遇到该关键字声明的变量时,编译器将不再优化访问该变量的代码,从而提供对特殊地址的稳定访问。语句语法:int volatile vInt当需要volatile声明的变量值时,系统总是从数据所在的内存中重新读取数据,即使前一条指令刚刚从那里读取了数据。并且读取的数据被立即保存。例如:
1.//.
2.volatile int i=10
3.int a=I;
4.//.
5.//其他没有明确告诉编译器的代码,都在I上操作过。
6.int b=I;Volatile指出I可以随时更改,每次使用都必须从I的地址读取, 所以编译器生成的汇编代码会从I的地址重新读取数据放入B中,优化的方法是编译器会自动将最后读取的数据放入B中,因为它发现两次从I读取数据的代码之间的代码没有对I进行操作,而不是从I重新读取,这样如果I是寄存器变量或者代表一个端口数据,就很容易出错,所以volatile可以保证对特殊地址的稳定访问。
写下面的程序,插入汇编代码,测试volatile关键字对程序最终代码的影响。
输入以下代码:
1.#包含stdio.h
2.
3.void main()
4.{
5.int i=10
6.
7.int a=I;
8.
9.printf(i=%d\n ,a);
10.
1.//下面汇编语句的作用是改变内存中I的值。
12.//但是不要让编译器知道
13.__asm
14.{
15.MOV德沃德PTR[EBP - 4],20H
16.}
17.
18.int b=I;
19.printf(i=%d\n ,b);
20.}发现上述程序在调试和发布模式下的输出为:
这个程序在Debug和Release模式下的输出是一致的,说明默认情况下VC 10.0在两种模式下都做了代码优化,造成了编译无法识别的变量变化。
输入以下代码:
1.#包含stdio.h
2.
3.void main()
4.{
5.volatile int i=10
6.
7.int a=I;
8.
9.printf(i=%d\n ,a);
10.
1.//下面汇编语句的作用是改变内存中I的值。
12.//但是不要让编译器知道
13.__asm
14.{
15.MOV德沃德PTR[EBP - 4],20H
16.}
17.
18.int b=I;
19.printf(i=%d\n ,b);
20.}发现上述程序在调试模式下的输出也是:
但是发布模式下的输出是:
上述程序在不同模式下的输出表明,volatile关键字只在发布模式下发挥作用,在调试模式下并没有达到预期的效果。也说明了在使用volatile修饰符修改变量时,需要注意程序在调试模式和发布模式下的不同行为。
其实不仅仅是“嵌入汇编操纵栈”是编译无法识别的变量变化。更何况,当多个线程并发访问共享变量时,一个线程改变变量的值,如何让改变后的值对其他线程可见。一般来说,volatile用在以下地方:
在中断程序中为被其他程序检测而修改的变量需要是易变的;在多任务环境下,易变;应添加到任务间共享的徽标中;内存的映射硬件寄存器通常用volatile来解释,因为每次读写都可能有不同的含义。易变指针
类似于修饰符const,const有一个指向常量的指针和一个指针常量。volatile也有相应的概念:修改指针指向的对象和数据是const还是volatile:
1.const char * cpch
2.挥发性char * vpch注意:对于VC来说,这个功能只有在VC 8以后才是安全的。*指针本身的值是——一个代表地址的整数变量,可以是const或volatile:
1.char * pchc
2.char * volatconstile pchv注意:
(1)可以把非易失性int赋给易失性int,但是不能把非易失性对象赋给易失性对象。
(2)除了基本类型之外,用户定义的类型也可以用可变类型来修饰。
(3)C中带有volatile标识符的类只能访问其接口的子集,这个子集由类的实现者控制。用户只能使用const_cast来获得对类型接口的完全访问权限。此外,volatile和const一样,从类传递给它的成员。
多线程下的Volatile
有些变量用volatile关键字声明。当两个线程都需要使用一个变量,并且该变量的值会改变时,应该用volatile声明。该关键字的作用是防止优化编译器将变量从内存加载到CPU寄存器中。如果将变量加载到寄存器中,就有可能两个线程同时使用内存中的变量和寄存器中的变量,从而导致程序的错误执行。Volatile是指每次编译器操作变量时,都必须从内存中取出,而不是使用寄存器中已经存在的值,如下:1。volatile BOOL bStop=FALSE(1)在一个线程中:
1.而(!bStop ) {.}
2.bStop=FALSE
3.返回;(2)在另一个线程中,终止上面的线程循环:
1.bStop=TRUE
2.while(bStop);//等待上述线程终止。如果bStop不使用volatile声明,那么这个循环将是一个无限循环,因为bStop已经被读入寄存器,寄存器中bStop的值永远不会变成FALSE。有了volatile,程序执行的时候,每次都会从内存中读取bStop的值,所以不会是死循环。
该关键字用于设置对象在内存中的存储位置,而不是在寄存器中。因为通用对象编译器可能会将它们的副本放在寄存器中以加速指令的执行,比如下面的代码:
1.
2.int nmy counter=0;
3.for(;nMyCounter 100nMyCounter)
4.{
5.
6.}
七.在这段代码中,nMyCounter的副本可能存储在一个寄存器中(在循环中,总是对这个寄存器中的值进行nMyCounter的测试和操作),但是另一段代码已经执行了这个操作:nmy counter-=1;在这个操作中,nMyCounter的变化是内存中nMyCounter的操作,所以有这样一个现象:nMyCounter的变化是不同步的。
,
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。