作個小實驗。
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的值已被改變,最後卻還印出原來的值的原因。
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的值已被改變,最後卻還印出原來的值的原因。
非常有趣的一個實驗
回覆刪除這個例子正好可以用來驗証 c++ 'volatile' 這個關鍵字的效果
將 main 函式中的變數 a 宣告改為
volatile const int a
然後將 func_a 的第一個參數宣告也加上 volatile
之後再編譯運行程式就會有不一樣的結果
我原以為 volatile 的用途大部分是用來配合 multithread 實作,看了你的例子之後才發現原來自己的理解有誤,單一 thread 也會有需要使用的時機
volatile其中一個用途是對變數修飾,避免編譯器最它最佳化後造成問題,舉個簡單例子。
回覆刪除int a;
a=1;
a=1;
a=1;
a=2;
...
編譯後,前面三條a=1都會因為最佳化而消失。解決辨法就是加上volatile來修飾變數a。