Як ви порівнюєте два екземпляри структур для рівності у стандарті С?
Як ви порівнюєте два екземпляри структур для рівності у стандарті С?
Відповіді:
C не забезпечує жодних мовних засобів для цього - ви повинні зробити це самостійно і порівняти кожного члена структури за членом.
0.0, -0.0 NaN
є проблемою memcmp()
. Покажчики, які відрізняються бінарним поданням, можуть вказувати на одне місце (наприклад, DOS: seg: offset) і так дорівнювати. Деякі системи мають кілька нульових покажчиків, які порівнюються однаково. Те ж саме для незрозумілих int
типів -0 і плаваючої точки з надмірними кодуванням. (Intel довгий подвійний, десятковий64 тощо). Ці питання не мають жодної різниці в calloc()
застосуванні чи ні.
==
не працює зі структурами (як я), будь ласка , см stackoverflow.com/questions/46995631 / ...
Ви можете спокуситись використовувати memcmp(&a, &b, sizeof(struct foo))
, але це може не спрацювати у всіх ситуаціях. Компілятор може додати в буфер простір буфера вирівнювання, і значення, знайдені в місцях пам'яті, що лежать у буферному просторі, не гарантуються як якесь конкретне значення.
Але якщо ви використовуєте calloc
або memset
повний розмір структур перед їх використанням, ви можете провести неглибоке порівняння memcmp
(якщо ваша структура містить покажчики, вона збігатиметься лише в тому випадку, якщо адреса покажчиків однакова).
memcmp
тим, що спочатку очищено пам'ять. Що близьке до роботи, але не коректно. Звичайно, питання також не визначає "рівність", тож якщо ви вважаєте, що це означає "байт-рівність рівності представлення об'єкта", то memcmp
це саме так (чи очищена пам'ять чи ні).
Якщо ти це робиш багато, я б запропонував написати функцію, яка порівнює дві структури. Таким чином, якщо ви коли-небудь змінюєте структуру, вам потрібно змінити лише порівняння в одному місці.
Щодо того, як це зробити .... Вам потрібно порівняти кожен елемент окремо
Ви не можете використовувати memcmp для порівняння структур для рівності через потенційні випадкові символи прокладки між полями в структурах.
// bad
memcmp(&struct1, &struct2, sizeof(struct1));
Вищезазначене не вдасться для такої структури:
typedef struct Foo {
char a;
/* padding */
double d;
/* padding */
char e;
/* padding */
int f;
} Foo ;
Ви повинні використовувати порівняння для членів, щоб бути безпечним.
@Greg вірно, що в загальному випадку потрібно писати явні функції порівняння.
Можна використовувати, memcmp
якщо:
NaN
.-Wpadded
кланг для перевірки цього) АБО структури явно ініціалізуються memset
при ініціалізації.BOOL
), які мають чіткі, але еквівалентні значення.Якщо ви не програмуєте вбудовані системи (або записуєте бібліотеку, яка може бути використана на них), я б не турбувався про деякі найважливіші випадки стандарту С. Відмінність вказівника від ближнього та далекого не існує на жодному 32- або 64-бітному пристрої. Жодна невбудована система, про яку я знаю, не має декількох NULL
покажчиків.
Інший варіант - автоматичне створення функцій рівності. Якщо ви викладете свої визначення структури простим способом, для простого визначення структури можна використовувати просту обробку тексту. Ви можете використовувати libclang для загального випадку - оскільки він використовує той самий фронт, як і Clang, він обробляє всі кутові корпуси правильно (помилки заборони).
Я не бачив такої бібліотеки генерації коду. Однак це видається порівняно просто.
Однак також буває так, що такі створені функції рівності часто роблять неправильну справу на рівні програми. Наприклад, чи слід UNICODE_STRING
порівняти дрібно чи глибоко дві структури в Windows?
memset
і так далі не гарантує вартість заповнення біт після подальшого запису на структуру елемент, см: stackoverflow.com/q/52684192/689161
Зауважте, що ви можете використовувати memcmp () для нестатичних конструкцій, не турбуючись про підкладку, якщо ви не ініціалізуєте всіх членів (одразу). Це визначено C90:
{0, }
також буде нульовим будь-який байт padding?
Це залежить від того, чи задаєте ви запитання:
Щоб дізнатись, чи є вони однаковим об'єктом, порівняйте покажчики на дві структури на рівність. Якщо ви хочете дізнатися, чи мають вони однакове значення, ви повинні провести глибоке порівняння. Це включає порівняння всіх членів. Якщо члени є вказівниками на інші структури, вам також потрібно повторити їх.
У спеціальному випадку, коли структури не містять покажчиків, ви можете зробити 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.
Цей сумісний приклад використовує розширення компілятора пакетів #pragma від 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))) {
/* ... */
}
}