Как вы сравниваете структуры для равенства в 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:
@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.
Это зависит от того, задаете ли вы вопрос:
- являются ли эти две структуры одним и тем же объектом?
- имеют ли они одинаковое значение?
чтобы узнать, являются ли они одним и тем же объектом, сравните указатели на две структуры для равенства. Если вы хотите узнать в целом, имеют ли они одинаковое значение, вам нужно сделать глубокое сравнение. Это включает в себя сравнение всех членов. Если члены указатели на другие структуры нужно обратиться в эти структуры тоже.
в частном случае, когда структуры не содержат указателей, вы можете сделать 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))) {
/* ... */
}
}