[心得] 在 struct 中的 vector 存取異常

很久沒有寫心得了,這個問題花了我整整兩天的時間才解決,所以值得分享一下。首先我們看以下的定義

typedef struct _Profile
{
 INT nFormat;
 BOOL bFanMode;
 ULONG ulFanSpeed;
#ifdef BOOST
 BOOL bBoost;
#endif
 INT nOCMode;
 vector<INT> OffsetVector;
} Profile, *PProfile;

應該沒甚麼問題,除了條件式的組譯:如果有定義 BOOST 的話,就多一個 bBoost 的欄位

在我的 Solution 中有 ProjectA 和 ProjectB,其中 ProjectA 會產生 Profile 的物件,並以 Call by address 的方式傳給 Project B 中做後續的資料處理與回傳。

而問題出在 ProjectB 中只要對 OffsetVector 做寫入就會產生記憶體存取異常。我找過幾個方向:
  1. 因為 vector 會動態配置記憶體來存取資料,所以初期一直朝這個方向找答案,是否在傳遞的過程中,vector 所指到的記憶體已經改變了?所以才造成異常。但相同的動作在 ProjectA 做卻完全沒問題
  2. struct alignment 的問題,是否兩個 Project 的配置不一樣,造成相同的欄位卻存取到不同的位址?因此我用 sizeof() 取得兩個這數所佔空間的大小,還真的不一樣,而且 OffsetVector 是指向 struct 中不同的 offset。但我查 Project alignment 的設定,一樣都是預設值
這就怪了,最後發現原來問題是我自己造成的。原來我在 ProjectA 有定義 BOOST,因此有 bBoost 這個欄位,而 ProjectB 是事後才加入的,並沒有定義 BOOST,因此兩邊在存取 OffsetVector 時,就差了一個 BOOL 的欄位空間,故 ProjectB 在存取的位址實際上是錯的,當然就產生記憶體存取異常了,還害我誤會了 vector

所以結論是:
  1. 以後不要在 struct 中設定條件式組譯,避免又發生同樣的問題。
  2. 而在程式碼中的條件式組譯,請定義在 Project Property | C/C++ | Preprocessor 的 Preprocessor Definitions 中,否則寫在程式碼中,有可能造成部份程式碼因為沒有 include 到定義的檔案而不正確



2016/8/4 續集

下列這段程式碼會產生 Memory leak

 PProfile pProfile = new Profile;
 memset(pProfile, 0, sizeof(Profile));
 delete pProfile;

為甚麼呢?不過是利用 memset 初始化利用 new 建立的 struct,應該也沒問題才對啊!

原來還是 struct 中的 vector 出問題了,當你利用 new 建立 struct 時,裡面的 vector 實際上也會配置自己的 memory 空間,以便存放後續加入 vector 中的元素。當你用 memset 把所有 struct 中的元素清為 0 時,等於沒有把 vector 配置的 memory 釋放掉,就直接把 vector 清為 0。所以當程式結束時,就產生 Memory leak 了

因此,下次 struct 中有 STL 的容器時,千萬不要再用 memset 初始化了

留言

這個網誌中的熱門文章

Linux 批次檔的寫法

【分享】如何顯示 Debug Message

SketchUp 如何列印 1:1 圖檔