Анализатор обнаружил фрагмент кода, в котором сравниваются объекты структур, содержащие байты выравнивания.
Рассмотрим синтетический пример:
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);
}
Однако этот способ имеет недостатки:
Данная диагностика классифицируется как:
|