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

StackOverflow https://stackoverflow.com/questions/141720

  •  02-07-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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

Другие советы

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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.

если переменная 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))) {
    /* ... */
  }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top