今天在回顧C++的const_cast的時候無意寫了這樣一段代碼:
#include<iostream>
using namespace std;
int main(){
const int i=100;
int *p=const_cast<int *>(&i);
(*p)++;
cout<<p<<" "<<&i<<endl;
cout<<*p<<" "<<i<<endl;
}
運作結果如下:
0x22ff74 0x22ff74
101 100
結果分析如下:
1、首先給出const_cast的說明,《C++ primer》第五版中這樣寫道:“const_cast隻能改變運算對象的底層const。如果對象本身不是一個常量,使用強制類型轉換獲得寫權限是合法行為。然而如果對象本身是一個常量,再使用const_cast執行寫操作就會産生未定義的行為。”
但是,以上代碼中int型變量i本身是const的,利用const_cast消除了i的底層const,并讓非const指針p指向i,通過p對i進行寫操作。在code blocks和VS 2010下運作該程式,編譯通過,并且int型常量i的值均被修改。不知《C++ primer》上所說的“未定義的行為”該如何了解。
2、字面值常量100存放在常量存儲區中;int型常量i,因為是局部的,我認為應該是和普通的局部變量一樣存儲在棧中。
3、最後列印i的值仍是100。原因是常量傳播,又稱常量替換,在編譯階段,編譯器直接将程式中的i替換為10,是以列印i的結果為10。但i在棧上的值确實是被修改掉了!
*************************** add in 2014-12-21***************************
今天看《C++反彙編與逆向分析技術》2.6.2 #definde和const的差別。書上給出了和上面代碼類似的代碼,還真是很巧,也記不清大半年前怎麼會寫出這段代碼的。書中講到#define定義的常量是真正的常量(儲存在常量區),而const卻是由編譯器判斷實作的常量,是一個假常量。const常量本質上還是一個變量,隻不過C++中提出的const機制在編譯層面上對const常量提供了寫保護,是為了防止意外修改。
既然const常量是假常量,那就肯定是能修改的,修改方法見上面代碼(擷取指針,強制将指針的const修飾去掉,再通過指針修改之)。這種通過指針繞過const機制的代碼在平時的編碼中自然沒什麼意義,但在逆向工程中卻是有意義的。
*************************** add in 2014-12-25***************************
不要混淆常量傳播和常量折疊,兩個都是編譯器的優化手段(目的是在不影響程式正常邏輯的前提下,盡可能生成立即數尋址的目标代碼,減少記憶體的通路次數)。VS下開啟O2優化後,會進行常量傳播和折疊。詳見《C++反彙編與逆向分析》P49