Jak porównać struktury równości w C?

Jak porównać dwie instancje struktur równości w standardzie C?

Author: Hans Sjunnesson, 2008-09-27

11 answers

C nie zapewnia żadnych udogodnień językowych, aby to zrobić - musisz to zrobić sam i porównać każdy członek struktury po członku.

 165
Author: Greg Hewgill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-26 20:22:14

Możesz ulec pokusie użycia memcmp(&a, &b, sizeof(struct foo)), ale może nie działać we wszystkich sytuacjach. Kompilator może dodać przestrzeń bufora wyrównania do struktury, a wartości Znalezione w miejscach pamięci leżących w przestrzeni bufora nie są gwarantowane jako żadna konkretna wartość.

Ale jeśli użyjesz calloc lub memset pełnego rozmiaru struktur przed ich użyciem, możesz zrobićpłytkie porównanie z memcmp (jeśli twoja struktura zawiera wskaźniki, będzie pasować tylko wtedy, gdy adres wskaźniki są wskazuje na to samo).

 95
Author: Sufian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-22 06:51:25

Jeśli robisz to dużo, sugerowałbym napisanie funkcji porównującej obie struktury. W ten sposób, jeśli kiedykolwiek zmienisz strukturę, wystarczy zmienić porównanie w jednym miejscu.

Jak to zrobić.... Musisz porównać każdy element indywidualnie

 19
Author: Ben,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-26 20:24:32

Nie można używać memcmp do porównywania struktur pod kątem równości z powodu potencjalnych losowych znaków wypełnienia między polami w strukturach.

  // bad
  memcmp(&struct1, &struct2, sizeof(struct1));

Powyższa struktura zawiedzie dla takiej struktury:

typedef struct Foo {
  char a;
  /* padding */
  double d;
  /* padding */
  char e;
  /* padding */
  int f;
} Foo ;

Aby być bezpiecznym, musisz użyć porównań między użytkownikami.

 17
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-26 20:34:12

Uwaga możesz używać memcmp() na statycznych stykach bez martwienie się o padding, o ile nie inicjalizujesz wszystkich członków (jednocześnie). Jest to zdefiniowane przez C90:

Http://www.pixelbeat.org/programming/gcc/auto_init.html

 5
Author: pixelbeat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-26 20:52:38

@Greg ma rację, że trzeba pisać jawne funkcje porównawcze w ogólnym przypadku.

Możliwe jest użycie memcmp Jeśli:

  • struktury nie zawierają pól zmiennoprzecinkowych, które mogą być NaN.
  • struktury nie zawierają wypełnienia (użyj -Wpadded z clang aby to sprawdzić) lub struktury są jawnie inicjalizowane za pomocą memset podczas inicjalizacji.
  • nie ma typów członkowskich (takich jak Windows BOOL), które mają różne, ale równoważne wartości.

Jeśli nie programujesz systemów wbudowanych (lub piszesz bibliotekę, która może być na nich używana), nie martwiłbym się o niektóre narożne przypadki w standardzie C. Różnica między wskaźnikiem bliskim a Dalekim nie istnieje na żadnym urządzeniu 32 - lub 64-bitowym. Żaden nie Wbudowany system, o którym wiem, nie ma wielu NULL wskaźników.

Inną opcją jest automatyczne generowanie funkcji równości. Jeśli zdefiniujesz strukturę w prosty sposób, możliwe jest użycie proste przetwarzanie tekstu do obsługi prostych definicji struktur. Możesz użyć libclang dla ogólnego przypadku – ponieważ używa tej samej nakładki co clang, obsługuje wszystkie przypadki narożne poprawnie (z wyjątkiem błędów).

Nie widziałem takiej biblioteki generującej kod. Wydaje się to jednak stosunkowo proste.

Jest jednak również tak, że takie generowane funkcje równości często robiłyby złe rzeczy na poziomie aplikacji. Na przykład, należy porównać dwie struktury UNICODE_STRING w systemie Windows płytko czy głęboko?

 5
Author: Demi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-23 19:16:03

memcmp memcmp nie porównuje struktury, memcmp porównuje binarne, a w strukturze zawsze są śmieci, dlatego zawsze wychodzi False w porównaniu.

Porównaj element po elemencie jego bezpieczny i nie zawiedzie.

 2
Author: sergio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-03 09:03:28

Jeśli struktury zawierają tylko pierwiastki lub jeśli interesuje Cię ścisła równość, możesz zrobić coś takiego:

int my_struct_cmp(const struct my_struct * lhs, const struct my_struct * rhs)
{
    return memcmp(lhs, rsh, sizeof(struct my_struct));
}

Jednakże, jeśli Twoje struktury zawierają wskaźniki do innych struktur lub związków, będziesz musiał napisać funkcję, która porównuje primitives odpowiednio i wykonać wywołania porównawcze z innymi strukturami.

Pamiętaj jednak, że powinieneś użyć memset(&a, sizeof(struct my_struct), 1), aby zerować zakres pamięci struktury jako część inicjalizacji ADT.

 1
Author: Kevin S.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-26 20:34:40

Zależy od tego, czy pytanie, które zadajesz, brzmi:

  1. czy te dwie struktury to ten sam obiekt?
  2. Czy mają taką samą wartość?

Aby sprawdzić, czy są one tym samym obiektem, porównaj wskaźniki do dwóch struktur równości. Jeśli chcesz dowiedzieć się ogólnie, czy mają taką samą wartość, musisz zrobić głębokie porównanie. Polega to na porównaniu wszystkich członków. Jeśli członkowie są wskaźnikami do innych struktur, musisz rekurencyjnie wprowadzić je do tych struktur też.

W szczególnym przypadku, gdy struktury nie zawierają wskaźników, można wykonać memcmp, aby wykonać bitowe porównanie danych zawartych w każdej z nich bez konieczności wiedzy, co oznaczają dane.

Upewnij się, że wiesz, co oznacza 'equals' dla każdego elementu - jest to oczywiste dla Int, ale bardziej subtelne, jeśli chodzi o wartości zmiennoprzecinkowe lub typy zdefiniowane przez użytkownika.

 1
Author: domgblackwell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-27 14:41:58

Jeśli zmienna 2 structures jest inicjalizowana przez calloc lub jest ustawiona na 0 przez memset, więc możesz porównać swoje 2 struktury z memcmp i nie ma obaw o śmieci struktury, a to pozwoli Ci zarobić czas

 -1
Author: MOHAMED,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-03 09:11:00

Ten zgodny przykład wykorzystuje rozszerzenie kompilatora pakietów # pragma z Microsoft Visual Studio, aby zapewnić, że elementy struktury są pakowane tak szczelnie, jak to możliwe:

#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))) {
    /* ... */
  }
}
 -2
Author: Hesham Eraqi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-30 17:06:41