C++ 底層實作
今天心血來潮(沒錯,高微暫時擱在一旁 XD),寫了個簡單的函式讓 g++ 編譯但不組譯。函式如下:
int f(int& r) { return r; }
g++ 編譯的結果是:
.file "test_r.cpp" .text .align 2 .globl __Z1fRi .def __Z1fRi; .scl 2; .type 32; .endef __Z1fRi: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax movl (%eax), %eax popl %ebp ret
唔,馬上看到和 masm 不同的幾個地方(後來查到這是 AT&T syntax):register 的名字前面全部冠以 '%',push、mov 等指令後面全部多了 'l'(這代表操作為 32-bit),[ebp + 8]
變成 8(%ebp)
,最有趣的當然是雙引數指令的 destination 和 source 順序 ─ 剛好和 masm 相反 XD。也看到《Inside the C++ Object Model》所講的 function name mangling(__Z1fRi
)。翻譯成 masm 並加上註釋:
__Z1fRi: ; C++ function name mangling ; next 2 lines build up the stack frame push ebp mov ebp, esp mov eax, [ebp + 8] ; copy the first parameter into eax mov eax, [eax] ; dereference eax and store the result in eax ; eax is the return value ; clear stack frame ; arguments are cleared by the caller pop ebp ret
然後我讓 f 改而接收 int*
參數,編譯出來完全一樣(除了 mangled name),可見 reference 底層實作與 pointer 相同。在這個層次就可直觀看出,傳遞基本型別參數時,以 by value 方式傳遞會比 by reference 方式還快,因為前者少了 deference 的動作。
--
哇,高微!XD
又做一個實驗,看看 pass struct by value 時是否真的是 by value:
struct S { int a[5]; }; int f(S s) { return s.a[2]; }
g++ 給我的是:
__Z1f1S: pushl %ebp movl %esp, %ebp movl 16(%ebp), %eax popl %ebp ret
顯然整個 array 就在 stack 上,確確實實是 pass by value。
繼續做實驗,看看 return struct by value 的狀況:
struct S { int i, j, k; S(int a, int b, int c): i(a), j(b), k(c) { } }; S f() { return S(1, 2, 3); }
g++ 開 O3 最佳化後的結果如下:
__Z1fv: pushl %ebp movl %esp, %ebp movl 8(%ebp), %eax ; "first parameter" as a pointer movl $1, (%eax) movl $2, 4(%eax) movl $3, 8(%eax) popl %ebp ret $4 ; "first parameter" cleared by the callee
當 S 內含一個 int 時,return value 放在 eax;當 S 內含兩個 int,return value 放在 eax 和 edx;當 S 內含三個 int(即上例),事情就生變了 ─《Inside the C++ Object Model》p.63 所講的手法活生生在眼前上演!g++ 將 S f()
改為 void f(S&)
,直接把初值填在第一參數所指向的空間!哇,太好玩了 XD!
--
好了,可以睡了 XD。
debug 12人次達成
@@~
看不懂編譯訊息
「讓 g++ 編譯但不組譯」所以結果是組合語言 XD。
<< 回到主頁