1. 程式人生 > >C++中memset()函式

C++中memset()函式

前段專案中發現一個問題,程式總是在某個dynamic_cast進行動態轉換時出異常,查了半天才發現問題原來是出在memset的使用上,雖然問題本身顯而易見,但當處於幾十萬行程式碼量級中時,就變得不太那麼容易定位了。

本文歸納了下使用memset幾個需要注意的地方,雖然內容很簡單,但也希望對大家有所幫助。
所在的標頭檔案在C++中:<cstring>,在C中<string.h>
函式原型為:
void * memset( void * ptr, int value, size_t num );

1.memset是以位元組為單位,初始化記憶體塊
當初始化一個位元組單位的陣列時,可以用memset把每個陣列單元初始化成任何你想要的值,比如,

char data[10];  
memset(data, 1, sizeof(data));    // right  
memset(data, 0, sizeof(data));    // right  

而在初始化其他基礎型別時,則需要注意,比如,

int data[10];  
memset(data, 0, sizeof(data));    // right  
memset(data, -1, sizeof(data));    // right  
memset(data, 1, sizeof(data));    // wrong, data[x] would be 0x0101 instead of 1  

memset進行非字元陣列賦值時一定要注意,因為mem是按位元組進行賦值的,而一個int為4位元組
2. 當結構體型別中包含指標時,在使用memset初始化時需要小心。
比如如下程式碼中,

struct Parameters {  
          int x;  
          int* p_x;  
};  
Parameters par;  
par.p_x = new int[10];  
memset(&par, 0, sizeof(par));  

當memset初始化時,並不會初始化p_x指向的int陣列單元的值,而會把已經分配過記憶體的p_x指標本身設定為0,造成記憶體洩漏。同理,對std::vector等資料型別,顯而易見也是不應該使用memset來初始化的。
3. 當結構體或類的本身或其基類中存在虛擬函式時,也需要謹慎使用memset。


這個問題就是在開頭專案中發現的問題,如下程式碼中,

class BaseParameters  
{  
public:  
    virtual void reset() {}  
};  
  
class MyParameters : public BaseParameters  
{  
public:   
    int data[3];  
    int buf[3];  
};  
  
MyParameters my_pars;  
memset(&my_pars, 0, sizeof(my_pars));  
BaseParameters* pars = &my_pars;  
  
//......  
  
MyParameters* my = dynamic_cast<MyParameters*>(pars);  

程式執行到dynamic_cast時發生異常。原因其實也很容易發現,我們的目的是為了初始化資料結構MyParameters裡的data和buf,正常來說需要初始化的記憶體空間是sizeof(int) * 3 * 2 = 24位元組,但是使用memset直接初始化MyParameters型別的資料結構時,sizeof(my_pars)卻是28位元組,因為為了實現多型機制,C++對有虛擬函式的物件會包含一個指向虛擬函式表(V-Table)的指標,當使用memset時,會把該虛擬函式表的指標也初始化為0,而dynamic_cast也使用RTTI技術,執行時會使用到V-Table,可此時由於與V-Table的連結已經被破壞,導致程式發生異常。

原文連結:老生常談,正確使用memset