Как вы сравниваете структуры для равенства в C? - PullRequest
190 голосов
/ 27 сентября 2008

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

Ответы [ 11 ]

176 голосов
/ 27 сентября 2008

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

100 голосов
/ 27 сентября 2008

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

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

20 голосов
/ 27 сентября 2008

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

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

17 голосов
/ 27 сентября 2008

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

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

Вышеприведенный код потерпит неудачу для такой структуры:

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

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

5 голосов
/ 19 августа 2015

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

Можно использовать memcmp, если:

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

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

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

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

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

5 голосов
/ 27 сентября 2008

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

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

2 голосов
/ 07 августа 2012

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

Сравните элемент за элементом, это безопасно и не дает сбоя.

2 голосов
/ 27 сентября 2008

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

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

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

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

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

1 голос
/ 27 сентября 2008

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

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.

0 голосов
/ 30 сентября 2014

В этом совместимом примере используется расширение компилятора #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))) {
    /* ... */
  }
}
...