C++11新特性中的匿名函數Lambda表達式的彙編實現分析(二)

C++11新特性中的匿名函數Lambda表達式的彙編實現分析(一)閉包

首先,讓咱們來看看以&方式進行變量捕獲,一樣沒有參數和返回。
函數

int main()
{
	int a = 0xB;
	auto lambda = [&]{
		a = 0xA;
	};
	lambda();
	return 0;
}

閉包中將main中a變量改寫爲0xA。this

main中的關鍵彙編代碼:spa

int a = 0xB;
 mov         dword ptr [ebp-8],0Bh  
	auto lambda = [&]{
		a = 0xA;
	};
 lea         eax,[ebp-8]  
 push        eax  
 lea         ecx,[ebp-14h]  
 call        002D1BE0  
	lambda();
 lea         ecx,[ebp-14h]  
 call        002D1C20  
	return 0;

一樣的,進入閉包前要調用一個拷貝函數。.net

002D1BE0 內:code

 pop         ecx  
 mov         dword ptr [ebp-8],ecx  
 mov         eax,dword ptr [ebp-8]  
 mov         ecx,dword ptr [ebp+8]  
 mov         dword ptr [eax],ecx  
 mov         eax,dword ptr [ebp-8]  
 pop         edi  
 pop         esi  
 pop         ebx  
 mov         esp,ebp  
 pop         ebp  
 ret         4

和前面一篇文章中的代碼基本一致,可是有兩個地方不一樣,對象

上文寫到:blog

 pop         ecx  內存

 mov         dword ptr [ebp-8],ecx  get

 mov         eax,dword ptr [ebp-8]  

 mov         ecx,dword ptr [ebp+8]  

 mov         edx,dword ptr [ecx]  

 mov         dword ptr [eax],edx  

 mov         eax,dword ptr [ebp-8]  

注意黑體部分,若採用[=]的捕獲方式,那麼將經過寄存器edx拷貝原變量的值;

若採用[&]方式,則直接經過ecx拷貝原變量的地址,而不取出值。

閉包內:

 pop         ecx  
 mov         dword ptr [ebp-8],ecx  
		a = 0xA;
 mov         eax,dword ptr [ebp-8]  
 mov         ecx,dword ptr [eax]  
 mov         dword ptr [ecx],0Ah  
	};
 pop         edi  
 pop         esi  
 pop         ebx  
 mov         esp,ebp  
 pop         ebp  
 ret

對a進行賦值,直接是:

*this = 0xA;

由於事先this就取到了a的地址。

能夠發現,按引用捕獲實際上就如同函數參數傳遞引用,傳遞的是一個地址值,而不建立對象副本(能夠和上一篇文中的內容比較)。

C++11標準中對Lambda表達式的捕獲內容還有一些特定支持,好比能夠以指定的方式捕獲指定的變量:

int main()
{
	int a = 0xB;
	bool b = true;
	auto lambda = [&a,b]{
		a = b;
	};
	lambda();
	return 0;
}

上面的代碼對a進行引用捕獲,對b按值捕獲。根據前面分析的結果,能夠預見,a的地址和b的值將被拷貝以供閉包函數使用。

int a = 0xB;
 mov         dword ptr [ebp-8],0Bh  
	bool b = true;
 mov         byte ptr [ebp-11h],1  
	auto lambda = [&a,b]{
		a = b;
	};
 lea         eax,[ebp-11h]  
 push        eax  
 lea         ecx,[ebp-8]  
 push        ecx  
 lea         ecx,[ebp-24h]  
 call        00222060  
	lambda();
 lea         ecx,[ebp-24h]  
	lambda();
 call        00221C20  
	return 0;

調用Lambda以前,先調用複製函數,傳入兩個參數,&a和&b,而this被放在main的[ebp-24h]中。

複製函數或者叫準備函數:

 pop         ecx  
 mov         dword ptr [ebp-8],ecx  
 mov         eax,dword ptr [ebp-8]  
 mov         ecx,dword ptr [ebp+8]  
 mov         dword ptr [eax],ecx  
 mov         eax,dword ptr [ebp-8]  
 mov         ecx,dword ptr [ebp+0Ch]  
 mov         dl,byte ptr [ecx]  
 mov         byte ptr [eax+4],dl  
 mov         eax,dword ptr [ebp-8]  
 pop         edi  
 pop         esi  
 pop         ebx  
 mov         esp,ebp  
 pop         ebp  
 ret         8

[eax] 就是 *this,也即a

[eax+4] 就是 *(this+4),也即b

從內存圖能夠清楚的看到,a的地址被記錄,b的值被複制

閉包函數內:

 pop         ecx  
 mov         dword ptr [ebp-8],ecx  
		a = b;
 mov         eax,dword ptr [ebp-8]  
 movzx       ecx,byte ptr [eax+4]  
 
 mov         edx,dword ptr [ebp-8]  
 mov         eax,dword ptr [edx]  
 mov         dword ptr [eax],ecx

b的值是從[eax+4]也即this+4中取出,而a在this中,其實就是:

*(this) = *(this+4);

能夠看到閉包內經過this做爲基址,對閉包外的變量進行偏移訪問。


下一篇中,我將對具備參數和返回值的Lambda表達式進行分析。

相關文章
相關標籤/搜索