在上一篇转载中学习了volatile关键字的用处:
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
关键在于两个地方:
1. 编译器的优化 (请高手帮我看看下面的理解)
在本次线程内, 当读取一个变量时,为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中;以后,再取变量值时,就直接从寄存器中取值;
当变量值在本线程里改变时,会同时把变量的新值copy到该寄存器中,以便保持一致
2. 多线程的使用
当变量在因别的线程等而改变了值,该寄存器的值不会相应改变,从而造成应用程序读取的值和实际的变量值不一致
当该寄存器在因别的线程等而改变了值,原变量的值不会改变,从而造成应用程序读取的值和实际的变量值不一致
下面是我在利用上篇中的小例子进行测试(以下测试均在visual studio 2008环境下)
1. 在没有使用volatile关键字
int _tmain(int argc, _TCHAR* argv[])
{
//下面i变量没有使用volatile
int i = 10;
int a = i;
printf("i = %d\n", a);
//下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道
__asm
{
mov dword ptr [ebp-4], 20h
}
int b = i;
printf("i = %d\n",b);
return 0;
}
输出结果:
在debug和release下 结果都是 (与上篇文章有出入啊)
i = 10
i = 10
(不会发图片请原谅)
2 使用volatile关键字
int _tmain(int argc, _TCHAR* argv[]) {
volatile int i = 10;int a = i;printf("i = %d\n", a);//下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道 __asm { mov dword ptr [ebp-4], 20h }int b = i; printf("i = %d\n",b);return 0;}//下面i变量没有使用volatile
分析原因:输出结果: debug下输出 release下输出 i = 10 i = 32 i = 10 i = 10
通过对代码的反汇编后发现
1. 在没有使用关键字的时候,release版汇编如下
2. 在使用了关键字的debug下00401002 in al,dx 00401003 push esi int i = 10; int a = i; printf("i = %d\n", a); 00401004 mov esi,dword ptr [__imp__printf (4020A0h)] 0040100A push 0Ah 0040100C push offset string "i = %d\n" (4020F4h) 00401011 call esi 00401013 add esp,8 //下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道 __asm { mov dword ptr [ebp-4], 20h 00401016 mov dword ptr [ebp-4],20h } int b = i; printf("i = %d\n",b); 0040101D push 0Ah 0040101F push offset string "i = %d\n" (4020F4h) 00401024 call esi 00401026 add esp,8 return 0; 00401029 xor eax,eax 0040102B pop esi
int _tmain(int argc, _TCHAR* argv[]) { 004113A0 push ebp 004113A1 mov ebp,esp 004113A3 sub esp,0E4h 004113A9 push ebx 004113AA push esi 004113AB push edi 004113AC lea edi,[ebp-0E4h] 004113B2 mov ecx,39h 004113B7 mov eax,0CCCCCCCCh 004113BC rep stos dword ptr es:[edi] volatile int i = 10; 004113BE mov dword ptr [i],0Ah int a = i; 004113C5 mov eax,dword ptr [i] 004113C8 mov dword ptr [a],eax printf("i = %d\n", a); 004113CB mov esi,esp 004113CD mov eax,dword ptr [a] 004113D0 push eax 004113D1 push offset string "i = %d\n" (41573Ch) 004113D6 call dword ptr [__imp__printf (4182BCh)] 004113DC add esp,8 004113DF cmp esi,esp 004113E1 call @ILT+320(__RTC_CheckEsp) (411145h) //下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道 __asm { mov dword ptr [ebp-4], 20h 004113E6 mov dword ptr [ebp-4],20h } int b = i; 004113ED mov eax,dword ptr [i] 004113F0 mov dword ptr [b],eax printf("i = %d\n",b); 004113F3 mov esi,esp 004113F5 mov eax,dword ptr [b] 004113F8 push eax 004113F9 push offset string "i = %d\n" (41573Ch) 004113FE call dword ptr [__imp__printf (4182BCh)] 00411404 add esp,8 00411407 cmp esi,esp 00411409 call @ILT+320(__RTC_CheckEsp) (411145h) return 0; 0041140E xor eax,eax } 00411410 pop edi 00411411 pop esi 00411412 pop ebx 00411413 add esp,0E4h 00411419 cmp ebp,esp 0041141B call @ILT+320(__RTC_CheckEsp) (411145h) 00411420 mov esp,ebp 00411422 pop ebp 00411423 ret
3. 在使用了关键字release版下00401002 in al,dx 00401003 push ecx 00401004 push esi volatile int i = 10; int a = i; printf("i = %d\n", a); 00401005 mov esi,dword ptr [__imp__printf (4020A0h)] 0040100B mov dword ptr [i],0Ah 00401012 mov eax,dword ptr [i] 00401015 push eax 00401016 push offset string "i = %d\n" (4020F4h) 0040101B call esi 0040101D add esp,8 //下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道 __asm { mov dword ptr [ebp-4], 20h 00401020 mov dword ptr [i],20h } int b = i; 00401027 mov eax,dword ptr [i] printf("i = %d\n",b); 0040102A push eax 0040102B push offset string "i = %d\n" (4020F4h) 00401030 call esi 00401032 add esp,8 return 0; 00401035 xor eax,eax 00401037 pop esi } 00401038 mov esp,ebp 0040103A pop ebp 0040103B ret