Warum die Adresse des ersten Elements von struct verwenden, anstatt struct selbst?

Lesezeit: 4 Minuten

Ich bin gerade bei der Arbeit auf eine weitere Codebasis gestoßen, bei der Entwickler beim Kopieren/Vergleichen/Festlegen konsequent die Adresse des ersten Elements von Strukturen verwenden und nicht die Struktur selbst. Hier ist ein einfaches Beispiel.

Zuerst gibt es einen Strukturtyp:

typedef struct {
    int a;
    int b;
} foo_t;

Dann gibt es eine Funktion, die eine Kopie einer solchen Struktur erstellt:

void bar(foo_t *inp)
{
    foo_t l;
    ...
    memcpy(&l.a, &inp->a, sizeof(foo_t));
    ...
}

Ich selbst würde keinen Anruf schreiben memcpy auf diese Weise und ich begann mit dem Verdacht, dass die ursprünglichen Entwickler Zeiger und Strukturen in C einfach nicht ganz verstanden haben. Jetzt habe ich dies jedoch in zwei unabhängigen Codebasen gesehen, ohne gemeinsame Entwickler, also fange ich an zu zweifeln mich selbst.

Warum sollte man diesen Stil verwenden wollen?

  • Keine der folgenden Antworten hat meinem Wissen über C tatsächlich etwas hinzugefügt, was ich mir erhofft hatte;)

    – Magnus

    4. November 2013 um 21:37 Uhr

  • Dies ist insbesondere dann ziemlich nützlich, wenn mehrere Arten von Strukturen mit demselben ersten Element beginnen, als eine Art Pseudo-Vererbung. Die CPython-Codebasis verwendet dies stark mit PyObject; Alle Python-Objekte beginnen mit a PyObject Mitglied, und sie werden normalerweise herumgereicht PyObject * Zeiger, sodass Sie den tatsächlichen Typ des Objekts, mit dem Sie arbeiten, nicht fest codieren müssen.

    – Benutzer2357112

    5. November 2013 um 4:23 Uhr


  • Das memcpy wird sicher, wenn dies das letzte Argument ist: sizeof(foo_t) - offsetof(foo_t, a).

    – Darren Stone

    5. November 2013 um 10:24 Uhr

  • Ich habe solche Sachen ziemlich oft gemacht, als ich zum ersten Mal mit C-Strukturen und Arrays klar kam. Ich würde vermuten, dass Ihre Überzeugung, warum es passiert ist, ziemlich genau richtig war.

    – Falscher Name

    5. November 2013 um 11:14 Uhr


  • Ich würde damit beginnen, dass ich keine Variable mit einem einzigen Buchstaben benannt habe l.

    – Guilherme Bernal

    6. November 2013 um 13:39 Uhr


Stattdessen:

memcpy(&l.a, &inp->a, sizeof(foo_t));

du kannst das:

memcpy(&l, inp, sizeof(foo_t));

Obwohl es gefährlich und irreführend sein kann, machen beide Anweisungen hier tatsächlich dasselbe, da C garantiert, dass vor dem ersten Strukturelement kein Füllzeichen vorhanden ist.

Am besten kopieren Sie die Strukturobjekte aber einfach mit einem einfachen Zuweisungsoperator:

l = *inp;

Warum sollte man diesen Stil verwenden wollen?

Meine Vermutung: Ignoranz oder schlechte Disziplin.

  • +1 für das Beste l = *inp. Vorgeschlagener entfernter Zweitbester memcpy(&l, inp, sizeof l).

    – chux – Wiedereinsetzung von Monica

    4. November 2013 um 21:03 Uhr


  • Ignoranz? Nicht so sicher … Ich meine, sie wusste (hoffentlich) dass die beiden Dinge gleichwertig waren und durch die Spezifikation garantiert wurden, also glaube ich nicht, dass Sie darauf schließen können Mangel des Wissens aus diesem Code, aber nur a schlechter Gebrauch von solchem ​​Wissen (was oft schlimmer ist!).

    – Bakuriu

    5. November 2013 um 8:05 Uhr

Niemand sollte das tun. Wenn Sie Struct-Mitglieder neu anordnen, geraten Sie in Schwierigkeiten.

Man würde nicht. Wenn Sie jemals umgezogen sind a in der Struktur oder Sie davor Member eingefügt haben, würden Sie einen speicherzerstörenden Fehler einführen.

Dieser Code ist unsicher, da das Neuanordnen der Mitglieder der Struktur dazu führen kann, dass die memcpy Zugriff über die Grenzen der Struktur hinaus, wenn Member a ist nicht mehr das erste Mitglied.

Es ist jedoch denkbar, dass Mitglieder absichtlich innerhalb der Struktur angeordnet sind und der Programmierer nur eine Teilmenge davon kopieren möchte, Anfang mit Mitglied a und läuft bis zum Ende der Struktur. Wenn dies der Fall ist, kann der Code mit der folgenden Änderung gesichert werden:

    memcpy(&l.a, &inp->a, sizeof(foo_t) - offsetof(foo_t, a));

Jetzt können die Strukturmitglieder in beliebiger Reihenfolge neu angeordnet werden und dies memcpy wird niemals die Grenzen verlassen.

Benutzeravatar des Engineers
Techniker

Tatsächlich gibt es dafür einen legitimen Anwendungsfall: den Aufbau einer Klassenhierarchie.

Wenn Strukturen als Klasseninstanzen behandelt werden, ist das erste Element (dh Offset 0) normalerweise die Supertypinstanz … wenn ein Supertyp existiert. Dadurch kann eine einfache Besetzung zwischen der Verwendung des Subtyps und des Supertyps wechseln. Sehr hilfreich.

Zu Darren Stones Notiz über Absicht, das ist erwartet beim Ausführen von OO in der C-Sprache.

In jedem anderen Fallwürde ich vorschlagen, dieses Muster zu vermeiden und stattdessen aus den bereits genannten Gründen direkt auf das Mitglied zuzugreifen.

Benutzeravatar von Chris Fox
Chris Fox

Es ist eine wirklich schlechte Angewohnheit. Der Struktur könnte beispielsweise ein anderes Element vorangestellt sein. Das ist eine wahnsinnig nachlässige Angewohnheit und ich bin überrascht zu lesen, dass irgendjemand so etwas tun würde.

Andere haben dies bereits bemerkt; das was mich nervt ist das:

struct Foo rgFoo [3];
struct Foo *pfoo = &rgFoo [0];

Anstatt von

struct Foo *pfoo = rgfoo;

Warum das Array nach Index derefieren und dann die Adresse erneut nehmen? Es ist bereits die Adresse, der einzige bemerkenswerte Unterschied besteht darin, dass pfoo technisch gesehen ist

struct Foo *const, 

nicht

struct Foo *.  

Aber ich habe immer den ersten gesehen.

1412730cookie-checkWarum die Adresse des ersten Elements von struct verwenden, anstatt struct selbst?

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

Privacy policy