Анализатор обнаружил фрагмент кода, в котором сравниваются объекты структур, содержащие байты выравнивания.
Рассмотрим синтетический пример:
struct Foo { unsigned char a; int i; }; void bar() { Foo obj1 { 2, 1 }; Foo obj2 { 2, 1 }; auto result = std::memcmp(&obj1, &obj2, sizeof(Foo)); // <= }
Чтобы понять суть проблемы, надо рассмотреть расположение объектов класса 'C' в памяти:
[offset 0] unsigned char [offset 1] padding byte [offset 2] padding byte [offset 3] padding byte [offset 4] int, first byte [offset 5] int, second byte [offset 6] int, third byte [offset 7] int, fourth byte
Для того, чтобы корректно и эффективно работать с объектами в памяти, компилятор применяет выравнивание данных. На типовых моделях данных выравнивание типа 'unsinged char' равно 1, а типа 'int' – 4. Это означает, адрес поля 'Foo::i' должен быть кратен 4. Чтобы сделать это, компилятор вставит 3 байта выравнивания после поля 'Foo::a'.
Стандарты C и C++ не уточняют, будут ли занулены байты выравнивания при инициализации объекта. Следовательно, при попытке побайтового сравнения двух объектов с одинаковыми значениями полей при помощи функции 'memcmp' результат может не всегда равняться 0.
Исправить проблему можно несколькими способами.
Способ N1 (предпочтительный). Написать компаратор и сравнивать объекты при помощи него.
Для языка C:
struct Foo { unsigned char a; int i; }; bool Foo_eq(const Foo *lhs, const Foo *rhs) { return lhs->a == rhs->a && lhs->i == rhs->i; }
Для языка C++:
struct Foo { unsigned char a; int i; }; bool operator==(const Foo &lhs, const Foo &rhs) noexcept { return lhs.a == rhs.a && lhs.i == rhs.i; } bool operator!=(const Foo &lhs, const Foo &rhs) noexcept { return !(lhs == rhs); }
Начиная с C++20, код можно упростить, указав компилятору самостоятельно сгенерировать компаратор:
struct Foo { unsigned char a; int i; auto operator==(const Foo &) const noexcept = default; };
Способ N2. Предварительно занулять объекты.
struct Foo { unsigned char a; int i; }; bool Foo_eq(const Foo *lhs, const Foo *rhs) { return lhs->a == rhs->a && lhs->i == rhs->i; } void bar() { Foo obj1; memset(&obj1, 0, sizeof(Foo)); Foo obj2; memset(&obj2, 0, sizeof(Foo)); // initialization part auto result = Foo_eq(&obj1, &obj2); }
Однако этот способ имеет недостатки:
Данная диагностика классифицируется как:
|