C-Zeiger-Arithmetik ohne Strukturobjekt

Lesezeit: 6 Minuten

Ich denke, es ist in C nicht möglich, aber bitten Sie, dies zu überprüfen. Ist es möglich, zwischen Strukturmitgliedern ohne reelle Variable dieses Typs zu rechnen? Zum Beispiel:

typedef struct _s1
{
  int a;
  int b;
  int c;
} T1;

Ich möchte den Versatz des “c”-Mitglieds im Vergleich zum Strukturanfang sehen. Es ist einfach, wenn ich eine Variable habe:

T1 str;

int dist = (int)&str.c - (int)&str;

Aber meine Struktur ist zu groß und hat kein Mitglied im RAM (nur im EEPROM). Und ich möchte einige Adressberechnungen durchführen, aber kein RAM-Mitglied definieren. Ich kann den Job mit Strukturzeiger statt Strukturvariable machen (es kostet nur 4 Bytes), aber der Fall ist für mich interessant.

  • Falschen Zeiger definieren und sehen?

    – Cthulhu

    30. Januar 2014 um 11:38 Uhr

  • Dies wurde hier wahrscheinlich schon zigmal beantwortet und anderswo diskutiert. Ich denke, die Antwort hätte eine Google-Suche entfernt sein sollen. Hast du das gegoogelt?

    – Bedeutungsangelegenheiten

    30. Januar 2014 um 11:41 Uhr


  • @EliasVanOotegem In Anbetracht der Erwähnung von EEPROM besteht eine gute Chance, dass dies auf einem eingebetteten System ausgeführt wird. Soweit wir wissen, könnte es sich um ein 8-Bit- oder 16-Bit-System handeln.

    – Bob

    30. Januar 2014 um 14:04 Uhr


  • Abgesehen davon: Ihr Beispielcode sollte wahrscheinlich sein (char*)&str.c - (char*)&str. Ich habe keine Referenz zur Hand, aber ich glaube nicht, dass Sie wirklich garantiert sind, dass Sie Hinweise darauf geben int und dann führt das Rechnen zu korrekten Ergebnissen. (Sie sollten auch verwenden intptr_t oder uintptr_t wenn Sie Zeiger auf ganze Zahlen umwandeln)

    Benutzer1084944

    30. Januar 2014 um 18:54 Uhr


  • @meaning-matters Wenn Sie das denken, finden Sie heraus, wo es beantwortet wurde, und stimmen Sie ab, um es als Duplikat zu schließen.

    – Sternchen

    31. Januar 2014 um 20:50 Uhr

Benutzeravatar von pmr
pmr

Verwenden Offsetvon Sie können Berechnungen zwischen Mitgliedern durchführen, ohne sich eine Variable dieses Typs besorgen zu müssen. Alles, was Sie brauchen, ist der Typ selbst und der Name des Members.

Ein Hinweis, warum einfache Berechnungen wahrscheinlich nicht funktionieren: Datenabgleich. Sie wissen nicht, wie viel Padding Ihr Compiler auf Ihre Struktur werfen wird, und dies kann zu sehr subtilen Fehlern führen oder scheinbar korrekte Berechnungen falsch machen, wenn Sie die Struktur ändern.

Benutzeravatar von Elias Van Ootegem
Elias Van Ootegem

Zunächst einmal werfen Sie die einzelnen Adressen an ints. Ich glaube nicht, dass das eine so gute Idee ist. ein int garantiert mindestens 2 Bytes oder mehr groß ist, ist eine Adresse/ein Zeiger im Allgemeinen 4 oder 8 Bytes groß. Gießen Sie diese an int sitzt einfach nicht richtig. Wenn ich sie auf irgendetwas werfen müsste, würde ich wahrscheinlich verwenden unsigned long für 32-Bit-Systeme und unsigned long long für 64bit. Letzteres würde ich sicherheitshalber nehmen.
Das heißt, ich würde die Adressen überhaupt nicht umwandeln und nur die verwenden offsetof Makro.

Hinzufügen #include <stddef.h> zu Ihrer Datei, und verwenden Sie die offsetof Makro, das den Offset-Wert in umwandelt size_t Typ, kein int, wohlgemerkt. Es funktioniert einfach, indem es 0 als Speicheradresse der Struktur verwendet und dann die Adresse des angegebenen Elements zurückgibt, wobei der tatsächliche Offset angegeben wird:

size_t offset = offsetoff(T1, c);
                    //struct, member

Wie Zack in seinem Kommentar betonte, ist der nächste Teil der Antwort etwas irrelevant, aber wegen der darin enthaltenen Links und der Vollständigkeit halber lasse ich ihn einfach hier – als offsetof wird von allen Implementierungen benötigt:

