C++中volatile变量测试

volatile有好几个特性,让我们来验证一下

1.对声明为volatile的变量操作时,每次都会从内存中取值,而不会使用原先保存在寄存器中的值。

         让我们看一下两个例子,一个例子是对不声明为volatile的变量操作,一个例子是对声明为volatile的变量操作。编译器为gcc version 4.8.4,平台为32位ubuntu14.04,开启了一级优化,即g++ -O1 -std=c++11。汇编部分只看有注释的部分就行了。

//非volatile版


#include<stdio.h>      
#include<pthread.h>
#include<iostream>
int fun(int a){
	int x;
	scanf("%d",&x);
	if(a > x)
		return a;
	else 
		return x;
}
int main(){

	int a = 5;   //声明为非volatile
	int b = 10;
	int c = 20;
	int d;
	
	scanf("%d",&c);

	a = fun(c);

	b = a + 1;
	
	

	printf("%d,%d\n",b,d);
	return 0;;

}


080485fd <_Z3funi>:
 80485fd:	53                   	push   %ebx
 80485fe:	83 ec 28             	sub    $0x28,%esp
 8048601:	8b 5c 24 30          	mov    0x30(%esp),%ebx
 8048605:	8d 44 24 1c          	lea    0x1c(%esp),%eax
 8048609:	89 44 24 04          	mov    %eax,0x4(%esp)
 804860d:	c7 04 24 50 87 04 08 	movl   $0x8048750,(%esp)
 8048614:	e8 d7 fe ff ff       	call   80484f0 <scanf@plt>
 8048619:	8b 44 24 1c          	mov    0x1c(%esp),%eax
 804861d:	39 c3                	cmp    %eax,%ebx
 804861f:	0f 4d c3             	cmovge %ebx,%eax
 8048622:	83 c4 28             	add    $0x28,%esp
 8048625:	5b                   	pop    %ebx
 8048626:	c3                   	ret    

08048627 <main>:
 8048627:	55                   	push   %ebp
 8048628:	89 e5                	mov    %esp,%ebp
 804862a:	83 e4 f0             	and    $0xfffffff0,%esp
 804862d:	83 ec 20             	sub    $0x20,%esp
 8048630:	c7 44 24 1c 14 00 00 	movl   $0x14,0x1c(%esp)
 8048637:	00 
 8048638:	8d 44 24 1c          	lea    0x1c(%esp),%eax
 804863c:	89 44 24 04          	mov    %eax,0x4(%esp)
 8048640:	c7 04 24 50 87 04 08 	movl   $0x8048750,(%esp)
 8048647:	e8 a4 fe ff ff       	call   80484f0 <scanf@plt>
 804864c:	8b 44 24 1c          	mov    0x1c(%esp),%eax
 8048650:	89 04 24             	mov    %eax,(%esp)
 8048653:	e8 a5 ff ff ff       	call   80485fd <_Z3funi> //调用fun函数,返回值保存在eax中
 8048658:	c7 44 24 0c 00 00 00 	movl   $0x0,0xc(%esp)
 804865f:	00 
 8048660:	83 c0 01             	add    $0x1,%eax  //直接使用eax中的值(b = a + 1)
 8048663:	89 44 24 08          	mov    %eax,0x8(%esp)
 8048667:	c7 44 24 04 53 87 04 	movl   $0x8048753,0x4(%esp)
 804866e:	08 
 804866f:	c7 04 24 01 00 00 00 	movl   $0x1,(%esp)
 8048676:	e8 35 fe ff ff       	call   80484b0 <__printf_chk@plt>
 804867b:	b8 00 00 00 00       	mov    $0x0,%eax
 8048680:	c9                   	leave  
 8048681:	c3                   	ret    
//volatile版本

#include<stdio.h>
#include<pthread.h>
#include<iostream>
int fun(int a){
	int x;
	scanf("%d",&x);
	if(a > x)
		return a;
	else 
		return x;
}
int main(){

	volatile int a = 5;//声明为volatile 
	int b = 10;
	int c = 20;
	int d;
	
	scanf("%d",&c);

	a = fun(c);

	b = a + 1;
	
	

	printf("%d,%d\n",b,d);
	return 0;;

}



080485fd <_Z3funi>:
 80485fd:	53                   	push   %ebx
 80485fe:	83 ec 28             	sub    $0x28,%esp
 8048601:	8b 5c 24 30          	mov    0x30(%esp),%ebx
 8048605:	8d 44 24 1c          	lea    0x1c(%esp),%eax
 8048609:	89 44 24 04          	mov    %eax,0x4(%esp)
 804860d:	c7 04 24 60 87 04 08 	movl   $0x8048760,(%esp)
 8048614:	e8 d7 fe ff ff       	call   80484f0 <scanf@plt>
 8048619:	8b 44 24 1c          	mov    0x1c(%esp),%eax
 804861d:	39 c3                	cmp    %eax,%ebx
 804861f:	0f 4d c3             	cmovge %ebx,%eax
 8048622:	83 c4 28             	add    $0x28,%esp
 8048625:	5b                   	pop    %ebx
 8048626:	c3                   	ret    

