Wie funktioniert das C offsetof Makro? [duplicate]

Lesezeit: 5 Minuten

Benutzer-Avatar
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

Benutzer-Avatar
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:

(
  (int)(         // 4.
    &( (         // 3.
      (a*)(0)    // 1.
     )->b )      // 2.
  )
)

Von innen nach außen arbeiten, das ist …

  1. Umwandeln des Werts Null in den Strukturzeigertyp a*
  2. Abrufen des Struct-Felds b dieses (illegal platzierten) Struct-Objekts
  3. Die Adresse davon bekommen b aufstellen
  4. 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


Benutzer-Avatar
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

Benutzer-Avatar
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.)

1144310cookie-checkWie funktioniert das C offsetof Makro? [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy