簡體   English   中英

使用未初始化的成員復制結構

[英]Copying structs with uninitialized members

復制其中一些成員未初始化的結構是否有效?

我懷疑這是未定義的行為,但如果是這樣,則將任何未初始化的成員留在結構中(即使這些成員從未直接使用過)非常危險。 所以我想知道標准中是否允許這樣做。

例如,這是有效的嗎?

struct Data {
  int a, b;
};

int main() {
  Data data;
  data.a = 5;
  Data data2 = data;
}

是的,如果未初始化的成員不是無符號窄字符類型或std::byte ,則使用隱式定義的復制構造函數復制包含此不確定值的結構在技術上是未定義的行為,因為它用於復制具有不確定值的變量相同的類型,因為[dcl.init]/12

這適用於此處,因為隱式生成的復制構造函數(除了union s 之外)被定義為單獨復制每個成員,就像通過直接初始化一樣,請參閱[class.copy.ctor]/4

這也是活動CWG 問題 2264 的主題

不過,我想在實踐中你不會有任何問題。

如果您想 100% 確定,即使成員具有不確定的值,如果類型是可簡單復制的,則使用std::memcpy始終具有明確定義的行為。


撇開這些問題不談,你應該總是在構造時使用指定的值正確初始化你的類成員,假設你不需要類有一個簡單的默認構造函數 您可以使用默認成員初始值設定項語法輕松完成此操作,例如對成員進行值初始化:

struct Data {
  int a{}, b{};
};

int main() {
  Data data;
  data.a = 5;
  Data data2 = data;
}

通常,復制未初始化的數據是未定義的行為,因為該數據可能處於陷阱狀態。 引用頁面:

如果對象表示不表示對象類型的任何值,則稱為陷阱表示。 除了通過字符類型的左值表達式讀取之外,以任何方式訪問陷阱表示都是未定義的行為。

對於浮點類型,信號 NaN 是可能的,並且在某些平台上整數可能具有陷阱表示。

但是,對於可 簡單復制的類型,可以使用memcpy來復制對象的原始表示。 這樣做是安全的,因為不解釋對象的值,而是復制對象表示的原始字節序列。

在某些情況下,例如所描述的情況,C++ 標准允許編譯器以客戶認為最有用的任何方式處理構造,而不要求該行為是可預測的。 換句話說,此類構造調用“未定義行為”。 然而,這並不意味着這樣的構造是“禁止的”,因為 C++ 標准明確放棄了對“允許”格式良好的程序的管轄權。 雖然我不知道 C++ 標准的任何已發布的基本原理文檔,但它描述未定義行為的事實與 C89 非常相似,這表明預期的含義是相似的:“未定義的行為使實現者許可不捕獲某些困難的程序錯誤診斷。它還確定了可能符合語言擴展的區域:實現者可以通過提供官方未定義行為的定義來擴充語言”。

在許多情況下,處理某些事情的最有效方法是編寫下游代碼將關心的結構部分,而忽略下游代碼不會關心的那些部分。 要求程序初始化結構的所有成員,包括那些永遠不會關心的成員,將不必要地阻礙效率。

此外,在某些情況下,讓未初始化的數據以不確定的方式表現可能是最有效的。 例如,給定:

struct q { unsigned char dat[256]; } x,y;

void test(unsigned char *arr, int n)
{
  q temp;
  for (int i=0; i<n; i++)
    temp.dat[arr[i]] = i;
  x=temp;
  y=temp;
}

如果下游代碼不關心索引未在arr列出的x.daty.dat的任何元素的值,則代碼可能會優化為:

void test(unsigned char *arr, int n)
{
  q temp;
  for (int i=0; i<n; i++)
  {
    int it = arr[i];
    x.dat[index] = i;
    y.dat[index] = i;
  }
}

如果要求程序員在復制之前顯式地編寫temp.dat每個元素,包括那些下游不會關心的元素, temp.dat這種效率的提高是不可能的。

另一方面,在某些應用程序中,避免數據泄露的可能性很重要。 在這樣的應用程序中,有一個代碼版本可能很有用,該版本被檢測以捕獲任何復制未初始化存儲的嘗試,而不管下游代碼是否會查看它,或者有一個實現保證任何存儲可能泄露的內容將被歸零或以其他方式被非機密數據覆蓋。

據我所知,C++ 標准並沒有試圖說這些行為中的任何一個都比其他行為更有用,以證明強制執行它是合理的。 具有諷刺意味的是,缺乏規范可能是為了促進優化,但如果程序員不能利用任何類型的弱行為保證,任何優化都將被否定。

由於Data所有成員都是原始類型,因此data2將獲得data的所有成員的精確“逐位副本”。 所以價值data2.b將是完全一樣的價值data.b 但是,無法預測data.b確切值,因為您尚未對其進行顯式初始化。 它將取決於為data分配的內存區域中的字節值。

暫無
暫無

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

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