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最多一个变体成员可以有一个默认的成员初始化器。因此,上面的代码可能不正确。但是您可以,例如,定义默认构造函数以TFooWrapper将所有字节清零。

于 2018-01-26T09:08:02.797 回答