Wie die meisten C-Programmierer wissen, können Sie zwei Strukturen nicht direkt vergleichen.
In Betracht ziehen:
void isequal(MY_STRUCT a, MY_STRUCT b)
{
if (a == b)
{
puts("equal");
}
else
{
puts("not equal");
}
}
Das a==b Der Vergleich wird AFAIK bei jedem vernünftigen C-Compiler einen Kompilierungsfehler auslösen, da der C-Standard keinen integrierten Strukturvergleich zulässt. Problemumgehungen mit memcmp sind aufgrund von Ausrichtung, Packen, Bitfeldern usw. natürlich eine schlechte Idee, sodass wir letztendlich Element-für-Element-Vergleichsfunktionen schreiben.
Andererseits ermöglicht es eine Strukturzuweisung, z a = b ist völlig legal. Der Compiler kommt damit offensichtlich ziemlich trivial zurecht, also warum nicht vergleichen?
Die einzige Idee, die ich hatte, war, dass die Strukturzuweisung wahrscheinlich ziemlich nah an memcpy() liegt, da die Lücken aufgrund von Ausrichtung usw. keine Rolle spielen. Auf der anderen Seite könnte ein Vergleich komplizierter sein. Oder ist das etwas, das ich vermisse?
Offensichtlich ist mir bewusst, dass ein einfacher Element-für-Element-Vergleich nicht unbedingt ausreicht, zB wenn die Struktur einen Zeiger auf eine Zeichenfolge enthält, aber es gibt Umstände, unter denen es nützlich wäre.
Vote to close: Ich denke, das ist eine wirklich interessante Frage, aber gleichzeitig denke ich, dass Sie wahrscheinlich keine Antworten erhalten werden, die keine bloße Spekulation sind.
– Oliver Charlesworth
24. August 2011 um 19:55 Uhr
stackoverflow.com/questions/141720/…
– Ciro Santilli Путлер Капут 六四事
2. Juli 2015 um 7:54 Uhr
Wie würden Sie mit void-Zeigern umgehen, wenn jede Struktur Speicher mit zwei verschiedenen Aufrufen von malloc belegt
– iss_eine_Zitrone
29. Mai 2017 um 18:48 Uhr
Void-Zeiger können auf Gleichheit verglichen werden, wenn sie eigenständig sind. Wenn eine Struktur einen void-Zeiger enthält, der sich von einem void-Zeiger in einer anderen Struktur unterscheidet, würde ich erwarten, dass sie als ungleich verglichen werden.
– WillW
29. Mai 2017 um 22:03 Uhr
Wie andere bereits erwähnt haben, hier ein Auszug aus C: A Reference Manual von Harbison und Steele:
Strukturen und Vereinigungen können nicht auf Gleichheit verglichen werden, obwohl die Zuweisung für diese Typen erlaubt ist. Die durch Ausrichtungsbeschränkungen verursachten Lücken in Strukturen und Vereinigungen könnten willkürliche Werte enthalten, und deren Kompensation würde dem Gleichheitsvergleich oder allen Operationen, die Struktur- und Vereinigungstypen ändern, einen inakzeptablen Overhead auferlegen.
Würden die möglichen Lücken in den Strukturen für die Ausrichtung nicht genau die gleichen Performance-Probleme beim Zuweisen zweier Strukturen verursachen wie beim Vergleichen?
– jzimmermann2
29. Oktober 2017 um 2:56 Uhr
Ich persönlich kaufe dieses Argument nicht, aber ich denke, die Argumentation ist die folgende: Zum Kopieren/Zuweisen einer Struktur ist der generierte Code ziemlich einfach, obwohl potenziell langsam: Er muss nur den gesamten Speicherbereich der Struktur kopieren und kann enthalten die undefinierten Inhalte der Lücken. Beim Vergleich müssten die Lücken von den tatsächlichen Feldern erkannt werden (wenn dies das gewünschte Verhalten ist), was komplizierteren Code erfordern würde.
– stefankt
30. Oktober 2017 um 7:54 Uhr
Cnicutar
Der Vergleich wird aus demselben Grund nicht unterstützt memcmp scheitert.
Aufgrund von Füllfeldern würde der Vergleich auf unvorhersehbare Weise fehlschlagen, was für die meisten Programmierer nicht akzeptabel wäre. Die Zuweisung ändert die unsichtbaren Füllfelder, aber diese sind sowieso unsichtbar, also nichts Unerwartetes.
Offensichtlich fragen Sie sich vielleicht: Warum füllt es nicht einfach alle Füllfelder mit Nullen? Sicher, das würde funktionieren, aber es würde auch dazu führen, dass alle Programme für etwas bezahlen, das sie möglicherweise nicht benötigen.
BEARBEITEN
Oli Charlesworth merkt in den Kommentaren an, dass Sie vielleicht fragen: „Warum generiert der Compiler keinen Code für den Vergleich von Mitglied zu Mitglied?“. Wenn dem so ist, muss ich gestehen: Ich weiß nicht :-). Der Compiler hätte alle benötigten Informationen, wenn er nur den Vergleich vollständiger Typen erlauben würde.
Das OP schlägt ausdrücklich vor nicht benutzen memcmpsondern damit der Compiler automatisch Code für den Member-by-Member-Vergleich generiert.
– Oliver Charlesworth
24. August 2011 um 16:53 Uhr
@Oli Charlesworth Vielleicht hast du Recht, aber ich habe es nicht als “warum macht der Compiler es nicht Mitglied für Mitglied” gelesen. Ich glaube nicht, dass es darauf eine einfache Antwort gibt, der Compiler hat sicherlich alle Informationen, die er braucht, wenn er nur den Vergleich zwischen vollständigen Typen zulässt.
– Cnicutar
24. August 2011 um 16:57 Uhr
Zusätzlich zu Füllbits kann es in einigen Implementierungen auch Probleme mit positiven und negativen Nullen geben. Wenn es zwei Strukturen gibt, die identisch sind, außer dass die eine eine positive Null hat, während die andere eine negative Null hat, kann es Fälle geben, in denen sie als gleich angesehen werden sollten, und andere Fälle, in denen dies nicht der Fall sein sollte. Es wäre schön, eine Syntax zu haben, um den Compiler aufzufordern, einen Memberwise-Vergleich durchzuführen (den er dann durch einen Speichervergleich ersetzen könnte, wenn dies funktionieren würde), aber ich bin mir nicht sicher, ob ich == oder != dafür möchte diesen Zweck.
– Superkatze
24. August 2011 um 17:02 Uhr
Aber was genau ist ein Member-by-Member-Vergleich? Sind zwei Structs, die Strings enthalten, nicht gleich, wenn die Strings übereinstimmen, aber nicht identisch sind? Ich denke, das Komitee wollte solche Probleme vermeiden und den Benutzer den Vergleich durchführen lassen, da der Benutzer am besten weiß, was zu vergleichen ist.
– Rudy Velthuis
24. August 2011 um 17:31 Uhr
@Rudy Velthuis: Der natürlichste Weg wäre, es so zu definieren, dass jedes entsprechende Mitglied mit demselben Operator verglichen wird (== oder !=). Dies würde bedeuten, dass, wenn Ihre Strukturen Zeiger auf Zeichenfolgen enthalten, die Zeiger (und nicht das, worauf sie zeigen) auf Gleichheit verglichen würden. Sie müssten auch definieren == und != für Array-Typen vermutlich auf ähnliche Weise durch Vergleichen jedes entsprechenden Elements. Der wahre Grund, warum es nicht vorhanden ist, liegt wahrscheinlich darin, dass der Vergleich von Strukturen auf strikte Gleichheit nur selten benötigt wird.
Der C89-Ausschuss erwog mehr als einmal, den Vergleich von Strukturen für die Gleichstellung zuzulassen. Solche Vorschläge scheiterten am Problem von Löchern in Strukturen. Ein byteweiser Vergleich zweier Strukturen würde erfordern, dass die Löcher sicher auf Null gesetzt werden, so dass alle Löcher gleich verglichen werden, eine schwierige Aufgabe für automatisch oder dynamisch zugewiesene Variablen.
Die Möglichkeit von Elementen vom Vereinigungstyp in einer Struktur wirft bei diesem Ansatz unüberwindbare Probleme auf. Ohne die Zusicherung, dass alle Löcher auf Null gesetzt wurden, müsste die Implementierung darauf vorbereitet sein, einen Strukturvergleich in eine beliebige Anzahl von Elementvergleichen aufzuteilen; ein scheinbar einfacher Ausdruck könnte sich somit zu einer beträchtlichen Codestrecke ausdehnen, was dem Geist von C widerspricht
Im Klartext: Da Structs/Unions Füllbytes enthalten können und das Komitee nicht erzwungen hatte, dass diese bestimmte Werte enthalten, würden sie diese Funktion nicht implementieren. Denn wenn alle Füllbytes auf Null gesetzt werden müssten, würde dies zusätzlichen Laufzeit-Overhead erfordern.
Ich persönlich würde diese Begründung als Unsinn bezeichnen, da der C-Standard bereits andere sehr ähnliche “ressourcenlastige” Anforderungen hat: – Structs/Unions mit statischer Speicherdauer müssen Füllbytes auf Null gesetzt haben (C11 6.7.9/10). – Alle nicht initialisierten Member einer teilweise initialisierten Struktur müssen auf Null gesetzt werden (C11 6.7.9/19).
– Ludin
1. November 2017 um 14:13 Uhr
Nein, diese Begründung ist völlig konsequent. Die Initialisierung statischer Dinge auf Null ist billig und wird nur einmal beim Start durchgeführt. Dies ist sehr billig, da das gesamte Speichersegment auf Null gesetzt wird (bevor ein Teil davon initialisiert wird), was Sie entweder aus Sicherheitsgründen (gehostete Systeme) oder von Ihrer Hardware beim Booten (eingebettete Systeme) tun möchten. Aus dem gleichen Grund werden statische Variablen auf Null initialisiert und lokale Variablen nicht – die Leistung von Dingen, die wiederholt zur Laufzeit ausgeführt werden, ist viel wichtiger.
– WillW
2. November 2017 um 10:09 Uhr
@WillW Nein, .bss wird auf eingebetteten Systemen nicht “durch Hardware initialisiert” … Und mein erstes Argument zur Nullinitialisierung von teilweise initialisierten Strukturen gilt sowohl für lokale Variablen als auch für statische Speicherdauer – dies wird nicht unbedingt von . bss copy-down, könnte aber genauso gut zur Laufzeit passieren.
– Ludin
2. November 2017 um 14:13 Uhr
Obwohl dies keine so allgemeine Regel ist, wie ich es klingen lasse, gibt eine beträchtliche Anzahl von eingebetteten Zielen an, dass alle Nicht-Register-Speicheradressen beim Zurücksetzen Null sind.
– WillW
2. November 2017 um 17:16 Uhr
vromanov
Vergleichsoperator automatisch generieren ist eine schlechte Idee. Stellen Sie sich vor, wie der Vergleich für diese Struktur funktionieren würde:
struct s1 {
int len;
char str[100];
};
Dies ist ein Pascal-ähnlicher String mit einer maximalen Länge von 100
Ein anderer Fall
struct s2 {
char a[100];
}
Wie kann der Compiler wissen, wie ein Feld zu vergleichen ist? Wenn es sich um eine NUL-terminierte Zeichenfolge handelt, muss der Compiler strcmp oder strncmp verwenden. Wenn dies ein Char-Array ist, muss der Compiler memcmp verwenden.
Um die vorhandenen guten Antworten zu ergänzen:
struct foo {
union {
uint32_t i;
float f;
} u;
} a, b;
a.u.f = -0.0;
b.u.f = 0.0;
if (a==b) // what to do?!
Das Problem ergibt sich inhärent daraus, dass Gewerkschaften nicht in der Lage sind, zu speichern/nachzuverfolgen, welches Mitglied aktuell ist.
Das ist nur ein Problem, weil der Vergleich von Strukturen von vornherein nicht spezifiziert ist (dh der (rekursive) Vergleich von Strukturen, die Unions enthalten, müsste von der Spezifikation angegangen werden, z. B. durch Deklaration als UB), aber es ist ein sehr interessanter Aspekt, danke.
– stefankt
6. Juli 2018 um 12:43 Uhr
Dies ist IMO die richtige Antwort: Gewerkschaften sind der wahre Grund, warum Sie keinen Code für Gleichheit generieren können, nicht für den Laufzeitaufwand.
– Benutzer519179
27. August 2019 um 12:57 Uhr
Das ist nur ein Problem, weil der Vergleich von Strukturen von vornherein nicht spezifiziert ist (dh der (rekursive) Vergleich von Strukturen, die Unions enthalten, müsste von der Spezifikation angegangen werden, z. B. durch Deklaration als UB), aber es ist ein sehr interessanter Aspekt, danke.
– stefankt
6. Juli 2018 um 12:43 Uhr
Dies ist IMO die richtige Antwort: Gewerkschaften sind der wahre Grund, warum Sie keinen Code für Gleichheit generieren können, nicht für den Laufzeitaufwand.
– Benutzer519179
27. August 2019 um 12:57 Uhr
11763600cookie-checkWarum bietet C keinen Strukturvergleich?yes
Vote to close: Ich denke, das ist eine wirklich interessante Frage, aber gleichzeitig denke ich, dass Sie wahrscheinlich keine Antworten erhalten werden, die keine bloße Spekulation sind.
– Oliver Charlesworth
24. August 2011 um 19:55 Uhr
stackoverflow.com/questions/141720/…
– Ciro Santilli Путлер Капут 六四事
2. Juli 2015 um 7:54 Uhr
Wie würden Sie mit void-Zeigern umgehen, wenn jede Struktur Speicher mit zwei verschiedenen Aufrufen von malloc belegt
– iss_eine_Zitrone
29. Mai 2017 um 18:48 Uhr
Void-Zeiger können auf Gleichheit verglichen werden, wenn sie eigenständig sind. Wenn eine Struktur einen void-Zeiger enthält, der sich von einem void-Zeiger in einer anderen Struktur unterscheidet, würde ich erwarten, dass sie als ungleich verglichen werden.
– WillW
29. Mai 2017 um 22:03 Uhr