Ist die Reihenfolge der Mitglieder in einer Struktur von Bedeutung?

77

Ich habe in C ein eigenartiges Verhalten gefunden. Betrachten Sie den folgenden Code:

Es kompiliert ganz gut. Ändern Sie dann die Reihenfolge der Mitglieder von struct zwie folgt

Und plötzlich bekommen wir den Kompilierungsfehler field has incomplete type 'struct s []'.

Warum ist das so?

Milo Wielondek
quelle

Antworten:

91

Die Reihenfolge der Felder in a structspielt eine Rolle - der Compiler darf Felder nicht neu anordnen , daher kann sich die Größe der Felder structaufgrund des Hinzufügens von Auffüllungen ändern.

In diesem Fall definieren Sie jedoch ein sogenanntes flexibles Element , ein Array, dessen Größe Sie ändern können. Die Regeln für flexible Mitglieder sind folgende

  • Es kann niemals mehr als ein solches Mitglied geben,
  • Falls vorhanden, muss das flexible Mitglied das letzte im struct, und sein
  • Sie structmüssen zusätzlich zu dem flexiblen mindestens ein Mitglied haben.

In diesen Fragen und Antworten finden Sie eine kleine Illustration zur Verwendung flexibler Strukturelemente.

Sergey Kalinichenko
quelle
5
Scheint also eine unglaublich schlechte Diagnose zu sein (wenn auch eine technisch genaue).
Leichtigkeitsrennen im Orbit
5
@LightnessRacesinOrbit Sie haben Recht, ich mag die Fehlermeldung auch nicht besonders.
Sergey Kalinichenko
21

Der Compiler kann nicht berechnen, wie viel Speicher belegt struct s b[];wird. Dies bedeutet, dass der Compiler nicht herausfinden kann, wo sich diese Felder befinden, wenn die Struktur Felder enthält.

Früher war es (in alten Versionen von C) so, dass (z. B.) struct s b[];als Mitglied einer Struktur nicht erlaubt war. Dies machte eine effiziente Speicherverwaltung ärgerlich. Stellen Sie sich als einfaches Beispiel vor, Sie haben eine Struktur, die eine "Name" -String enthält (dies können nur wenige oder viele Zeichen sein). Sie können ein Array mit fester Größe verwenden, das groß genug für den größten Namen ist (was Speicherplatz verschwendet), oder einen Zeiger verwenden und zwei Speicherelemente zuweisen (eines für die Struktur und eines für die Namenszeichenfolge mit variabler Länge). Alternativ können Sie einen Zeiger verwenden und ihn auf zusätzlichen Platz hinter dem Ende der Struktur verweisen lassen, was ungefähr so ​​aussieht:

Dies war die effizienteste Option; aber es ist auch hässlich und fehleranfällig.

Um dies zu umgehen, haben Compiler nicht standardmäßige Erweiterungen hinzugefügt, um Arrays mit "unbekannter Größe" am Ende der Struktur zuzulassen. Dies machte es für Programmierer etwas einfacher und effizienter (da kein zusätzliches Zeigerelement erforderlich ist). Dies wurde schließlich vom C-Standard übernommen (vielleicht in C99 - ich erinnere mich nicht).

Brendan
quelle
2
Interessanterweise erinnere ich mich, bevor flexible Array-Mitglieder als Standard verfügbar wurden, einige Compiler zu verwenden, mit denen Code (ob beabsichtigt oder versehentlich) ein Array der Größe Null als Teil einer Struktur deklarieren konnte, wahrscheinlich weil eine Überprüfung der Größe des Arrays Null weggelassen wurde würde dazu führen, dass solche Arrays standardmäßig zulässig sind. Die Verwendung von leer []ist semantisch etwas anders, da ein Compiler die direkte Instanziierung einer Struktur ermöglichen würde, die ein Array mit der Größe Null enthält (obwohl die Verwendung des Arrays UB wäre), während eine FLA oder eine Struktur, die eines enthält, das letzte Element von sein muss eine Struktur.
Supercat
@supercat: Ja, Arrays mit der Länge Null wurden zumindest in Version 2 ( gcc.gnu.org/onlinedocs/gcc-2.95.3/gcc_4.html#SEC74 ) als Erweiterung in GCC eingeführt und werden auch jetzt noch unterstützt ( gcc.gnu.org/onlinedocs/gcc/Zero-Length.html ).
Nemo
15

In der Regel spielt die Reihenfolge der Elemente eine Rolle (dh, zwischen den Feldern wird möglicherweise ein Auffüllen eingefügt). In Ihrem speziellen Fall verwenden Sie jedoch ein flexibles Elementarray. Dies ist in C99 - 6.7.2.1.16 standardisiert

Als Sonderfall kann das letzte Element einer Struktur mit mehr als einem benannten Element einen unvollständigen Array-Typ haben. Dies wird als flexibles Array-Mitglied bezeichnet. In den meisten Situationen wird das flexible Array-Mitglied ignoriert. Insbesondere ist die Größe der Struktur so, als ob das flexible Array-Element weggelassen worden wäre, mit der Ausnahme, dass es möglicherweise mehr nachlaufende Polsterung aufweist, als die Auslassung implizieren würde.

Ihr struct s b[];Mitglied soll für den Zugriff auf dynamische Heap-Zuordnungen für mehrere struct sElemente verwendet werden.

Marco A.
quelle
1
Vielen Dank, dass Sie den Standard zitiert haben. Ich habe mich oft gefragt, wie groß eine solche Struktur sein würde.
Dan
4

Der Titel Ihrer Frage lautet "Ist die Reihenfolge der Mitglieder in einer structAngelegenheit?".

Das offensichtliche Problem in Ihrem Code hängt mit der Tatsache zusammen, dass Sie structein flexibles Mitglied enthalten.

Hier ist also ein zusätzliches Problem im Zusammenhang mit der allgemeinen Frage der Reihenfolge der Mitglieder in a struct:


Nehmen Sie zum Beispiel die folgenden zwei Strukturen:

Die meisten Compiler fügen Auffüllungen hinzu, um jedes Mitglied an einer durch seine Größe teilbaren Adresse auszurichten.

So struct s2kann letztendlich kompiliert werden in:

Dies führt schließlich zu unterschiedlichen Größen für Instanzen von Typen struct s1und struct s2.

Barak Manos
quelle
1

In diesem Fall spielt die Bestellung eine Rolle. Ihr struct zenthält ein Array bestehend aus structs s. Diesem Array ist jedoch keine Größe zugeordnet, sodass der Compiler nicht weiß, wie der entsprechende Stapelspeicher zugewiesen werden soll, da anschließend ein weiteres Feld von struct ( int a) vorhanden ist. Ein Beispiel dafür, wie es funktionieren würde:

Wenn Sie das Array wirklich benötigen, um die Größe zu ändern, ist es am besten, wenn Sie die gesamte Struktur auf dem Heap mit dem Array als Zeiger zuweisen struct sund es dann dynamisch neu zuweisen , um der sich ändernden Arraygröße Rechnung zu tragen. Sehen Sie malloc (3), realloc 3), calloc (3), und free (3).

Gophyr
quelle