簡體   English   中英

memset() 或值初始化以將結構歸零?

[英]memset() or value initialization to zero out a struct?

在 Win32 API 編程中,通常使用具有多個字段的 C struct 通常只有其中幾個具有有意義的值,而所有其他值都必須歸零。 這可以通過以下兩種方式之一實現:

STRUCT theStruct;
memset( &theStruct, 0, sizeof( STRUCT ) );

或者

STRUCT theStruct = {};

第二個變體看起來更簡潔——它是單行的,它沒有任何可能輸入錯誤並導致植入錯誤的參數。

與第一個變體相比,它有什么缺點嗎? 使用哪個變體,為什么?

這兩個構造的含義非常不同。 第一個使用memset函數,該函數旨在將內存緩沖區設置為某個值 第二個初始化一個對象 讓我用一些代碼解釋一下:

假設您有一個僅包含 POD 類型成員的結構(“Plain Old Data” - 請參閱C++ 中的 POD 類型是什么?

struct POD_OnlyStruct
{
    int a;
    char b;
};

POD_OnlyStruct t = {};  // OK

POD_OnlyStruct t;
memset(&t, 0, sizeof t);  // OK as well

在這種情況下寫一個POD_OnlyStruct t = {}POD_OnlyStruct t; memset(&t, 0, sizeof t) POD_OnlyStruct t; memset(&t, 0, sizeof t)沒有太大區別,因為我們這里唯一的區別是在使用memset情況下對齊字節設置為零值。 由於您通常無法訪問這些字節,因此對您來說沒有區別。

另一方面,由於您已將問題標記為 C++,讓我們嘗試另一個示例,其成員類型與 POD 不同

struct TestStruct
{
    int a;
    std::string b;
};

TestStruct t = {};  // OK

{
    TestStruct t1;
    memset(&t1, 0, sizeof t1);  // ruins member 'b' of our struct
}  // Application crashes here

在這種情況下,使用像TestStruct t = {}這樣的表達式是好的,在它上面使用memset會導致崩潰。 如果您使用memset會發生以下情況 - 創建了一個TestStruct類型的對象,從而創建了一個std::string類型的對象,因為它是我們結構的成員。 接下來, memset將對象b所在的內存設置為某個值,比如零。 現在,一旦我們的 TestStruct 對象超出范圍,它將被銷毀,當輪到它的成員std::string b您將看到崩潰,因為該對象的所有內部結構都被memset破壞了。

所以,現實情況是,那些東西有很大的不同,雖然有時需要memset整體結構,在某些情況下零,它總是重要的是要確保你知道你在做什么,而不是犯了一個錯誤在我們的第二個例子。

我的投票 -只有在需要時才在對象上使用memset ,並在所有其他情況下使用默認初始化x = {}

根據結構成員的不同,這兩種變體不一定等效。 memset會將結構設置為所有位為零,而值初始化會將所有成員初始化為零值。 C 標准保證這些僅對於整數類型是相同的,而不是對於浮點值或指針。

此外,某些 API 要求將結構真正設置為所有位為零。 例如,伯克利套接字 API 以多態方式使用結構,重要的是將整個結構真正設置為零,而不僅僅是明顯的值。 API 文檔應該說明結構是否真的需要全部為零,但它可能有缺陷。

但是,如果這些或類似的情況都不適用,那么這取決於您。 在定義結構時,我更喜歡值初始化,因為它可以更清楚地傳達意圖。 當然,如果您需要將現有結構歸零,則memset是唯一的選擇(當然,除了手動將每個成員初始化為零之外,但通常不會這樣做,尤其是對於大型結構)。

如果您的結構包含以下內容:

int a;
char b;
int c;

然后將在“b”和“c”之間插入填充字節。 memset() 會將它們歸零,反之則不會,因此將有 3 個字節的垃圾(如果您的整數是 32 位)。 如果您打算使用結構從文件中讀/寫,這可能很重要。

我會使用值初始化,因為它看起來很干凈,而且不像你提到的那樣容易出錯。 我認為這樣做沒有任何缺點。

不過,您可能會依賴memset在使用后將結構歸零。

並不是說它很常見,但我想第二種方法也有將浮點數初始化為零的好處。 雖然做 memset 肯定不會

值的初始化因為它可以在編譯時完成。
它也正確 0 初始化所有 POD 類型。

memset() 在運行時完成。
如果結構不是 POD,則使用 memset() 也是可疑的。
未正確初始化(為零)非 int 類型。

在某些編譯器中, STRUCT theStruct = {}; 將轉換為memset( &theStruct, 0, sizeof( STRUCT ) ); 在可執行文件中。 一些 C 函數已經鏈接到執行運行時設置,因此編譯器可以使用這些庫函數,如 memset/memcpy。

如果有很多指針成員並且您將來可能會添加更多成員,則使用 memset 會有所幫助。 結合適當的assert(struct->member)調用,您可以避免因嘗試遵從您忘記初始化的壞指針而導致的隨機崩潰。 但如果你不像我那么健忘,那么成員初始化可能是最好的!

但是,如果您的結構被用作公共 API 的一部分,您應該讓客戶端代碼使用 memset 作為要求。 這有助於未來驗證,因為您可以添加新成員,並且客戶端代碼將在 memset 調用中自動將它們清空,而不是將它們留在(可能危險的)未初始化狀態。 例如,這就是您在使用套接字結構時所做的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM