Wie funktioniert das C offsetof Makro? [duplicate]
Lesezeit: 5 Minuten
Samuel Liew
Mögliches Duplikat:
Warum funktioniert dieser C-Code?
Wie verwenden Sie offsetof() für eine Struktur?
Ich habe im Internet über dieses offsetof-Makro gelesen, aber es erklärt nicht, wofür es verwendet wird.
#define offsetof(a,b) ((int)(&(((a*)(0))->b)))
Was versucht es zu tun und was ist der Vorteil seiner Verwendung?
Dass offsetof Makro ist falsch. Sie sollten zu werfen size_tnicht intund sie sollten wahrscheinlich subtrahieren (char*)0 aus dem Ergebnis vor dem Casting, obwohl es sich um eine Nullzeigerkonstante handelt.
– Chris Lutz
26. Oktober 2011 um 2:27 Uhr
Eamonn O’Brien-Stamm
R.. hat Recht mit seiner Antwort auf den zweiten Teil Ihrer Frage: Dieser Code wird bei Verwendung eines modernen C-Compilers nicht empfohlen.
Aber um den ersten Teil Ihrer Frage zu beantworten, was dies tatsächlich tut, ist:
Umwandeln des Werts Null in den Strukturzeigertyp a*
Abrufen des Struct-Felds b dieses (illegal platzierten) Struct-Objekts
Die Adresse davon bekommen b aufstellen
Casting der Adresse an eine int
Konzeptionell bedeutet dies, ein Struct-Objekt an der Speicheradresse Null zu platzieren und dann die Adresse eines bestimmten Felds herauszufinden. Auf diese Weise könnten Sie die Offsets im Speicher jedes Felds in einer Struktur ermitteln, sodass Sie Ihre eigenen Serialisierer und Deserialisierer schreiben könnten, um Strukturen in und aus Byte-Arrays zu konvertieren.
Wenn Sie tatsächlich einen Nullzeiger dereferenzieren würden, würde Ihr Programm natürlich abstürzen, aber eigentlich passiert alles im Compiler und kein tatsächlicher Nullzeiger wird zur Laufzeit dereferenziert.
In den meisten Originalsystemen lief C auf der Größe von an int war 32 Bit und war dasselbe wie ein Zeiger, also hat das tatsächlich funktioniert.
Exzellent! Vielen Dank. Der Schlüssel zu mir war placing a struct object at memory address zero and then finding out at what the address of a particular field is.
– Timur Fayzrakhmanov
17. Oktober 2018 um 10:43 Uhr
R.. GitHub HÖREN SIE AUF, ICE ZU HELFEN
Es hat keine Vorteile und sollte nicht verwendet werden, da es undefiniertes Verhalten hervorruft (und den falschen Typ verwendet – int Anstatt von size_t).
Der C-Standard definiert an offsetof Makro ein stddef.h was tatsächlich funktioniert, für Fälle, in denen Sie den Offset eines Elements in einer Struktur benötigen, wie zum Beispiel:
#include <stddef.h>
struct foo {
int a;
int b;
char *c;
};
struct struct_desc {
const char *name;
int type;
size_t off;
};
static const struct struct_desc foo_desc[] = {
{ "a", INT, offsetof(struct foo, a) },
{ "b", INT, offsetof(struct foo, b) },
{ "c", CHARPTR, offsetof(struct foo, c) },
};
wodurch Sie die Felder von a programmgesteuert ausfüllen können struct foo nach Namen, z. B. beim Lesen einer JSON-Datei.
Der Standard offsetof Makro aus stddef.h ruft UB nicht auf. Das Definieren Ihres eigenen Hacks zum Berechnen von Offsets auf diese Weise ruft UB auf.
– R.. GitHub HÖR AUF, EIS ZU HELFEN
26. Oktober 2011 um 2:24 Uhr
@ Adrian: Er hat nicht gesagt: “Das Definieren Ihrer eigenen Version des Makros führt zu undefiniertem Verhalten.” Er sagte ausdrücklich: „Definieren Sie Ihren eigenen Hack, um Offsets zu berechnen Hier entlang ruft UB auf.” Im Code an dieser Stelle: ((a*)(0))-> Sie haben undefiniertes Verhalten aufgerufen, indem Sie null dereferenziert haben.
– GManNickG
26. Oktober 2011 um 2:29 Uhr
@AdrianCornish: Die Umsetzung definieren darf offsetof aber es gefällt, solange es das richtige Verhalten implementiert. Ihre Bewerbung hat dieses Privileg nicht, weil es das Verhalten von nichts definieren kann; es kann nur bereits definierte Sprachkonstrukte verwenden. So funktioniert C.
– R.. GitHub HÖR AUF, EIS ZU HELFEN
26. Oktober 2011 um 2:42 Uhr
6.5.2.3 verwendet das Wort „Dereferenzierung“ nicht, sondern spezifiziert es als „das benannte Element des Objekts, auf das der erste Ausdruck zeigt“. Seit (a*)(0) zeigt nicht auf ein Objekt des Typs aist das Verhalten undefiniert (da es nicht definiert ist).
– R.. GitHub HÖR AUF, EIS ZU HELFEN
26. Oktober 2011 um 2:49 Uhr
Der zitierte Text ist irrelevant. Kein Zeiger auf einen unvollständigen oder Objekttyp wird in dem gefälschten Makro in einen Zeiger auf void umgewandelt.
– R.. GitHub HÖR AUF, EIS ZU HELFEN
26. Oktober 2011 um 2:57 Uhr
Es findet den Byte-Offset eines bestimmten Mitglieds von a struct. Wenn Sie beispielsweise die folgende Struktur hätten:
struct MyStruct
{
double d;
int i;
void *p;
};
Dann hättest du offsetOf(MyStruct, d) == 0, offsetOf(MyStruct, i) == 8und offsetOf(MyStruct, p) == 12 (d. h. das genannte Mitglied d ist 0 Byte vom Anfang der Struktur usw.).
Die Art und Weise, wie es funktioniert, ist, dass es vorgibt, dass eine Instanz Ihrer Struktur an der Adresse 0 existiert (die ((a*)(0)) Teil), und nimmt dann die Adresse des beabsichtigten Strukturmitglieds und wandelt sie in eine Ganzzahl um. Obwohl das Dereferenzieren eines Objekts bei Adresse 0 normalerweise ein Fehler wäre, ist es in Ordnung, die Adresse zu übernehmen, da der Adressoperator & und die Member-Dereferenzierung -> heben sich gegenseitig auf.
Es wird normalerweise für generalisierte Serialisierungsframeworks verwendet. Wenn Sie Code zum Konvertieren zwischen einer Art von Drahtdaten (z. B. Bytes in einer Datei oder aus dem Netzwerk) und In-Memory-Datenstrukturen haben, ist es oft praktisch, eine Zuordnung vom Member-Namen zum Member-Offset zu erstellen, damit Sie serialisieren können oder Werte generisch deserialisieren.
Die Frage ist C, wir müssen noch verwenden struct MyStruct. 😉
– Chris Lutz
26. Oktober 2011 um 2:29 Uhr
Adrian Kornisch
Die Implementierung des Offsetof-Makros ist wirklich irrelevant.
Der aktuelle C-Standard definiert es wie in 7.17.3:
offsetof(type, member-designator)
die zu einem ganzzahligen konstanten Ausdruck vom Typ size_t erweitert wird, dessen Wert der Offset in Bytes zum Strukturmitglied (festgelegt durch Mitgliedsbezeichner) vom Anfang seiner Struktur (festgelegt durch Typ) ist. Die Typ- und Elementbezeichnung muss wie angegeben sein static type t;.
Vertrauen Sie der Antwort von Adam Rosenfield.
R ist völlig falsch, und es hat viele Verwendungsmöglichkeiten – insbesondere um festzustellen, wann Code zwischen Plattformen nicht portierbar ist.
(OK, es ist C++, aber wir verwenden es in Behauptungen zur Kompilierzeit von statischen Vorlagen, um sicherzustellen, dass unsere Datenstrukturen die Größe zwischen Plattformen/Versionen nicht ändern.)
11443100cookie-checkWie funktioniert das C offsetof Makro? [duplicate]yes
Dass
offsetof
Makro ist falsch. Sie sollten zu werfensize_t
nichtint
und sie sollten wahrscheinlich subtrahieren(char*)0
aus dem Ergebnis vor dem Casting, obwohl es sich um eine Nullzeigerkonstante handelt.– Chris Lutz
26. Oktober 2011 um 2:27 Uhr