2009年11月13日 星期五

GCC C++ Compiler 會聰明地幫你還原 const 變數的數值

關於前一篇文章 C++ Programming: Call by Reference/Value/Pointer and const_cast 所留下的問題
再寫一段簡單的程式碼來作實驗
/* main.cpp */
void funcA(const int &);
void funcB(int);
void funcC(const int *);

int main(int argc, char* argv[])
{
    const int value = 11;
    funcA(value);
    funcB(value);
    funcC(&value);
    funcB(value);
    return 0;
}

/* Call by Reference */
void funcA(const int& value)
{
    int& tmp = const_cast<int&>(value);
    tmp = 22;
}

/* Call by Value */
void funcB(int value)
{
    value = 33;
}

/* Call by Pointer */
void funcC(const int* value)
{
    int* tmp = const_cast<int*>(value);
    *tmp = 44;
}
這次利用 GCC 來產生 x86 的組合語言看看到底發生了什麼事情
$ g++ -S main.cpp
就會產生 main.s 檔案
來看看其中的片段
    call    _Z5funcARKi
    movl    $11, (%esp)
    call    _Z5funcBi
    leal    -8(%ebp), %eax
    movl    %eax, (%esp)
    call    _Z5funcCPKi
    movl    $11, (%esp)
    call    _Z5funcBi
答案是在呼叫完 funcA 跟 funcC 之後
GCC 聰明地將 value 的值給還原成 11 了
如果在 C++ 程式碼中第二次呼叫 funcB 給拿掉就不會看到第二個
    movl    $11, (%esp)
聰明的 GCC 只會在適當的時候幫你還原 const 變數的數值,造成 const 變數值從來沒被改變過的假象 :)

2 則留言:

不來的可勞得 提到...

真是太厲害了!
偉哉GCC

Hayashi 提到...

ㄛ, 我比較不相信 GCC 會幫你還原 const value 的作法. 只是 compiler 知道那是 const, 基於 optimization 的角度, 用 11 幫你帶入那些 function 的 caller stack. 否則它要做兩件事, mov -8(%ebp), %eax; mov %eax,(%esp);
所以會看到它做了這樣的事情, 這也就是為什麼有些書會建議你把都用到但不會去改變它的變數加上 const 可以換來一些 compiler optimization.
如果要說還原的話, 他應當 movl $11,-8(%ebp); 把 main stack 中的 local variable 給重設為 11. 但是因為 call by address 需要 address, 所以 GCC 不得以一定要生一個 local variable 才能有 address 給 funcC
因為 optimization, GCC 相信 const value 從來不會變, 所以你在 return 前寫個 cout << value * 5; 在 assembly 裡面看, 他其實就是直接拿 55 去代換.
又, 如果把 funcA 和 funcC 交錯寫, const value 變數的位置的那個數值是會變的.