﻿# V1103\. The values of padding bytes are unspecified\. Comparing objects with padding using 'memcmp' may lead to unexpected result\.

Анализатор обнаружил фрагмент кода, в котором сравниваются объекты структур, содержащие байты выравнивания\. 

Рассмотрим синтетический пример:

```cpp
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' в памяти:

```cpp
[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
```

Для того, чтобы корректно и эффективно работать с объектами в памяти, компилятор применяет [выравнивание данных](https://pvs-studio.ru/ru/blog/terms/0011/)\. На типовых [моделях данных](https://pvs-studio.ru/ru/blog/terms/0012/) выравнивание типа 'unsinged char' равно 1, а типа 'int' – 4\. Это означает, адрес поля 'Foo::i' должен быть кратен 4\. Чтобы сделать это, компилятор вставит 3 байта выравнивания после поля 'Foo::a'\.

Стандарты C и C\+\+ не уточняют, будут ли занулены байты выравнивания при инициализации объекта\. Следовательно, при попытке побайтового сравнения двух объектов с одинаковыми значениями полей при помощи функции 'memcmp' результат может не всегда равняться 0\.

Исправить проблему можно несколькими способами\.

**Способ N1 \(предпочтительный\)**\. Написать компаратор и сравнивать объекты при помощи него\. 

Для языка C:

```cpp
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\+\+:

```cpp
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, код можно упростить, указав компилятору самостоятельно сгенерировать компаратор:

```cpp
struct Foo
{
  unsigned char a;
  int i;

  auto operator==(const Foo &) const noexcept = default;
};
```

**Способ N2**\. Предварительно занулять объекты\.

```cpp
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);
}
```

Однако этот способ имеет недостатки:

* Вызов 'memset' вносит накладные расходы на зануление всего участка памяти\.
* Перед вызовом 'memcmp' нужно также гарантировать, что память под объект была занулена\. В проекте со сложным потоком управления об этом можно легко забыть\.