Как вы сравниваете структуры для равенства в C?
Как вы сравниваете два случая структур на равенство в стандарте C?
11 ответов
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
если:
- структуры не содержат полей с плавающей точкой, которые возможно
NaN
, - структуры не содержат отступов (используйте
-Wpadded
с помощью clang, чтобы проверить это) ИЛИ структуры явно инициализируются сmemset
при инициализации. - нет типов членов (таких как Windows
BOOL
), которые имеют разные, но эквивалентные значения.
Если вы не программируете для встраиваемых систем (или пишете библиотеку, которая может быть использована на них), я бы не стал беспокоиться о некоторых ключевых случаях в стандарте C. Различия ближнего и дальнего указателей не существует ни на одном 32- или 64-разрядном устройстве. Ни одна не встроенная система, о которой я знаю, не имеет NULL
указатели.
Другой вариант - автоматически генерировать функции равенства. Если вы выложите свои определения структуры простым способом, можно использовать простую обработку текста для обработки простых определений структуры. Вы можете использовать libclang для общего случая - поскольку он использует тот же внешний интерфейс, что и Clang, он правильно обрабатывает все угловые случаи (за исключением ошибок).
Я не видел такой библиотеки генерации кода. Однако это выглядит относительно просто.
Тем не менее, это также тот случай, когда такие сгенерированные функции равенства часто делают неправильные вещи на уровне приложения. Например, если два UNICODE_STRING
структуры в винде сравнивать неглубоко или глубоко?
Обратите внимание, что вы можете использовать memcmp() для нестатических структур, не беспокоясь о заполнении, если вы не инициализируете все элементы (сразу). Это определяется C90:
Это зависит от того, является ли вопрос, который вы задаете:
- Являются ли эти две структуры одним и тем же объектом?
- Они имеют одинаковую ценность?
Чтобы выяснить, являются ли они одним и тем же объектом, сравните указатели с двумя структурами равенства. Если вы хотите узнать в общем, имеют ли они одинаковое значение, вы должны сделать глубокое сравнение. Это включает в себя сравнение всех членов. Если участники являются указателями на другие структуры, вам также необходимо участвовать в этих структурах.
В особом случае, когда структуры не содержат указателей, вы можете сделать memcmp, чтобы выполнить побитовое сравнение данных, содержащихся в каждом, без необходимости знать, что эти данные означают.
Убедитесь, что вы знаете, что означает "равно" для каждого члена - это очевидно для целых чисел, но более тонко, когда речь идет о значениях с плавающей запятой или пользовательских типах.
memcmp
не сравнивает структуру, memcmp
сравнивает двоичный файл, и в структуре всегда есть мусор, поэтому он всегда выдает False в сравнении.
Сравните элемент за элементом, это безопасно и не терпит неудачу.
Если структуры содержат только примитивы или если вы заинтересованы в строгом равенстве, вы можете сделать что-то вроде этого:
int my_struct_cmp (const struct my_struct * lhs, const struct my_struct * rhs) { вернуть memcmp(lhs, rsh, sizeof(struct my_struct)); }
Однако, если ваши структуры содержат указатели на другие структуры или объединения, вам нужно написать функцию, которая правильно сравнивает примитивы и при необходимости выполняет сравнения с другими структурами.
Имейте в виду, однако, что вы должны были использовать memset(&a, sizeof(struct my_struct), 1), чтобы обнулить диапазон памяти структур как часть вашей инициализации ADT.
Если переменная 2 структурам инициализирована с помощью 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))) {
/* ... */
}
}