最小例子:
#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是对的。
但是如果代码中包含非常大的代码示例(将结构保存到二进制文件中),是否有任何漂亮的方法可以大规模地使代码更好?
(打包结构不是我们的解决方案。)
答案 0 :(得分: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
,但它可能适用于你的编译器。请参阅How can I zero just the padding bytes of a class?,其中讨论了此解决方案。
原始回答
我想到了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
定义默认构造函数,将所有字节清零。