2

最小の例:

#include <fstream>

struct TFoo
{
    bool Field1_ = false;
    uint64_t Field2_ = 0;
};

int main() {
    TFoo Foo_{};
    const char* filename = "text.txt";
    std::ofstream f(filename);

    f.write((char*)(&Foo_), sizeof(Foo_));
}

msan の 5 番目のバージョン以降の clang は、次のように報告します。

Uninitialized bytes in __interceptor_fwrite at offset 0 inside [0x720000000000, 15)
==71928==WARNING: MemorySanitizer: use-of-uninitialized-value
    #0 0x2823aa  (/home/<hidden>/test-ofstream+0x2823aa)
    #1 0x27830f  (/home/<hidden>/test-ofstream+0x27830f)
    #2 0x272757  (/home/<hidden>/test-ofstream+0x272757)
    #3 0x271388  (/home/<hidden>/test-ofstream+0x271388)
    #4 0x270c96  (/home/<hidden>/test-ofstream+0x270c96)
    #5 0x2709e2  (/home/<hidden>/test-ofstream+0x2709e2)
    #6 0x26ff9e  (/home/<hidden>/test-ofstream+0x26ff9e)
    #7 0x7fbc7238382f  (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

これは、Field1_との間のパディング バイトField2_が初期化されていないためです。

大丈夫です、MSANは正しいです。

しかし、コードにそのようなコード (構造体をバイナリ ファイルに保存する) の非常に大きな例が含まれている場合、コードを大幅に改善する美しい方法はありますか?

(パックされた構造体は解決策ではありません。)

4

1 に答える 1

1

の定義を変更できる場合はstruct TFoo、次のようなコンストラクタを追加できます。

struct TFoo {
  bool Field1_;
  uint64_t Fields_;
  TFoo() { memset(this, 0, sizeof(*this)); }
  TFoo(const TFoo &rhs) : TFoo() { Field1_ = rhs.Field1_; Field2_ = rhs.Field2_; }
};

memset標準に従ってこの方法を使用することはできないと思いますが、コンパイラでは機能する可能性があります。クラスのパディング バイトだけをゼロにする方法を参照してください。で、このソリューションについて説明します。

元の答え

Field1_これは、との間のパディングされたバイトをゼロにするために頭に浮かびましたField2_。しかし、正直なところ、規格に準拠しているかどうかはわかりません。確かに、ある種のTFooオブジェクトのシリアル化の方がはるかに優れていますが、私の理解が正しければ、それは選択肢ではありませんね。

struct TFoo
{
  bool Field1_ = false;
  uint64_t Field2_ = 0;
}; 

struct TFooWrapper {
  union {
    TFoo tfoo;
    char dummy[sizeof(TFoo)] = { 0 };
  } u;
};

アップデート

http://en.cppreference.com/w/cpp/language/unionから:最大で 1 つのバリアント メンバーが既定のメンバー初期化子を持つことができます。したがって、上記のコードは正しくない可能性があります。ただし、たとえば、デフォルトのコンストラクターを定義TFooWrapperして、すべてのバイトをゼロにすることができます。

于 2018-01-26T09:08:02.797 に答える