C語言再學習5-陣列與優化
阿新 • • 發佈:2018-12-11
什麼是陣列?為什麼要用陣列?
通俗來講,在記憶體中一塊連續儲存資料的叫陣列,陣列的每個子元素的寬度都一樣,並且只能為通用的資料型別做單位(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 |
…