C++ Const

作個小實驗。

void func_a(const int& a) {
  int& b = const_cast<int&>(a);
  b = 200;
  cout << a << ", " << &a << endl;
}

int main() {
  const int a = 100;
  cout << a << ", " << &a << endl;
  func_a(a);
  cout << a << ", " << &a << endl;
}

-------------------------------------------------
100, 0012FED4
200, 0012FED4
100, 0012FED4
-------------------------------------------------

可以看到,在func_a內,明明a的值已經被改成200了(可以由變數位址確認)。為了確認這一點,加上了第四個cout,如下。

cout << *(int*)&a << ", " << &a << endl;

-------------------------------------------------
100, 0012FED4
200, 0012FED4
100, 0012FED4
200, 0012FED4
-------------------------------------------------

可以看到第四個cout果然印出a的內容已改變為200。但是第三個cout卻是印出原來的值100,這是為什麼?答案在將程式反組譯,從組合語言的層面來看就很明顯了。

cout << a << ", " << &a << endl;
0041E6B2 push offset std::endl (41C528h)
0041E6B7 lea eax,[a]
0041E6BA push eax
0041E6BB push offset string ", " (4570C8h)
0041E6C0 push 64h
0041E6C2 mov ecx,offset std::cout (460888h)
0041E6C7 call std::basic_ostream<char,std::char_traits<char> >::operator<< (41C663h)
0041E6CC push eax
0041E6CD call std::operator<<<std::char_traits<char> > (41CB8Bh)
0041E6D2 add esp,8
0041E6D5 mov ecx,eax
0041E6D7 call std::basic_ostream
0041E6DC mov ecx,eax
0041E6DE call std::basic_ostream<char,std::char_traits<char> >::operator<< (41CBB8h)

注意紅色那行指令。編譯器直接把100(64h)這個常數值push進堆疊,而不再從常數變數a裡取出值來。這就是為什麼在func_a內,a的值已被改變,最後卻還印出原來的值的原因。

留言

  1. 非常有趣的一個實驗
    這個例子正好可以用來驗証 c++ 'volatile' 這個關鍵字的效果
    將 main 函式中的變數 a 宣告改為
    volatile const int a
    然後將 func_a 的第一個參數宣告也加上 volatile
    之後再編譯運行程式就會有不一樣的結果

    我原以為 volatile 的用途大部分是用來配合 multithread 實作,看了你的例子之後才發現原來自己的理解有誤,單一 thread 也會有需要使用的時機

    回覆刪除
  2. volatile其中一個用途是對變數修飾,避免編譯器最它最佳化後造成問題,舉個簡單例子。

    int a;
    a=1;
    a=1;
    a=1;
    a=2;
    ...

    編譯後,前面三條a=1都會因為最佳化而消失。解決辨法就是加上volatile來修飾變數a。

    回覆刪除

張貼留言

這個網誌中的熱門文章

以lex/yacc實作算式計算機

猜數字遊戲 (電腦猜人)

KillSudoku 4顆星精彩數獨詳解 - 鍊技巧