常量,一般指在程序中出現的數字1,2,3,等,字符串「Hello World」,以及數組名稱等,他們都屬於常量。在程序中是不容許修改他們的值。程序員
虛假常量const挑戰真正常量define數組
下面一段程序:函數
代碼前面定義了:工具
#define ZS 2234;測試
程序調試,反彙編以下:spa
如上圖所示:命令行
首先:程序是將當即數8BAh(當即數能夠做爲彙編的操做數)直接存儲到const修飾的常量cNum中,其地址爲ebp-0Ch;指針
第一條cout指令輸出cNum的地址:ebp-0Ch,同時將其存入ecx並進棧,做爲cout函數調用的參數;調試
第二條cout指令輸出cNum的值:內存
能夠看到,程序中並無使用到cNum變量的地址[ebp-0Ch],而是經過直接將當即數:8BAh(=2234)直接壓棧,做爲cout函數的參數;
也就是說當程序中須要用到cNum的值得時候,系統已經用「真正的值」對其進行了替換。
綜上所述,能夠發現,所謂的const修飾的常量,其本質仍是變量,程序依然是在堆棧中進行空間分配。那麼程序又是經過什麼來阻止程序員修改const修飾的變量的呢?《C++反彙編與逆向分析技術揭祕》一書中曾提到,const關鍵字的做用域是在程序編譯過程當中,也就是說程序在編譯過程當中經過某種檢測可以阻止對const修飾的變量的修改。能夠嘗試對cNum變量進行修改,而後對文件進行編譯,發現編譯過程當中就會出現錯誤。那麼到底怎麼才能修改cNum內存中的數值呢?如今的限制條件只有const關鍵字一條,那麼咱們能夠將cNum的地址複製給一個沒有const修飾的指針,例如pNum,而後經過pNum來修改內存中的值。嘗試以下。
int *pNum=(int*)&cNum;
*pNum=1111;
cout<<(int*)pNum<<endl;
cout<<*pNum<<endl;
cout<<cNum<<endl;
能夠發現,輸出結果爲:
1111
2234
cNum所指向的內存空間中的數值已經修改,可是編譯過的程序中出現cNum的地方仍是維持原來的數值(2234)
(既然在出現cNum的地方,編譯器已經用已知的預設值對其進行了替換,那麼修改變量內存中的值起始對程序應該是沒有任何影響的。)
真正的常量:數字、字符、字符串
程序中的數字、字符都是直接做爲當即數參與彙編指令運算。即其存儲位置是代碼段。結果以下圖:(利用VS2008自帶的反彙編窗口,選中「顯示代碼字節」)
可見,十六進制的數:0x12345678直接被編譯成了彙編指令,存儲在代碼段:0x00411554h位置。
也可利用微軟Visual Studio提供的命令行編譯工具:dumpbin.exe(一般存在的路徑是:C:\Program Files\Microsoft Visual Studio 9.0\VC\bin\)
dumpbin /all XXX.exe,結果截圖以下:
下面看一下字符串的存儲:
(測試代碼同上)
如上圖所示:
第一條指令:
將「Hello World」傳遞給str字符串的時候,直接從文件的只讀區00417810h讀取,從反彙編代碼能夠看出,程序利用eax、ecx、edx三個寄存器將00417810h只讀區域的字符串常量「Hello World」拷貝到str所表明的內存堆棧空間ebp-38h中。
第二條指令:定義了一個字符串指針,能夠看到其彙編指令只有短短的一行,只是將字符串在只讀區域的地址00417880h傳遞給了指針所指向的堆棧空間。
繼續查看利用字符串名和字符串指針來輸出的代碼:
二者指令徹底相同,都是將所指向的字符串的地址傳遞給了I/O流函數cin。
一樣利用dumpbin.exe工具能夠看到只讀區域的原始代碼以下: