Минимальный пример:
#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_));
}
clang с 5-ой версии в msan сообщает что-то вроде этого:
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 прав.
Но если код содержит очень большие примеры такого кода (сохранение структур в двоичные файлы), есть ли какой-нибудь красивый способ массового улучшения кода?
(Упакованные структуры не являются решением для нас.)
Если вы можете изменить определение 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
обнулить все байты.
Других решений пока нет …