簡體   English   中英

memcpy和memset與分配和初始化的歷史記錄

[英]history of memcpy and memset vs assignment and initialization

所以,我的理解是以下代碼:

somestruct_t a = {0};
somestruct_t b;
b = a;

在可能的情況下,總是更可取的:

somestruct_t a;
somestruct_t b;
memset(&a, 0, sizeof(a));
memcpy(&b, &a, sizeof(a));

頂級構造幾乎總是可能的...這引出了我的問題:由於頂級代碼都表現良好,對我來說,對於學習該語言的人來說顯然更直觀,為什么memsetmemcpy模式如此驚人地流行於C甚至非OO C ++代碼? 幾十年來我所做的每一個項目都傾向於最底層的模式。

我假設有一些歷史原因,例如非常老的編譯器不支持它或某些,但我非常想知道具體原因。

我知道一般歷史問題是偏離主題的,但這是一個非常具體的不良做法,我想更好地理解。

編輯我不是試圖斷言memcpy和memset一般都不好。 我在談論一個非常具體的使用模式的分配或初始化單個結構。

聽起來你的經歷與我的經歷有很大不同,而且這里的其他一些評論員也是如此。

我不認識任何更喜歡的人

memcpy(&a, &b, sizeof(a));

過度

a = b;

在我的編程世界中(以及幾乎任何我能想象到的世界),簡單的賦值比memcpymemcpy memcpy用於移動任意數據塊(類似於strcpy ,但是當它是任意字節而不是以空字符結尾的字符串時)。 很難想象為什么有人會主張使用memcpy而不是struct assignment。 當然,到處都有個別程序員遇到各種不良習慣,所以我想如果有人喜歡相反的話,我不會感到驚訝,但我不得不說,我一般不同意他們正在做的事情。

有人在評論中推測可能有一些歷史先例在起作用,但至少對於memcpy -versus-assignment問題,我可以肯定地說,事實並非如此。

曾幾何時,之前有C90 memcpy ,有BSD bcopy ,但在此之前有bcopy 有沒有做一堆字節的有效副本,從A點到B點的標准功能。 但是有結構分配,幾乎從一開始就在語言中。 結構賦值通常使用一個漂亮,緊湊,編譯器生成的字節復制循環。 所以曾經有一段時間做這樣的事情很時髦:

#define bcpy(a, b, n) (*(struct {char x[n];} *)a = *(struct {char x[n];} *)b)

我可能得到了錯誤的語法,但這會劫持編譯器執行高效結構分配的能力,並重新利用它將n個字節從任意指針b復制到任意指針a ,即就像bcopymemcpy

換句話說,它不像memcpy首先出現,然后是結構賦值 - 實際上恰恰相反!

現在, memset與struct初始化是一個不同的故事。

將結構歸零的大多數“干凈”方法都是初始化,但當然,想要在某個時間點將某個結構設置為全零時並不常見。 擁有動態分配的結構並使用malloc / realloc而不是calloc也是不常見的。 所以在這些情況下, memset很有吸引力。 我認為現代C有結構常量,你可以隨時使用,但我猜我不是唯一還沒有學過它們的人,所以仍然傾向於使用memset

所以我不會考慮使用memset是糟糕的風格,而不是像memcpy是結構賦值的糟糕風格。

雖然我已經看到並編寫了類似的代碼

struct s zerostruct = { 0 };

然后是

a = zerostruct;

作為“更好的風格”的替代品

memset(&a, 0, sizeof(a));   

一句話:我不同意建議使用memcpy而不是結構分配,而且我批評任何喜歡它的人。 memset對於歸零結構非常有用(並且不被推薦),因為替代方案並不那么引人注目。

只有一個用例在哪里

struct somestruct  foo = { 0 };

是不夠的,而且

struct somestruct  foo;
memset(&foo, 0, sizeof foo);

需要改為使用:當結構中的填充可能很重要時。

你看,兩者之間唯一的區別是后者保證將結構填充清除為零,而前者只能保證將結構成員清零。

人們可能關心填充的原因是基於向上/未來的兼容性。 如果在當前程序中保證填充為零,則庫的未來版本可以“重用”新數據字段的填充,並且仍然可以使用較舊的二進制文件。

從C99開始,新的C庫確實應該為這個目的保留一些成員。 這通常是您在許多庫定義的結構中看到“保留”字段的原因,甚至在Linux內核用戶空間界面中也是如此。 因此,填充問題實際上只與C99支持普及之前開發的結構有關; 換句話說,只在舊圖書館。

我知道的一個結構應該總是使用memset()清除,是struct sigaction ,在POSIX.1中定義。 在大多數POSIXy系統中,它是一個完全正常的結構(所以只清除結構成員的代碼在這些系統上工作得非常好),但是由於不同時間的各種不同實現(特別是如何實現信號掩碼) ,我相信仍然有C庫的系統有一個結構版本,清除填充仍然很重要。

(這是因為sa_handlersa_sigaction成員通常在聯合中,和/或因為sigset_t的定義可能已更改。)

在其他一些較舊的庫中可能還有其他庫,因此我建議在使用具有1999之前版本的庫時使用memset()習慣用法,其示例代碼也使用它。

嗯,結構賦值和“零初始化” 總是優於memset()memcpy() 對於大小或速度來說,編譯器可以更好地優化memset/cpy() (具有這兩個標准庫函數的“特殊知識”)。 當然,會有點奇怪,但是,有可能。

此外,由於“零初始化”對堆分配的結構不起作用,因此總是使用memset()可以說一致性。

類似的情況適用於您可能希望復制一些相鄰結構(數組的一部分)的情況 - 再次,如果您始終使用memcpy() ,則代碼更加一致。

在一個歷史性的說明中,我使用了工具鏈,其中本地結構初始化被破壞了。 在經歷過這樣的工具鏈的項目中,即使在這樣的工具鏈被解雇之后,“始終使用memset() ”也將占上風。 問題是,即使您的工具鏈的memset()被破壞,您也可以創建自己的,但是您無法進行自己的本地struct初始化...

暫無
暫無

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

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