﻿# V2629\. MISRA\. Pointer arguments to the 'memcmp' function should point to an appropriate type\.

Данное диагностическое правило основано на руководстве [MISRA](https://misra.org.uk/) \(Motor Industry Software Reliability Association\) по разработке программного обеспечения\.

Это правило актуально только для C\.

Использование функции [`memcmp`](https://en.cppreference.com/w/c/string/byte/memcmp) из стандартной библиотеки может приводить к неожиданным результатам при использовании с определёнными типами данных\.

Функция производит побайтовое сравнение первых `n` байт двух объектов, переданных через указатели\. Однако есть случаи, когда не следует использовать `memcmp` для побайтового сравнения\.

**Случай N1**\. Структуры или объединения\. Из\-за [байтов выравнивания](https://pvs-studio.ru/ru/blog/terms/0011/) при логически равных объектах можно получить различный результат\.

Пример:

```cpp
struct S 
{
  int  a;
  char b;
};

bool equals(struct S *s1, struct S *s2)
{
  return memcmp(s1, s2, sizeof(struct S)) == 0;
}
```

Для дальнейшего понимания примем, что размер и выравнивание типа `int` равно `4`, а для `char` – `1`\. Вследствие того, что процессор должен эффективно работать с данными в памяти, структура `S` будет располагаться в памяти следующим образом:

```cpp
class S size(8):
     +---
 0   | a
 4   | b
     | <alignment member> (size=3)
     +---
```

Первое поле расположено по смещению `0x0` и занимает 4 байта\. Следующее поле расположено по смещению `0x4` и занимает 1 байт\. Помимо этого, начиная со смещения `0x5` в объекты класса добавляются 3 байта для того, чтобы выровнить объекты класса по максимальному выравниванию в 4 байта\.

Стандарт языка C не гарантирует, как именно будут инициализированы байты выравнивания\. Из\-за этого сравнение двух объектов через функцию `memcmp` может произойти неверно\.

Корректным способом сравнения двух объектов будет попарное сравнение всех полей класса:

```cpp
struct S 
{
  int  a;
  char b;
};

bool equals(struct S *s1, struct S *s2)
{
  return s1->a == s2->a && s1->b == s2->b;
}
```

**Случай N2**\. Вещественные числа\. Рассмотрим пример:

```cpp
bool equals(double *lhs, double *rhs, size_t length)
{
  return memcmp(lhs, rhs, length * sizeof(double)) == 0;
}
```

Функция производит сравнение двух массивов вещественных чисел размера `length`\. Из\-за особенностей формата представления вещественных чисел одинаковые значения могут иметь различные байтовые представления\. Из\-за этого `memcmp` вернёт не тот результат, что ожидает разработчик\.

Более корректный способ сравнения выглядит следующим образом:

```cpp
bool equals_d(double lhs, double rhs, double eps = DBL_EPSILON);

bool equals(double *lhs, double *rhs, size_t length)
{
  for (size_t i = 0; i < length; ++i)
  {
    if (!equals_d(lhs[i], rhs[i])) return false;
  }

  return true;
}
```

Функция сравнения двух вещественных чисел подбирается исходя из условий сравнения\. Например, она может быть такой:

```cpp
bool equals_d(double lhs, double rhs, double eps)
{
  double diff = fabs(lhs - rhs);
  lhs = fabs(lhs);
  rhs = fabs(rhs);

  double largest = (rhs > lhs) ? rhs : lhs;

  return diff <= largest * eps;
}
```

**Случай N3**\. Символьные массивы\. Рассмотрим пример:

```cpp
bool equals(const char *lhs, const char *rhs, size_t length)
{
  return memcmp(lhs, rhs, length) == 0;
}
```

При применении `memcmp` при сравнении символьных массивов [терминальный ноль](https://pvs-studio.ru/ru/blog/terms/0088/) может быть интерпретирован как данные, а не как сигнал об остановке алгоритма\. Из\-за этого может произойти либо неверное сравнение, либо выход за границу массива, что ведёт к [неопределённому поведению](https://pvs-studio.ru/ru/blog/terms/0066/)\.

Корректный способ сравнения символьных массивов:

```cpp
bool equals(const char *lhs, const char *rhs, size_t length)
{
  return strncmp(lhs, rhs, length) == 0;
}
```