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。
<< 回到主頁