Wie vergleichen Sie zwei Instanzen von Strukturen auf Gleichheit in Standard C?
Wie vergleichen Sie zwei Instanzen von Strukturen auf Gleichheit in Standard C?
Antworten:
C bietet hierfür keine Sprachfunktionen - Sie müssen dies selbst tun und jede Struktur Mitglied für Mitglied vergleichen.
0.0, -0.0 NaN
ist ein Problem mit memcmp()
. Zeiger, die sich in der binären Darstellung unterscheiden, können auf dieselbe Position zeigen (z. B. DOS: seg: offset) und sind daher gleich. Einige Systeme haben mehrere Nullzeiger, die sich gleichermaßen vergleichen lassen. Gleiches gilt für Obskur int
mit -0- und Gleitkommatypen mit redundanten Codierungen. (Intel Long Double, Decimal64 usw.) Diese Probleme machen keinen Unterschied, ob sie calloc()
verwendet werden oder nicht.
Sie könnten versucht sein, es zu verwenden memcmp(&a, &b, sizeof(struct foo))
, aber es funktioniert möglicherweise nicht in allen Situationen. Der Compiler kann einer Struktur einen Ausrichtungspufferraum hinzufügen, und es wird nicht garantiert, dass die Werte, die an Speicherstellen gefunden werden, die im Pufferraum liegen, ein bestimmter Wert sind.
Wenn Sie jedoch die Strukturen calloc
oder memset
die volle Größe der Strukturen verwenden, bevor Sie sie verwenden, können Sie einen flachen Vergleich mit durchführen memcmp
(wenn Ihre Struktur Zeiger enthält, stimmt sie nur überein, wenn die Adresse, auf die die Zeiger zeigen, identisch ist).
memcmp
vorausgesetzt, der Speicher wurde zuerst gelöscht. Welches ist nah an der Arbeit, aber nicht korrekt. Natürlich definiert die Frage auch nicht "Gleichheit". Wenn Sie also "byteweise Gleichheit der Objektdarstellung" meinen, dann memcmp
tut dies genau das (ob der Speicher gelöscht wird oder nicht).
Wenn Sie viel tun, würde ich vorschlagen, eine Funktion zu schreiben, die die beiden Strukturen vergleicht. Auf diese Weise müssen Sie den Vergleich nur an einer Stelle ändern, wenn Sie jemals die Struktur ändern.
Wie es geht ... Sie müssen jedes Element einzeln vergleichen
Sie können memcmp nicht verwenden, um Strukturen auf Gleichheit zu vergleichen, da möglicherweise zufällige Auffüllzeichen zwischen Feldern in Strukturen vorhanden sind.
// bad
memcmp(&struct1, &struct2, sizeof(struct1));
Das Obige würde für eine Struktur wie diese fehlschlagen:
typedef struct Foo {
char a;
/* padding */
double d;
/* padding */
char e;
/* padding */
int f;
} Foo ;
Sie müssen einen Vergleich nach Mitgliedern verwenden, um sicher zu sein.
@ Greg ist richtig, dass man im allgemeinen Fall explizite Vergleichsfunktionen schreiben muss.
Es ist möglich zu verwenden, memcmp
wenn:
NaN
.-Wpadded
mit clang, um dies zu überprüfen) ODER die Strukturen werden memset
bei der Initialisierung explizit mit initialisiert.BOOL
) mit unterschiedlichen, aber äquivalenten Werten.Wenn Sie nicht für eingebettete Systeme programmieren (oder eine Bibliothek schreiben, die möglicherweise auf ihnen verwendet wird), würde ich mir keine Gedanken über einige der Eckfälle im C-Standard machen. Die Nah- und Fernzeigerunterscheidung existiert auf keinem 32- oder 64-Bit-Gerät. Kein mir bekanntes nicht eingebettetes System verfügt über mehrere NULL
Zeiger.
Eine andere Möglichkeit besteht darin, die Gleichheitsfunktionen automatisch zu generieren. Wenn Sie Ihre Strukturdefinitionen auf einfache Weise anordnen, können Sie mithilfe einfacher Textverarbeitung einfache Strukturdefinitionen verarbeiten. Sie können libclang für den allgemeinen Fall verwenden - da es dasselbe Frontend wie Clang verwendet, werden alle Eckfälle korrekt behandelt (mit Ausnahme von Fehlern).
Ich habe eine solche Codegenerierungsbibliothek nicht gesehen. Es erscheint jedoch relativ einfach.
Es ist jedoch auch der Fall, dass solche generierten Gleichheitsfunktionen auf Anwendungsebene häufig das Falsche tun. UNICODE_STRING
Sollten beispielsweise zwei Strukturen in Windows flach oder tief verglichen werden?
memset
usw. garantiert nicht den Wert der Füllbits nach dem weiteren Schreiben in ein Strukturelement, siehe: stackoverflow.com/q/52684192/689161
Beachten Sie, dass Sie memcmp () für nicht statische Strukturen verwenden können, ohne sich Gedanken über das Auffüllen machen zu müssen, solange Sie nicht alle Mitglieder (auf einmal) initialisieren. Dies wird durch C90 definiert:
{0, }
auch Auffüllbytes auf Null gesetzt werden?
Es hängt davon ab, ob die Frage, die Sie stellen, lautet:
Um herauszufinden, ob es sich um dasselbe Objekt handelt, vergleichen Sie die Zeiger auf die beiden Strukturen, um die Gleichheit zu gewährleisten. Wenn Sie allgemein herausfinden möchten, ob sie den gleichen Wert haben, müssen Sie einen gründlichen Vergleich durchführen. Dies beinhaltet den Vergleich aller Mitglieder. Wenn die Mitglieder Zeiger auf andere Strukturen sind, müssen Sie auch in diese Strukturen zurückgreifen.
In dem speziellen Fall, in dem die Strukturen keine Zeiger enthalten, können Sie ein memcmp ausführen, um einen bitweisen Vergleich der jeweils enthaltenen Daten durchzuführen, ohne wissen zu müssen, was die Daten bedeuten.
Stellen Sie sicher, dass Sie wissen, was "gleich" für jedes Mitglied bedeutet - es ist für Ints offensichtlich, aber subtiler, wenn es um Gleitkommawerte oder benutzerdefinierte Typen geht.
memcmp
vergleicht die Struktur nicht, memcmp
vergleicht die Binärdatei und es gibt immer Müll in der Struktur, daher kommt sie im Vergleich immer als falsch heraus.
Vergleichen Sie Element für Element, es ist sicher und schlägt nicht fehl.
Wenn die Strukturen nur Grundelemente enthalten oder wenn Sie an strikter Gleichheit interessiert sind, können Sie Folgendes tun:
int my_struct_cmp (const struct my_struct * lhs, const struct my_struct * rhs) { return memcmp (lhs, rsh, sizeof (struct my_struct)); }}
Wenn Ihre Strukturen jedoch Zeiger auf andere Strukturen oder Vereinigungen enthalten, müssen Sie eine Funktion schreiben, die die Grundelemente ordnungsgemäß vergleicht, und gegebenenfalls Vergleichsaufrufe mit den anderen Strukturen durchführen.
Beachten Sie jedoch, dass Sie memset (& a, sizeof (struct my_struct), 1) verwendet haben sollten, um den Speicherbereich der Strukturen im Rahmen Ihrer ADT-Initialisierung auf Null zu setzen.
In diesem kompatiblen Beispiel wird die Compiler-Erweiterung #pragma pack von Microsoft Visual Studio verwendet, um sicherzustellen, dass die Strukturelemente so dicht wie möglich gepackt sind:
#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))) {
/* ... */
}
}