2012-01-02 14 views
7

Załóżmy, że mamy dwa konstrukcjom:Struct kompatybilność wskaźnik

typedef struct Struct1 
{ 
    short a_short; 
    int id; 
} Struct1; 

typedef struct Struct2 
{ 
    short a_short; 
    int id; 
    short another_short; 
} Struct2; 

Czy bezpieczne jest odlana z Struct2 * do Struct1 *? Co mówi o tym specyfikacja ANSI? Wiem, że niektóre kompilatory mają możliwość zmiany kolejności pól structs w celu optymalizacji wykorzystania pamięci, co może spowodować, że dwie struktury będą niekompatybilne. Czy jest jakiś sposób, aby upewnić się, że ten kod będzie ważny, niezależnie od flagi kompilatora?

Dziękujemy!

+2

* Zmiana kolejności * członkowie nie mogą korzystać ze standardowego AFAIK. Sądzę, że dozwolone byłoby wstawianie różnych ilości wypełnień. – delnan

+0

@delnan Oh więc to, że struct 'packing' wyłącza tylko wyrównanie? Dzięki, nie wiedziałem o tym! – Waneck

Odpowiedz

5

struct rodzaje pointers zawsze mają taką samą reprezentację w C

(C99 6.2.5p27) „Wszystkie wskaźniki do uporządkowania rodzaje mają te same reprezentacja i wyrównania wymagania co do siebie.”

, a człony w rodzaju konstrukcji są zawsze w kolejności C

(C99 6.7.2.1p5) „struktura jest typu składającego się z sekwencji członków, którego pamięci jest przydzielona w zamówionej sekwencji "

+1

To nie odpowiada na pytanie; nawet z tymi ograniczeniami może nadal być naruszeniem aliasingu. Jednak pod pewnymi warunkami norma C wyraźnie dopuszcza to, co chce OP. –

+0

Dziękuję bardzo za te cytaty ze specyfikacji ANSI. To dla mnie wyjaśnia, że ​​jest to bezpieczne! – Waneck

+0

@R. Jakie warunki? – Waneck

2

Najprawdopodobniej będzie działać. Ale masz rację pytając, jak możesz być pewien, że ten kod będzie ważny. A więc: gdzieś w twoim programie (przy starcie) osadzić kilka instrukcji ASSERT, które upewniają się, że offsetof(Struct1.a_short) jest równy offsetof(Struct2.a_short) itd. Poza tym, jakiś programista inny niż ty mógłbyś kiedyś zmodyfikować jedną z tych struktur, ale nie inną, więc lepiej bezpieczniej niż przepraszam.

+0

Używanie statycznych wyświetleń byłoby znacznie lepsze ... –

+0

Dzięki Mike, na pewno dodam kilka gwarancji, aby to zagwarantować! – Waneck

+0

@R .. statyczne potwierdza? Nie wiedziałem, że oni istnieją.[Sprawdziłem i się dowiedziałem] (http://stackoverflow.com/questions/3385515/static-assert-in-c). Masz rację, dzięki. –

3

Jest bezpieczny, o ile wiem.

Ale to o wiele lepiej, jeśli to możliwe, należy zrobić:

typedef struct { 
    Struct1 struct1; 
    short another_short; 
} Struct2; 

Potem już nawet powiedział kompilator, że Struct2 rozpoczyna się wystąpieniem Struct1, a ponieważ wskaźnik do struktury zawsze wskazuje na jego pierwszy członek, możesz bezpiecznie traktować Struct2 * jako Struct1 *.

+0

cóż, jeśli istnieje najmniejsza szansa, że ​​pewnego dnia 'offsetof (Struct1.a_short)' zostanie znaleziony NIE jest równe 'offsetof (Struct2.a_short)' wtedy jest taka sama szansa, że ​​jeden dzień 'offsetof (Struct2.struct1)' zostanie znaleziony nie równy zeru. (Co oznaczałoby, że '& struct2! = (Struct2 *) i struct2.struct1'). –

+0

Rzeczywiście, ten sposób jest znacznie lepszy! :) Dziękuję Ci! – Waneck

+0

Jeśli zarówno struct1, jak i struct2 umieszczają "int" pierwszy, a "int" wymaga 32-bitowego wyrównania, to oba typy struktur mogą mieć 8 bajtów, ale twoja alternatywna forma Struct2 będzie wymagać 12 bajtów. Jeśli kompilatory przestrzegają reguły Common Initial Sequence, każda z nich powinna być poprawna (a forma 8-bajtowa byłaby bardziej wydajna), ale nawet po wywołaniu w trybie C89, gcc nie zachowuje już gwarancji C89, z wyjątkiem sytuacji, gdy '-fno-strict- stosuje się flagę aliasing'. – supercat

1

Tak, można to zrobić w porządku!

Przykładowy program wygląda następująco.

#include <stdio.h> 

typedef struct Struct1 
{ 
    short a_short; 
    int id; 
} Struct1; 

typedef struct Struct2 
{ 
    short a_short; 
    int id; 
    short another_short; 
} Struct2; 

int main(void) 
{ 

    Struct2 s2 = {1, 2, 3}; 
    Struct1 *ptr = &s2; 
    void *vp = &s2; 
    Struct1 *s1ptr = (Struct1 *)vp; 

    printf("%d, %d \n", ptr->a_short, ptr->id); 
    printf("%d, %d \n", s1ptr->a_short, s1ptr->id); 

    return 0; 
} 
Powiązane problemy