Как вы сравниваете структуры для равенства в C?

Как вы сравниваете два экземпляра структур для равенства в стандарте C?

11 ответов


C не предоставляет языковых средств для этого - вы должны сделать это сами и сравнить каждый член структуры по членам.


у вас может возникнуть искушение использовать memcmp(&a, &b, sizeof(struct foo)), но это может работать не во всех ситуациях. Компилятор может добавить в структуру буферное пространство выравнивания, и значения, найденные в расположениях памяти, расположенных в буферном пространстве, не гарантируются каким-либо конкретным значением.

но, если вы используете calloc или memset полный размер структур перед их использованием, вы can сделать мелкий сравнению с memcmp (Если ваша структура содержит указатели, она будет соответствовать только если адрес, на который указывают указатели, одинаковый).


Если вы делаете это много, я бы предложил написать функцию, которая сравнивает две структуры. Таким образом, если вы когда-либо измените структуру, вам нужно только изменить сравнение в одном месте.

а как это сделать.... Вам нужно сравнить каждый элемент индивидуально


вы не можете использовать memcmp для сравнения структур для равенства из-за потенциальных случайных символов заполнения между полями в структурах.

  // bad
  memcmp(&struct1, &struct2, sizeof(struct1));

вышеуказанное не удалось бы для такой структуры:

typedef struct Foo {
  char a;
  /* padding */
  double d;
  /* padding */
  char e;
  /* padding */
  int f;
} Foo ;

вы должны использовать сравнение по членам, чтобы быть в безопасности.


Примечание Вы можете использовать memcmp () на нестатических конструкциях без беспокоясь о прокладке, пока вы не инициализируете все члены (сразу). Это определяется C90:

http://www.pixelbeat.org/programming/gcc/auto_init.html


@Greg правильно, что в общем случае необходимо написать явные функции сравнения.

можно использовать memcmp Если:

  • структуры не содержат полей с плавающей запятой, которые, возможно,NaN.
  • структуры не содержат заполнения (используйте -Wpadded С clang, чтобы проверить это) или структуры явно инициализируются с memset при инициализации.
  • нет типов членов (например, Windows BOOL), которые различные, но эквивалентные значения.

если вы не программируете для встроенных систем (или пишете библиотеку, которая может быть использована на них), я бы не беспокоился о некоторых угловых случаях в стандарте C. Различие между указателем near и far не существует ни на одном 32 - или 64 - разрядном устройстве. Никакая не встроенная система, о которой я знаю, не имеет нескольких NULL указатели.

другой вариант-автоматически генерировать функции равенство. Если вы выложите свои определения структуры в простой способ, можно использовать простую обработку текста для обработки простых определений структуры. Вы можете использовать libclang для общего случая-поскольку он использует тот же интерфейс, что и Clang, он правильно обрабатывает все угловые случаи (исключая ошибки).

Я не видел такой библиотеки генерации кода. Однако, это кажется относительно простым.

однако, это также случай, когда такие генерируемые функции равенства часто делают неправильную вещь на уровне приложения. Например, если два UNICODE_STRING структуры в Windows сравниваются поверхностно или глубоко?


memcmp не сравнивает структуру,memcmp сравнивает двоичный файл, и в структуре всегда есть мусор, поэтому он всегда выходит ложным в сравнении.

сравнить элемент за элементом его безопасно и не терпит неудачу.


Если структуры содержат только примитивы или если вас интересует строгое равенство, вы можете сделать что-то вроде этого:

int my_struct_cmp(const struct my_struct * lhs, const struct my_struct * rhs)
{
    return memcmp(lhs, rsh, sizeof(struct my_struct));
}

однако, если ваши структуры содержат указатели на другие структуры или объединения, вам нужно будет написать функцию, которая правильно сравнивает примитивы и делает вызовы сравнения против других структур по мере необходимости.

имейте в виду, однако, что вы должны были использовать memset (&a, sizeof (struct my_struct), 1) для обнуления памяти диапазон структур как часть инициализации ADT.


Это зависит от того, задаете ли вы вопрос:

  1. являются ли эти две структуры одним и тем же объектом?
  2. имеют ли они одинаковое значение?

чтобы узнать, являются ли они одним и тем же объектом, сравните указатели на две структуры для равенства. Если вы хотите узнать в целом, имеют ли они одинаковое значение, вам нужно сделать глубокое сравнение. Это включает в себя сравнение всех членов. Если члены указатели на другие структуры нужно обратиться в эти структуры тоже.

в частном случае, когда структуры не содержат указателей, вы можете сделать memcmp для выполнения побитового сравнения данных, содержащихся в каждом, без необходимости знать, что означают данные.

убедитесь, что вы знаете, что означает "равно" для каждого члена - это очевидно для ints, но более тонко, когда дело доходит до значений с плавающей запятой или пользовательских типов.


Если переменная 2 structures инициализирована calloc или они установлены с 0 по memset, так что вы можете сравнить ваши 2 структуры с memcmp и нет никакого беспокойства о структуре мусора, и это позволит вам заработать время


в этом совместимом примере используется расширение компилятора #pragma pack от Microsoft Visual Studio для обеспечения максимально плотной упаковки элементов структуры:

#include <string.h>

#pragma pack(push, 1)
struct s {
  char c;
  int i;
  char buffer[13];
};
#pragma pack(pop)

void compare(const struct s *left, const struct s *right) { 
  if (0 == memcmp(left, right, sizeof(struct s))) {
    /* ... */
  }
}