2015-01-11 9 views
9

Ich habe in eine nicht traditionelle Art und Weise der Suche Struktur „Polymorphismus“ in pre-C11 C. Angenommen, wir haben zwei Strukturen zu erreichen:Vereinigung von structs teilt gleiche erste Mitglieder

struct s1 { 
    int var1; 
    char var2; 
    long var3; 
}; 

struct s2 { 
    int var1; 
    char var2; 
    long var3; 
    char var4; 
    int var5; 
}; 

Auf den meisten Compiler, Wir könnten sicher zwischen den Zeigern zu den beiden umwandeln und dann auf die gemeinsamen ersten Mitglieder zugreifen, wenn keine Auffüllung stattfindet. Dies ist jedoch kein standardisiertes Verhalten.

Nun fand ich die folgende Zeile in der C-Standard bis C89:

Eine besondere Garantie wird gemacht, um die Verwendung von Gewerkschaften zu vereinfachen: Wenn ein Zusammenschluss mehrere Strukturen enthält, die eine gemeinsame teilen Anfangsfolge, und wenn das Vereinigungsobjekt gegenwärtig eine dieser Strukturen enthält, ist es erlaubt, den gemeinsamen Anfangsteil von jedem von ihnen zu untersuchen. Zwei Strukturen teilen eine gemeinsame Anfangssequenz, wenn entsprechende Mitglieder kompatible Typen für eine Sequenz von einem oder mehreren Anfangselementen haben.

Weiter heißt es, die folgend:

Ein Zeiger auf ein Objekt Union, in geeigneter Weise gegossen, um jedes ihrer Mitglieder Punkte (oder wenn ein Mitglied ist, ein Bit-Feld, dann an die Einheit in wo es sich befindet) und umgekehrt.

Nun, wenn ich eine Vereinigung dieser beiden structs:

union s2_polymorphic { 
    struct s1 base; 
    struct s2 derived; 
}; 

Und es auf diese Weise verwenden:

union s2_polymorphic test_s2_polymorphic, *ptest_s2_polymorphic; 
struct s2 *ptest_s2; 
struct s1 *ptest_s1; 

ptest_s2_polymorphic = &test_s2_polymorphic; 

ptest_s2 = (struct s2*)ptest_s2_polymorphic; 

ptest_s2->var1 = 1; 
ptest_s2->var2 = '2'; 

ptest_s1 = (struct s1*)ptest_s2; 

printf("ptest_s1->var1 = %d\n", ptest_s1->var1); 
printf("ptest_s1->var2 = %c\n", ptest_s1->var2); 

Welche kompiliert und läuft gut und gibt, auf gcc (GCC) 4.8.3 20140911, der Ausgang

ptest_s1->var1 = 1                
ptest_s1->var2 = 2 

Wird das Verhalten wohldefiniert sein, entsprechend den Zitaten aus dem oben angegebenen Standard?

+0

Ich mag falsch verstehen, was Sie hier tun, aber in dem Beispiel mit der "Union" sollte nicht "s2" (d. H. "Abgeleitet") jetzt nicht den Inhalt von "s1" duplizieren? IE sollte es nicht nur die zusätzlichen Elemente enthalten? – abligh

+1

"* Wird das Verhalten klar definiert sein *" Ich würde sagen: Ja. Was lässt dich daran zweifeln? – alk

+0

@abligh: und ich mag deine Frage falsch verstehen ... Was meinst du mit "nur die zusätzlichen Elemente enthalten"? – Mints97

Antwort

1

Nach ein paar Recherchen denke ich, dass ich eine qualifizierte Antwort für diese Frage habe.

Das angegebene Zitat stammt aus dem C89-Standard. C99 und C11 haben es wie folgt neu gefasst:

Eine besondere Garantie wird gemacht, um die Verwendung von Gewerkschaften zu vereinfachen: Wenn eine Vereinigung enthält mehrere Strukturen, die eine gemeinsame Anfangssequenz (siehe unten) zu teilen, und wenn die Union Objekt enthält derzeit eine dieser Strukturen, es ist erlaubt, den gemeinsamen Anfangsteil von jedem von ihnen zu überprüfen überall dort, dass eine Erklärung des fertigen Typs der Union sichtbar ist.

Der letzte Teil kann interpretiert werden, IMHO, in einer Vielzahl von Möglichkeiten. Das Komitee hat es jedoch so gelassen wie es ist. Gemäß ihnen bedeutet es, dass die Inspektion des "gemeinsamen Anfangsteils" der Strukturen nur unter Verwendung eines Objekts des Typs durchgeführt werden kann, der erklärt worden ist, sie zu enthalten. Das wird sehr gut in this question demonstriert.

Was ist mit C89, was zu erlauben scheint, was ich versuchte? Nun, in einem C89-konformen Compiler, ja, das sollte funktionieren.Es wird jedoch möglicherweise nicht wirklich benötigt: Ich kenne keinen einzigen strikt C89-konformen Compiler, der striktes Aliasing unterstützt, daher ist es einfacher, die Strukturen mit der gemeinsamen Anfangssequenz gegenseitig zu typisieren und versuchen Sie nicht, ihnen unterschiedliche Verpackungseinstellungen zu geben. Das Ergebnis sollte das gleiche sein.

+0

Angesichts der Tatsache, dass der fertige Typ der Gewerkschaft sichtbar sein müsste, um durch sie Zugang zu allem zu erhalten, warum hätten die Autoren des Standards irgendetwas über die Sichtbarkeit des fertigen Typs der Gewerkschaft gesagt, wenn sie dies nicht zulassen wollten Gewerkschaftsdeklarationen, um die assoziierten Typen als eine Gruppe zu "binden", die eine gegenseitige Inspektion der gemeinsamen Mitglieder ermöglichen sollte? Ich weiß, dass die Autoren von gcc diese Regel nicht mögen, und es gäbe bessere Wege, um das gewünschte Ergebnis zu erreichen, aber die Zugehörigkeit zu einer sichtbaren Union ist das Mittel, das die Autoren des Standards gewählt haben, um eine solche Semantik zuzulassen. – supercat

+0

Wenn der Standard ausdrücklich zulassen würde, dass ein Zeiger auf einen beliebigen Typ in einen Zeiger auf eine Union mit diesem Typ umgewandelt wird, vorausgesetzt, dass er nur für den Zugriff auf das richtige Union-Member oder andere Member mit einer gemeinsamen Anfangssequenz verwendet wird hat angegeben, dass Operationen über zwei Union-Typen, die alle Member verwenden, einen Aliasnamen enthalten, der eine gute Möglichkeit bietet, die richtige Funktionalität anzubieten. Wenn jedoch ein Union-Member eine gröbere Ausrichtung aufweist, wird ein Zeiger auf ein lose ausgerichtetes Objekt an eine Union übergeben Zeiger würde UB aufrufen, da es die Ausrichtung der Union nicht erfüllen würde. – supercat