08048627 <main>:
 8048627:	55                   	push   %ebp
 8048628:	89 e5                	mov    %esp,%ebp
 804862a:	83 e4 f0             	and    $0xfffffff0,%esp
 804862d:	83 ec 20             	sub    $0x20,%esp
 8048630:	c7 44 24 18 05 00 00 	movl   $0x5,0x18(%esp)
 8048637:	00 
 8048638:	c7 44 24 1c 14 00 00 	movl   $0x14,0x1c(%esp)
 804863f:	00 
 8048640:	8d 44 24 1c          	lea    0x1c(%esp),%eax
 8048644:	89 44 24 04          	mov    %eax,0x4(%esp)
 8048648:	c7 04 24 60 87 04 08 	movl   $0x8048760,(%esp)
 804864f:	e8 9c fe ff ff       	call   80484f0 <scanf@plt>
 8048654:	8b 44 24 1c          	mov    0x1c(%esp),%eax
 8048658:	89 04 24             	mov    %eax,(%esp)
 804865b:	e8 9d ff ff ff       	call   80485fd <_Z3funi>//调用fun函数
 8048660:	89 44 24 18          	mov    %eax,0x18(%esp) //把返回值放入a对应的内存中
 8048664:	8b 44 24 18          	mov    0x18(%esp),%eax //从a的内存中取值
 8048668:	c7 44 24 0c 00 00 00 	movl   $0x0,0xc(%esp)
 804866f:	00 
 8048670:	83 c0 01             	add    $0x1,%eax       //然后加1(b = a + 1)
 8048673:	89 44 24 08          	mov    %eax,0x8(%esp)  
 8048677:	c7 44 24 04 63 87 04 	movl   $0x8048763,0x4(%esp)
 804867e:	08 
 804867f:	c7 04 24 01 00 00 00 	movl   $0x1,(%esp)
 8048686:	e8 25 fe ff ff       	call   80484b0 <__printf_chk@plt>
 804868b:	b8 00 00 00 00       	mov    $0x0,%eax
 8048690:	c9                   	leave  
 8048691:	c3                   	ret    

 

2.编译器不会对声明为volatile的变量进行优化,比如说声明了一个变量但是没有使用或者是使用了但是程序不关心使用后的结果。如果是没有声明volatile的变量编译器会忽略掉这个变量,但是如果把这个变量声明为volatile,那么编译器就不可以忽略这个变量,看下面的例子。

#include<stdio.h>
#include<pthread.h>
#include<iostream>

int main(){

	int a = 5;
	int b = 10;
	int c = 20;
	int d;
	a = b + 1;
	
	return 0;;

}


生成的汇编代码中把所有的变量都忽略了

0804857d <main>:
 804857d:	b8 00 00 00 00       	mov    $0x0,%eax
 8048582:	c3                   	ret    

         接下来让我们把a声明为volatile变量再看看会发生什么事情

#include<stdio.h>
#include<pthread.h>
#include<iostream>

int main(){

	volatile int a = 5;
	int b = 10;
	int c = 20;
	int d;
	
	
	return 0;;

}


生成的汇编中并没有忽略变量a

0804857d <main>:
 804857d:	83 ec 10             	sub    $0x10,%esp
 8048580:	c7 44 24 0c 05 00 00 	movl   $0x5,0xc(%esp)// a = 5
 8048587:	00 
 8048588:	b8 00 00 00 00       	mov    $0x0,%eax
 804858d:	83 c4 10             	add    $0x10,%esp
 8048590:	c3                   	ret    

 

 

3.顺序性(和java中的顺序性不太一样)

这里用的g++的2级优化

int a;
int b;


int fun(){
	a = b + 1;
	b = 0;

	return 0;
}
	


080485c0 <_Z3funv>:
 80485c0:	a1 2c a0 04 08       	mov    0x804a02c,%eax //取b的值
 80485c5:	c7 05 2c a0 04 08 00 	movl   $0x0,0x804a02c // b = 0
 80485cc:	00 00 00 
 80485cf:	83 c0 01             	add    $0x1,%eax  // a = b + 1
 80485d2:	a3 30 a0 04 08       	mov    %eax,0x804a030
 80485d7:	31 c0                	xor    %eax,%eax
 80485d9:	c3                   	ret    

由上面的例子可见编译器会对原来的程序重排顺序,再让我们把a声明为volatile看还会发生重排吗

这里用的g++的2级优化
volatile int a;
int b;


int fun(){
	a = b + 1;
	b = 0;

	return 0;
}
	


080485c0 <_Z3funv>:
 80485c0:	a1 2c a0 04 08       	mov    0x804a02c,%eax //取b的值
 80485c5:	c7 05 2c a0 04 08 00 	movl   $0x0,0x804a02c //b = 0
 80485cc:	00 00 00 
 80485cf:	83 c0 01             	add    $0x1,%eax    // a = b + 1
 80485d2:	a3 30 a0 04 08       	mov    %eax,0x804a030
 80485d7:	31 c0                	xor    %eax,%eax
 80485d9:	c3                   	ret    

从上面的程序来看还是会发生指令重排,让我们再把两个变量都声明为volatile来看看。

这里用的g++的2级优化
volatile int a;
volatile int b;


int fun(){
	a = b + 1;
	b = 0;

	return 0;
}
	


080485c0 <_Z3funv>:
 80485c0:	a1 2c a0 04 08       	mov    0x804a02c,%eax //取b的值
 80485c5:	83 c0 01             	add    $0x1,%eax  // a = b+1
 80485c8:	a3 30 a0 04 08       	mov    %eax,0x804a030//把结果放入a中
 80485cd:	31 c0                	xor    %eax,%eax
 80485cf:	c7 05 2c a0 04 08 00 	movl   $0x0,0x804a02c //b = 0
 80485d6:	00 00 00 
 80485d9:	c3                   	ret    

从上面结果来看,如果两个都声明为volatile,那么编译器就不会对指令进行重排

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值