Как вы сравниваете структуры на предмет равенства в C?
Вопрос
Как вы сравниваете два экземпляра 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:
@Greg прав в том, что в общем случае необходимо написать явные функции сравнения.
Можно использовать memcmp
если:
- структуры не содержат полей с плавающей запятой, которые, возможно,
NaN
. - структуры не содержат отступов (используйте
-Wpadded
с помощью clang для проверки этого) ИЛИ структуры явно инициализируются с помощьюmemset
при инициализации. - не существует типов элементов (таких как Windows
BOOL
), которые имеют различные, но эквивалентные значения.
Если вы не программируете для встроенных систем (или не пишете библиотеку, которая может быть использована в них), я бы не стал беспокоиться о некоторых угловых случаях в стандарте C.Ближний противразличие между дальними указателями не существует ни на одном 32- или 64-разрядном устройстве.Ни одна не внедренная система, о которой я знаю, не имеет нескольких NULL
указатели.
Другой вариант - автоматически сгенерировать функции равенства.Если вы изложите свои определения структур простым способом, то можно использовать простую обработку текста для обработки простых определений структур.Вы можете использовать libclang для общего случая – поскольку он использует тот же интерфейс, что и Clang, он корректно обрабатывает все угловые случаи (за исключением ошибок).
Я не видел такой библиотеки генерации кода.Однако это кажется относительно простым.
Однако также бывает так, что такие сгенерированные функции равенства часто делают неправильные вещи на уровне приложения.Например, должны ли два UNICODE_STRING
структуры в Windows сравниваются поверхностно или глубоко?
Это зависит от того, является ли вопрос, который вы задаете,:
- Являются ли эти две структуры одним и тем же объектом?
- Имеют ли они одинаковую ценность?
Чтобы выяснить, являются ли они одним и тем же объектом, сравните указатели на две структуры на предмет равенства.Если вы хотите в целом выяснить, имеют ли они одинаковое значение, вам нужно провести глубокое сравнение.Это включает в себя сравнение всех участников.Если члены являются указателями на другие структуры, вам также необходимо выполнить рекурсию в эти структуры.
В особом случае, когда структуры не содержат указателей, вы можете выполнить 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))) {
/* ... */
}
}