C++经验之volatile关键字

版权声明:本文为博主原创文章,转载时请标明来源。 https://blog.csdn.net/aiwangtingyun/article/details/86592485

先搞清楚概念:volatile关键字中文翻译为易变的,那易变就易变呗有什么卵用!确实,在一般的程序里面根本用不着这东西,除非你的多线程任务使用了共享的变量,如果这时候不使用volatile关键字,你的程序等着修bug吧!好吧,说了这么多,现在让我们来看看volatile这家伙干什么用的?

简单的说,volatile关键字就是防止编译器对代码进行优化(我没猜错的话,你根本不知道编译器会对你的代码进行优化,或者编译器会对你的代码做什么样的鬼优化),来,我们看看下面的例子:

int i = 1;		// 在内存中定义变量
int a = i;		// 从i的内存中读取值
int b = i;		// 从寄存器中读取i的值

首先,第一条语句我们在内存中定义了变量i;接着,第二条语句编译器从内存中读取了i的值,并把它赋值给变量a(这里需要解释一下i是如何赋值给a的:CPU从i的内存地址中读取其值并把它压入寄存器中,然后CPU再从内存中找到a的内存地址并写入i的值);最后,第三条语句还是读取i的值并把它赋值给b,由于两次读取i的值之间没有对i进行修改,所以编译器对其进行优化:把上次读取在寄存器中i的值赋值给b,而不是重新从i的内存中读取。假如使用volatile修饰变量i的话,那么上面的优化编译器都不会做,编译器保证每次读取i都是从其内存中读取出来。

编译器优化最经典的例子就是for循环的变量:for (int i = 0; i < N; i++) 由于i变量频繁被使用,编译器为了提高效率会把它存到某个寄存器中。


等等,目前为止好像也没什么问题呀,但前面说了,在多线程中,假如有多个线程同时对同一个变量进行读写会发生什么?考虑下面的例子:

// 定义一个bool变量
bool bStop = false;

// 假如第一个线程中使用该变量
{
	while (!bStop) { ... }
	bStop = false;		
	return;
}

// 第二个线程中也使用该变量
{
	bStop = true;
	while (bStop) ;		// 等待第一个线程结束
}

思考一下第二个线程,由于bStop被置为了真,while循环里读取bStop时每次读取的都是寄存器里面的true值,结果造成死循环,哪怕第一个线程结束时bStop被置为了假也没用,因为编译器根本不会再次从bStop的内存中读取其值。要想解决这个问题就需要使用volatile关键字修饰:volatile bool bStop; 这样 while(bStop)中每次调用bStop的值时编译器都会从bStop的内存中读取它的值。

总结一下就是在多线程情况下,如果每个线程对某个变量都需要访问,为了确保每次访问都是该变量的最新值,这时就需要使用volatile来修饰了。


这里需要提一下,volatile关键字和const关键字是等价的,const有常量指针和指针常量的概念,volatile也用相应的概念:

const int *cpi;			// 指针指向的对象是const
volatile int *vpi;		// 指针指向的对象是volatile

int * const *pci;		// 指针变量本身是const
int * volatile *pvi;	// 指针变量本身是volatile

对了,可以把一个非volatile int 赋给volatile int,但是不能把非volatile对象赋给一个volatile对象,也就是非volatile赋给volatile只对内置类型生效。


那么问题来了,可以使用const、volatile同时修饰一个变量吗?

答案是:可以。const修饰符只是限定在程序内不能被修改,但是可以被程序外的东西修改,比如外部端口值(不要问我什么是外部端口值,我也不知道,就算知道也不告诉你@0@),而外部端口值恰巧是需要使用volatile来修饰的。

猜你喜欢

转载自blog.csdn.net/aiwangtingyun/article/details/86592485