2017-12-08 2 views
7

Ich habe eine Struktur X, die von Struktur Basis erbt. Doch meine aktuelle Setup aufgrund Ausrichtung, Größe X ist 24B:Vererbt die Vererbung über die Abwicklung die strenge Aliasing-Regel?

typedef struct { 
    double_t a; 
    int8_t b; 
} Base; 

typedef struct { 
    Base base; 
    int8_t c; 
} X; 

Um den Speicher zu sparen, würde Ich mag die Basis-Struktur entspannen, so habe ich Struktur Y, die enthält Felder aus Base (in der gleichen Reihenfolge, immer am Anfang der Struktur), so dass die Größe der Struktur ist 16B:

typedef struct { 
    double_t base_a; 
    int8_t base_b; 
    int8_t c; 
} Y; 

Dann die einen Zeiger erwartet werde ich Instanz von struct Y in einem Verfahren verwenden, zu Basisstruktur:

void print_base(Base* b) 
{ 
    printf("%f %d\n", b->a, b->b); 
} 
// ... 
Y data; 
print_base((Base*)&data); 

Verletzt der obige Code die strenge Aliasing-Regel und führt zu undefiniertem Verhalten?

+0

Recht, fixierte den Beispielcode –

+0

Warum Sie nicht einfach 'Base Base erklären;' in 'Y' und rufen Sie die Funktion mit 'print_base (& data.base); '? Es verbraucht den gleichen Speicher wie die Mitglieder der Struktur in der anderen Struktur. – mch

+1

@mch es nicht. Größe der Struktur X, die Ihren Ansatz verwendet, dauert 24 Bytes, während Struktur Y nur 16 –

Antwort

8

Erstens, Base und Y sind nicht kompatible Typen wie in der Norm 6.2.7 definiert, müssen alle Mitglieder übereinstimmen.

Um ein Y durch ein Base* ohne eine strenge Aliasing Verletzung zu schaffen, greifen Y „ein Aggregat-Typ“ (es ist), die enthält einen Base Typen unter den Mitgliedern sein muss. Es tut nicht.

Es ist also eine strenge Aliasing-Verletzung und außerdem, da Y und Base nicht kompatibel sind, können sie unterschiedliche Speicherlayouts haben. Welches ist der ganze Punkt, Sie haben sie verschiedene Arten genau aus diesem Grund :)

Was Sie in Situationen wie diesem tun können, ist die Verwendung von Unions mit Strukturelementen, die eine gemeinsame Anfangssequenz teilen, die ist ein spezieller erlaubter Fall. Beispiel gültigen Code von C11 6.5.2.3:

union { 
    struct { 
    int alltypes; 
    } n; 
    struct { 
    int type; 
    int intnode; 
    } ni; 
    struct { 
    int type; 
    double doublenode; 
    } nf; 
} u; 

u.nf.type = 1; 
u.nf.doublenode = 3.14; 
/* ... */ 
if (u.n.alltypes == 1) 
    if (sin(u.nf.doublenode) == 0.0) 
+0

Danke. Ich bin mir nicht sicher über das Speicherlayout. Gewährleistet der Standard nicht, dass der Offset für zwei erste Felder (a, b in Base, base_a, base_b in Y) gleich ist? –

+1

@MarcinKolny Dies ist nicht über Speicherlayout. Das Speicherlayout ist in beiden Typen gleich. Hier geht es darum, den Compiler-Optimierer zu informieren. Sie definieren 'union {Basis b; Y y;}; 'in Ihrem Header, um den Optimierer zu informieren, dass er den Wert' b.a' aus dem Speicher jederzeit neu laden muss, wenn sich der Wert 'y.base_a' ändert. – Marian

+0

@Marian danke, das klingt interessant. Nur neugierig, könntest du mir ein paar Artikel nennen, wo ich mehr darüber lesen könnte? –

Verwandte Themen