Definieren Sie das Makro selbst, wenn Sie aus irgendeinem Grund keine stddef.h haben (was es haben sollte, weil offsetof seit einiger Zeit Teil des Standards: C89 und höher), etwa so:

#ifndef offsetof
#define offsetof(s, m) ((size_t)&(((s *) 0)->m))
#endif

Das sollte funktionieren, aber seien Sie vorsichtig: Diese Implementierung kann zu undefiniertem Verhalten führen. Wie und warum wird hier erklärt
Ich habe die genommen offsetof obige Definition aus die Quelle stddef.h habe ich hier gefundenalso können Sie diese Datei einfach herunterladen und verwenden, anstatt das Makro in Ihrer eigenen Datei zu definieren, aber denken Sie daran, dass die offsetof Sie verwenden, ist nicht 100% zuverlässig.

So, jetzt reicht es erstmal, mehr Infos bei Google, aber hier ein paar Links:

  • Definieren offsetof ist ein bisschen gefährlich. Die von Ihnen gezeigte Implementierung ist ein Grenzfall von undefiniertem Verhalten (ich kann eine Referenz dafür ausgraben), aber es ist in Ordnung, dies für einen Implementierer zu tun, da er weiß (oder sicherstellen muss), wie sich der Compiler verhält.

    – pmr

    30. Januar 2014 um 11:52 Uhr

  • @pmr: Ich habe meiner Antwort einige Links dazu hinzugefügt, danke, dass du mich darauf hingewiesen hast. Ich wusste nichts davon offsetof‘s Macken (meine Hauptquelle für C “Wissen” ist K&R, das das Makro überhaupt nicht erwähnt). trotzdem +1 zu Ihrer Antwort für den Hinweis auf die Datenausrichtung an das OP

    – Elias Van Ootegem

    30. Januar 2014 um 12:06 Uhr


  • Es gibt keine Entschuldigung dafür, dass eine Implementierung nicht bereitgestellt wird stddef.hmit offsetof darin, heutzutage – es ist seit C89 für alle Implementierungen erforderlich, ob freistehend oder nicht.

    – zol

    30. Januar 2014 um 16:13 Uhr

Sie können verwenden offsetof definiert in stddef.h.

Ich weiß, das geht über das hinaus, was Sie gefragt haben, aber es gibt eine interessante Verwendung von offsetof Wird zum Beispiel im Linux-Kernel-Code verwendet, der container_of Makro. container_of kann Ihnen die Adresse der übergeordneten Struktur zurückgeben, wenn Sie einen Zeiger auf eines ihrer Mitglieder erhalten:

#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})

Das können Sie zum Beispiel verwenden:

// pointer_to_c points to a member of the struct, c in this case
// and you want to get the address of the container
T1 *container;
container = container_of(pointer_to_c, T1, c);

  • Ich mag container_of Makro, aber die (char *) Besetzung sieht ein bisschen aus Erbe zu mir, wenn es Ihnen nichts ausmacht, wenn ich sage. Ich denke, heutzutage würden wir einen void-Zeiger verwenden

    – Elias Van Ootegem

    30. Januar 2014 um 11:54 Uhr

  • @EliasVanOotegem Ich denke, dass (char *) notwendig ist, da später __mptr für die Zeigerarithmetik verwendet wird. Es muss genau um offsetof(…) Bytes dekrementiert werden. Sie sollten niemals Arithmetik für Viud-Zeiger verwenden stackoverflow.com/questions/3523145/…

    – fede1024

    30. Januar 2014 um 12:01 Uhr

  • Guter Punkt … Ich habe es gerade gesehen (char *)__mptr hielt nicht an, um die Auswirkungen von zu betrachten void * - size_t 🙂

    – Elias Van Ootegem

    30. Januar 2014 um 12:09 Uhr

das ist der Benutzer-Avatar
Dies

stddef.h hat ein Makro namens Offsetvon was den Versatz eines Mitglieds vom Anfang einer Struktur angibt.

size_t t = offsetof( T1 , c ) ;

Auf diese Weise müssen Sie keine Strukturvariablen deklarieren.

Schauen Sie sich um Ausrichtung der Datenstruktur

Das ist richtig:

size_t dist = offsetof(struct _s1, c);

Benutzeravatar von Anthony
Antonius

In der Kopfzeile stddef.h es gibt ein Makro; offsetof. Sie können es wie folgt für Ihre Struktur verwenden:

int dist = (int)offsetof(T1, c);

1388420cookie-checkC-Zeiger-Arithmetik ohne Strukturobjekt

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

Privacy policy