管理好你C++項目中的結構對齊

最近把一個VC6.0的項目升級到VC2008,主要是有點厭倦了VC6那冷冰冰的古老面孔,想換點新鮮感。順便說一下,項目中使用了Notes API C庫來實現郵件的訪問。重新編譯

並不像想象中的那麼困難重重,無非就是修正了一些字符串轉換方面的warning和error。編譯完成後,隨手打開CppUnit準備測試,砰,一個錯誤對話框跳出:

Run-Time Check Failure #2 - Stack around the variable 'xxx' was corrupted.

錯誤是由一個字符串變量引起的,該變量是一個自定義類CNString的實例。按照MSDN上的說法,這個對象引起了堆棧泄露。

爲了找到問題的原因,把CNString做了一個精簡,只保留構造和析構函數:

重新做了一個很簡單的測試:

  

運行,錯誤依舊。關掉了VC2008的RTC檢查,錯誤消失。不能輕易放棄,一定有問題。一般的堆棧內存泄露錯誤都是因爲數組越界訪問。可是,我的代碼就是一個簡單的new和delete操作。可是問題到底在哪呢?

在google了搜尋了好久,在CSDN的博客上找到free2o一篇關於security cookie的文章:

http://blog.csdn.net/free2o/archive/2008/06/20/2570168.aspx,其中關於_RTC_CheckStackVars的函數部分引起了我的注意。經過對彙編代碼的反覆推敲和琢磨,我發現了問題的真正原因。

首先,編譯器在函數調用前分配一個0E4h大小的堆棧空間,並使用0CCCCCCCCh填充堆棧:

  

在函數完成後,編譯器檢查未使用的堆棧部分是否被破壞:

其中,472B00h內存空間存放的是_RTC_framedesc結構內容,其中保存了堆棧中變量的地址、大小、名稱等信息:

01 00 00 00 08 2b 47 00 e0 ff ff ff 0a 00 00 00 14 2b 47 00 73

注意:上面的01表示變量的數量,0e表示的是變量的大小。發現問題了嗎?我的類定義中只有char*, WORD, wchar_t*三個成員變量,按照默認的/ZP8對齊方式,應該大小是0c(12)纔對,怎麼是0a(10)了?這樣,當運行時檢查str變量後面的堆棧空間時,內存尋址出現錯誤,把正常的對象空間當成了未使用空間來檢查,因此,砰!你中獎了。

有了這樣的認識,我立即開始檢查頭文件中關於結構對齊的設置。終於,在notes c API的global.h中,發現瞭如下行:

#pragma pack(1)

記得之前在notes c API的幫助中看到關於Data Structure Packing的部分,還頗不以爲然,這回徹底頓悟了。

On machines that use the Intel architecture, data structures passed through the Lotus C API for Domino and Notes must have the structure members packed on 1-byte boundaries. This saves space in data structures stored on disk, and preserves binary compatibility with previous releases of Domino or Notes. 

因爲,我在項目中並沒有使用需要傳遞結構的Lotus API函數,因此,在測試類頭文件中加上:

#pragma pack()

問題立即解決。

接着運行另一個測試,發現XML又無法加載,總是提示“無法識別的XML頭”。XML的數據是通過一個結構指針傳入的:

檢查config裏的數據,都是正確的,到底是怎麼回事?難道還是對齊問題?

通過在創建結構數據的cpp和加載xml的cpp中加入以下指令:

#pragma pack(show)

 

編譯後發現,兩個cpp的結構對齊方式一個是1,一個是8。原來,前者包含了global.h頭文件,後者沒有包含。果真是結構對齊的問題,解決辦法是在結構定義前後定義pack指令:

 

問題解決。

 

 

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章