Jak porównać dwa przypadki struktur dla równości w standardowym C?
Jak porównać dwa przypadki struktur dla równości w standardowym C?
Odpowiedzi:
C nie udostępnia żadnych narzędzi językowych, aby to zrobić - musisz to zrobić sam i porównać każdego członka struktury według członka.
0.0, -0.0 NaN
jest problem z memcmp()
. Wskaźniki, które różnią się reprezentacją binarną, mogą wskazywać na tę samą lokalizację (np. DOS: seg: offset), a więc są równe. Niektóre systemy mają wiele zerowych wskaźników, które porównują się jednakowo. To samo dotyczy niejasnych int
typów -0 i zmiennoprzecinkowych z kodowaniem redundantnym. (Intel long double, decimal64 itp.) Te problemy nie mają calloc()
znaczenia, czy są używane, czy nie, ani padding.
==
nie działa ze strukturami (jak ja), zobacz stackoverflow.com/questions/46995631/...
Możesz ulec pokusie użycia memcmp(&a, &b, sizeof(struct foo))
, ale może nie działać we wszystkich sytuacjach. Kompilator może dodawać przestrzeń bufora wyrównania do struktury, a wartości znalezione w lokalizacjach pamięci znajdujących się w przestrzeni bufora nie są gwarantowane, że będą jakąś szczególną wartością.
Ale jeśli używasz calloc
lub memset
pełny rozmiar struktur przed ich użyciem, to może zrobić płytkie porównanie z memcmp
(jeśli struktura zawiera wskaźniki, będzie pasować tylko wtedy, gdy adres Wskaźniki wskazują na to samo).
memcmp
warunkiem, że pamięć została najpierw wyczyszczona. Co jest bliskie pracy, ale nie jest poprawne. Często pytanie również nie definiuje „równości”, więc jeśli przyjmiesz, że oznacza to „bajtową równość reprezentacji obiektu”, to memcmp
robi dokładnie to (niezależnie od tego, czy pamięć jest wyczyszczona, czy nie).
Jeśli często to robisz, sugerowałbym napisanie funkcji porównującej dwie struktury. W ten sposób, jeśli kiedykolwiek zmienisz strukturę, wystarczy zmienić porównanie w jednym miejscu.
Co do tego, jak to zrobić ... Musisz porównać każdy element indywidualnie
Nie można używać memcmp do porównywania struktur dla równości ze względu na potencjalne losowe wypełnianie znaków między polami w strukturach.
// bad
memcmp(&struct1, &struct2, sizeof(struct1));
Powyższe nie powiedzie się dla struktury takiej jak ta:
typedef struct Foo {
char a;
/* padding */
double d;
/* padding */
char e;
/* padding */
int f;
} Foo ;
Aby być bezpiecznym, musisz użyć porównania członków.
@Greg ma rację, że w ogólnym przypadku należy napisać wyraźne funkcje porównawcze.
Można użyć, memcmp
jeśli:
NaN
.-Wpadded
z clang, aby to sprawdzić) LUB struktury są jawnie inicjowane memset
przy inicjalizacji.BOOL
), które mają odrębne, ale równoważne wartości.O ile nie programujesz dla systemów wbudowanych (lub piszesz bibliotekę, która może być na nich użyta), nie martwiłbym się niektórymi przypadkami narożnymi w standardzie C. Rozróżnienie wskaźnika bliskiego i dalekiego nie istnieje na żadnym urządzeniu 32- lub 64-bitowym. Żaden znany mi system niewbudowany nie ma wielu NULL
wskaźników.
Inną opcją jest automatyczne generowanie funkcji równości. Jeśli rozłożysz definicje struktur w prosty sposób, możliwe jest użycie prostego przetwarzania tekstu do obsługi prostych definicji struktur. Możesz użyć libclang do ogólnego przypadku - ponieważ używa tej samej nakładki co Clang, poprawnie obsługuje wszystkie przypadki narożne (z wyjątkiem błędów).
Nie widziałem takiej biblioteki do generowania kodu. Wydaje się jednak stosunkowo prosty.
Jednak zdarza się również, że takie generowane funkcje równości często robią coś złego na poziomie aplikacji. Na przykład, czy dwie UNICODE_STRING
struktury w systemie Windows powinny być porównywane płytko czy głęboko?
memset
itp. Nie gwarantuje wartości bitów dopełniających po dalszym zapisywaniu do elementu struct, patrz: stackoverflow.com/q/52684192/689161
Pamiętaj, że możesz używać memcmp () na statycznych konstrukcjach bez obawy o wypełnianie, o ile nie zainicjujesz wszystkich elementów (jednocześnie). Jest to zdefiniowane przez C90:
{0, }
wyzeruje również bajty wypełniania?
Zależy to od tego, czy zadajesz pytanie:
Aby dowiedzieć się, czy są to ten sam obiekt, porównaj wskaźniki do dwóch struktur zapewniających równość. Jeśli chcesz dowiedzieć się ogólnie, czy mają taką samą wartość, musisz dokonać głębokiego porównania. Polega to na porównaniu wszystkich członków. Jeśli członkowie są wskaźnikami do innych struktur, musisz również powrócić do tych struktur.
W szczególnym przypadku, gdy struktury nie zawierają wskaźników, możesz wykonać memcmp, aby wykonać bitowe porównanie danych zawartych w każdym z nich bez konieczności poznania znaczenia danych.
Upewnij się, że wiesz, co oznacza „równa się” dla każdego elementu członkowskiego - jest to oczywiste dla liczb całkowitych, ale bardziej subtelne, jeśli chodzi o wartości zmiennoprzecinkowe lub typy zdefiniowane przez użytkownika.
memcmp
nie porównuje struktury, memcmp
porównuje plik binarny, a struktura zawsze zawiera śmieci, dlatego zawsze wychodzi Fałsz w porównaniu.
Porównaj element po elemencie, jest bezpieczny i nie zawiedzie.
Jeśli struktury zawierają tylko prymitywy lub jeśli jesteś zainteresowany ścisłą równością, możesz zrobić coś takiego:
int my_struct_cmp (const struct my_struct * lhs, const struct my_struct * rhs) { zwraca memcmp (lhs, rsh, sizeof (struct my_struct)); }
Jeśli jednak twoje struktury zawierają wskaźniki do innych struktur lub związków, będziesz musiał napisać funkcję, która odpowiednio porównuje prymitywy i odpowiednio wywoła porównania z innymi strukturami.
Pamiętaj jednak, że powinieneś był użyć memset (& a, sizeof (struct my_struct), 1), aby wyzerować zakres pamięci struktur w ramach inicjalizacji ADT.
W tym zgodnym przykładzie użyto rozszerzenia kompilatora pakietu #pragma z Microsoft Visual Studio, aby zapewnić możliwie ścisłe spakowanie elementów struktury:
#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))) {
/* ... */
}
}