天天看點

關于常量傳播(如何修改const常量的值)

今天在回顧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

繼續閱讀