Wyściółka w konstrukcjach w C

To jest pytanie z wywiadu. Do tej pory uważałem, że takie pytania są całkowicie zależne od kompilatora i nie powinny mnie martwić, ale teraz jestem tym raczej ciekaw.

Załóżmy, że masz dwie struktury:

struct A {  
  int* a;  
  char b;  
 }  

I,

struct B {  
  char a;  
  int* b;  
}  
Więc który wolisz i dlaczego? Moja odpowiedź brzmiała tak (choć trochę strzelałem w ciemności), że pierwsza struktura powinna być preferowana, ponieważ kompilator przydziela miejsce na strukturę w kilku wielokrotnościach wielkość słowa (czyli wielkość wskaźnika - 4 bajty na maszynach 32-bitowych i 8 bajtów na maszynach 64-bitowych). Tak więc, dla obu struktur kompilator przydzieliłby 8 bajtów (zakładając, że jest to maszyna 32-bitowa). Ale, w pierwszym przypadku, padding będzie wykonywane po wszystkich moich zmiennych (tj. po a i b). Więc nawet jeśli przez jakiś przypadek, b dostaje jakąś wartość, która przepełnia i niszczy moje następne wyściełane bajty, ale moje a jest nadal bezpieczne. Nie wydawał się zbyt zadowolony i poprosił o jedną wadę pierwsza struktura nad drugą. Nie miałem wiele do powiedzenia. : D Proszę, pomóż mi z odpowiedziami.
Author: nsane, 2011-08-06

5 answers

Nie wydaje mi się, żeby ta struktura miała jakąś przewagę. Jest jeden(!) stała w tym równaniu. Kolejność członków struktury jest gwarantowana zgodnie z deklaracją.

Tak więc w przypadku, jak poniżej, druga struktura może mieć przewagę, ponieważ prawdopodobnie ma mniejszy rozmiar, ale nie w twoim przykładzie, ponieważ prawdopodobnie będą miały ten sam rozmiar:

struct {
    char a;
    int b;
    char c;
} X;

Vs.

struct {
    char a;
    char b;
    int c;
} Y;

Trochę więcej wyjaśnień dotyczących komentarzy poniżej:

Wszystko poniżej nie jest 100%, ale wspólny sposób, w jaki struktury będą konstruowane w 32-bitowym systemie, gdzie int jest 32-bitowy:

Struct X:

|     |     |     |     |     |     |     |     |     |     |     |     |
 char  pad    pad   pad   ---------int---------- char   pad   pad   pad   = 12 bytes

Struct Y:

|     |     |     |     |     |     |     |     |
 char  char  pad   pad   ---------int----------        = 8 bytes
 33
Author: MByD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-07 00:49:12

Niektóre maszyny uzyskują dostęp do danych bardziej efektywnie , gdy wartości są wyrównane do pewnej granicy. Niektóre wymagaj DANE do wyrównania.

Na nowoczesnych maszynach 32-bitowych, takich jak SPARC czy Intel [34]86, czy też Chip Motorola od 68020 w górę, każdy iten danych musi być zazwyczaj "self-aligned", zaczynając od adresu, który jest wielokrotnością jego Rozmiar typu. Tak więc typy 32-bitowe muszą zaczynać się na granicy 32-bitowej, 16-bitowej typy na granicy 16-bitowej, typy 8-bitowe may begin anywhere , typy struct / array / union mają swoje najbardziej restrykcyjne członek.

Więc możesz mieć

struct B {  
    char a;
    /* 3 bytes of padding ? More ? */
    int* b;
}

Prosta reguła, która minimalizuje padding w przypadku" self-aligned " (i nie szkodzi w większości innych) jest nakazanie swoim członkom struct przez malejący rozmiar.

Osobiście nie widzę wad w porównaniu z pierwszą strukturą w porównaniu z drugą.

 11
Author: cnicutar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-06 17:54:18

Nie mogę myśleć o wadach pierwszej struktury nad drugą w tym konkretnym przypadku, ale można wymyślić przykłady, gdzie są wady ogólnej zasady stawiania największych członków na pierwszym miejscu:

struct A {  
    int* a;
    short b;
    A(short num) : b(2*num+1), a(new int[b]) {} 
    // OOPS, `b` is used uninitialized, and a good compiler will warn. 
    // The only way to get `b` initialized before `a` is to declare 
    // it first in the class, or of course we could repeat `2*num+1`.
}

Słyszałem też o dość skomplikowanym przypadku dla dużych struktur, gdzie procesor ma tryby szybkiej adresacji dostępu do wskaźnika+offset, dla małych wartości offsetu (na przykład do 8 bitów lub jakiś inny limit natychmiastowej wartości). You best mikro-optymalizacja dużej struktury poprzez umieszczenie jak największej liczby najczęściej używanych pól w zasięgu najszybszych instrukcji.

Procesor może mieć nawet szybkie adresowanie dla pointer+offset i pointer+4 * offset. Załóżmy, że masz 64 pola znaków i 64 pola int: jeśli najpierw umieścisz pola znaków, to wszystkie pola obu typów mogą być adresowane za pomocą najlepszych instrukcji, podczas gdy jeśli najpierw umieścisz pola int, to pola znaków, które nie są wyrównane do 4, będą musiały być dostęp jest różny, być może poprzez wczytanie stałej do rejestru, a nie z wartością natychmiastową, ponieważ są one poza 256-bajtowym limitem.

Nigdy nie musiałem tego robić sam, a na przykład x86 i tak pozwala na duże wartości natychmiastowe. Nie jest to rodzaj optymalizacji, o której każdy normalnie by pomyślał, chyba że spędza dużo czasu gapi się na montaż.

 4
Author: Steve Jessop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-06 23:45:06

Krótko mówiąc, nie ma żadnej przewagi w wyborze w ogólnym przypadku . Jedyną sytuacją, w której wybór miałby znaczenie w praktyce, jest włączenie structure packing , W przypadku struct A byłoby lepszym wyborem (ponieważ oba pola byłyby wyrównane w pamięci, podczas gdy w struct B pole b znajduje się w nieparzystym offsecie). Pakowanie struktury oznacza, że żadne bajty wypełnienia nie są wstawiane wewnątrz struktury.

Jest to jednak dość nietypowy scenariusz: struktura pakowanie jest na ogół możliwe tylko w określonych sytuacjach. To nie jest problemem w większości programów. Nie można go również kontrolować za pomocą żadnej przenośnej konstrukcji w standardzie C.

 2
Author: alecov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-06 19:07:41

Jest to również coś w rodzaju zgadywania, ale większość kompilatorów ma opcję misalign, która wyraźnie nie dodaje bajtów wypełnienia. To wtedy wymaga (na niektórych platformach) poprawki (pułapki sprzętowej) w celu wyrównania dostępu w locie (z odpowiednią karą za wydajność). O ile dobrze pamiętam HPUX wpadł do tej kategorii. Tak więc pierwsza struktura pola są nadal wyrównane, nawet jeśli używane są opcje kompilatora misalign (ponieważ jak powiedziałeś, wypełnienie będzie na końcu).

 1
Author: Kevin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-06 17:47:56