>
>
>
V2629. MISRA. Pointer arguments to the …


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

This diagnostic rule is based on the MISRA (Motor Industry Software Reliability Association) software development guidelines.

The diagnostic rule is relevant only for C.

The memcmp function from the standard library can lead to unexpected results when used with certain data types.

The function compares the first n bytes of two objects passed via pointers, byte by byte. However, there are cases whenmemcmp should not be used for byte-by-byte comparison.

The case N1. Structures or unions. Due to data alignment, logically equal objects may produce different results.

The example:

struct S 
{
  int  a;
  char b;
};

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

To clarify, assume that the size and alignment of int are 4, while for char they are 1. Since the processor must process data in memory efficiently, the S structure will be arranged in memory as follows:

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

The first member is located at offset 0x0 and occupies 4 bytes. The next member is located at offset 0x4 and occupies 1 byte. In addition, starting from offset 0x5, 3 bytes are added to the class objects to align them to a maximum alignment of 4 bytes.

The C language standard does not specify how padding bytes will be initialized. As a result, comparing two objects using the memcmp function may yield incorrect results.

The correct way to compare two objects is to compare each member of the class pairwise:

struct S 
{
  int  a;
  char b;
};

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

The case N2. Real numbers. Look at the example:

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

The function compares two arrays of real numbers of size length. Due to the nuances of real number representation, identical values may have different byte representations. As a result, memcmp may return a different result than the developer expects.

The better way to compare arrays of real numbers is as follows:

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;
}

The comparison function for two real numbers is determined by the comparison conditions. For example, it can be as follows:

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

  double largest = (B > A) ? B : A;

  return diff <= largest * maxRelDiff;
}

The case N3. Character arrays. Look at the example:

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

When you use memcmp to compare character arrays, the null character at the end of a string may be interpreted as data rather than a signal to stop the algorithm. Because of this, either an incorrect comparison may occur, or the comparison may go beyond the array bounds, leading to undefined behavior.

The correct way to compare character arrays:

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