1. 程式人生 > >C語言再學習5-陣列與優化

C語言再學習5-陣列與優化

什麼是陣列?為什麼要用陣列?

通俗來講,在記憶體中一塊連續儲存資料的叫陣列,陣列的每個子元素的寬度都一樣,並且只能為通用的資料型別做單位(char,short,int等等) 讓我們先定義一個數組,然後賦值:

char arr1[2] = { 0 };
arr1[0] = 1;
arr1[1] = 2;
arr1[2] = 3;

讓我們先見識一下反彙編

	char arr1[3] = { 0 };
013D4DC2 33 C0                xor         eax,eax  
013D4DC4 66 89 45 F4          mov         word ptr [arr1],ax  
013D4DC8 88 45 F6             mov         byte ptr [ebp-0Ah],al  
	arr1[0] = 10;
013D4DCB B8 01 00 00 00       mov         eax,1  
013D4DD0 6B C8 00             imul        ecx,eax,0  
013D4DD3 C6 44 0D F4 0A       mov         byte ptr arr1[ecx],0Ah  
	arr1[1] = 20;
013D4DD8 B8 01 00 00 00       mov         eax,1  
013D4DDD C1 E0 00             shl         eax,0  
013D4DE0 C6 44 05 F4 14       mov         byte ptr arr1[eax],14h  
	arr1[2] = 30;
013D4DE5 B8 01 00 00 00       mov         eax,1  
013D4DEA D1 E0                shl         eax,1  
013D4DEC C6 44 05 F4 1E       mov         byte ptr arr1[eax],1Eh  

一個小小的陣列,為什麼變成了這麼多東西??(vs2017下) 先別慌,我們來一條一條的解析指令!

xor eax,eax             			 //清除eax暫存器的資料
	--eax-ax-al        0x00000000
mov word ptr [arr1],ax	    //根據陣列的寬度來講,我們要儲存兩個陣列,所以第一次先使用ax暫存器(0x0000)
	-- cc cc cc         	 ->      	    00 00 cc
mov byte ptr [ebp-0Ah],al   //ax暫存器+al(0x00),這樣我們就使用2+1的寬度填充了3 的寬度的0
	-- 00 00 cc        	 ->        	    00 00 00
	//第一次把cccccc變成0000cc,第二次把0000cc變成000000,完成填充0,初始化陣列
mov         eax,1
imul        ecx,eax,0
mov         byte ptr arr1[ecx],0Ah

//這個指令看起來有點複雜,繼續解析
//-把1放到eax暫存器裡,imul是什麼?我們先觀察一下

影響 OF、CF 標誌位 第一種指令格式:IMUL r/m ;單運算元; 如果引數是 r8/m8, 將把 AL 做乘數, 結果放在 AX; 如果引數是 r16/m16, 將把 AX 做乘數, 結果放在 EAX; 如果引數是 r32/m32, 將把 EAX 做乘數, 結果放在 EDX:EAX

看了半天也沒看懂,反正imul ecx,eax,0,乘來乘去最後乘了0,就一直是0

再觀察一下shl指令,邏輯左移指令,通過左移來計算偏移!
後面你就會知道為什麼需要通過imul和shl指令來計算eax或者ecx的值

但是這條指令為什麼看起來怪怪的?
mov         byte ptr arr1[ecx],0Ah          //ecx為0
不要緊,這是編譯器的鍋,反彙編的顯示就是這樣,讓我們換一個軟體來顯示這些opcode
C6 44 0D   F4    0A             mov byte ptr ss:[ebp+ecx-C],0a           //ebp+0-c,其實就是ebp-c的地方存了第一個
C6 44 05    F4   14              mov byte ptr ss:[ebp+ecx-C],14		//ebp+1-c
讓我來展示一張堆疊結構圖

在這裡插入圖片描述 比如我們的ebp是401020的時候,第一行運算元組的彙編程式碼的意思就是:

mov byte ptr ss:[ebp+ecx-C],0a 
401020的ebp+0-c。其實就是401014。在401014的地方儲存10
401020的ebp+1-c。其實就是401015。在401015的地方儲存20
...

在這裡插入圖片描述 我們的資料成功被寫入堆疊!

傳統的陣列通過ss:[ebp-8], ss:[ebp-c]

通過對堆疊進行操作並且儲存陣列,而在vs2017裡,使用ebp+準確偏移來儲存陣列,保證每個記憶體單元都不會白費

在這裡插入圖片描述

再看看全域性陣列



	arr1[0] = 10;
00204DB8 B8 01 00 00 00       mov         eax,1  
00204DBD 6B C8 00             imul        ecx,eax,0  
00204DC0 C6 81 48 A1 20 00 0A mov         byte ptr arr1 (020A148h)[ecx],0Ah  
	arr1[1] = 20;
00204DC7 B8 01 00 00 00       mov         eax,1  
00204DCC C1 E0 00             shl         eax,0  
00204DCF C6 80 48 A1 20 00 14 mov         byte ptr arr1 (020A148h)[eax],14h  
	arr1[2] = 30;
00204DD6 B8 01 00 00 00       mov         eax,1  
00204DDB D1 E0                shl         eax,1  
00204DDD C6 80 48 A1 20 00 1E mov         byte ptr arr1 (020A148h)[eax],1Eh  

我們的陣列被儲存在基址0x20A148h的位置,再看到imul和shl指令的時候,我們已經恍然大悟 mov word ptr ds:[0x20a148+eax],14//eax=0 mov word ptr ds:[0x20a148+eax],14//eax=1 和我們的堆疊操作何其相似?

最後對arr[10]等大的陣列進行拆分發現:

ebp+eax(0)-14 ebp+eax(1)-14 ebp+eax(2)-14 ebp+eax(3)-14
ebp+eax(0)-c ebp+eax(1)-10 ebp+eax(2)-10 ebp+eax(3)-10
ebp+eax(0)-c ebp+eax(1)-c ebp+eax(2)-c ebp+eax(3)-c

在這裡插入圖片描述

結論:通過精確到位的操作,合理的分配堆疊空間來使用陣列,達到優化的目的!