2013-11-25 10 views
15

W tym uproszczonym przypadku testowego:Co oznacza komunikat "tester klasy dopełnienia" z 4 bajtami "?

#include <map> 

class Tester { 
    int foo; 
    std::map<int, int> smap; 
}; 

int main() { 
    Tester test; 
    return 0; 
} 

otrzymuję następujące ostrzeżenie kompilatora:

$ clang++ -std=c++98 -Weverything test.cc 
test.cc:5:24: warning: padding class 'Tester' with 4 bytes to align 'smap' [-Wpadded] 
    std::map<int, int> smap; 
        ^

może ktoś wyjaśnić co to oznacza ostrzegawczych, a jak mam to adres?

+14

Oznacza to, że włączyłeś zbyt wiele ostrzeżeń. :) –

+0

Tak, struct padding jest * cechą *, a nie czymś, o czym trzeba ostrzegać. Przypuszczam, że flaga jest tam, więc ludzie, którzy muszą znać * dokładny * układ swoich struktur w pamięci, mogą dodać wyraźne dopełnienie i zostać ostrzeżeni za każdym razem, gdy opuścili część, ale to nie jest normalny scenariusz. – hobbs

+1

@RetiredNinja Święty człowiek krowa, sposób, aby całkowicie reprezentować swoją nazwę użytkownika :) – bobobobo

Odpowiedz

10

Nie ma tu prawdziwego problemu. W C i C++ kompilator może wstawiać dopełnienie po członach struktury, aby zapewnić lepsze wyrównanie, a tym samym umożliwić szybszy dostęp do pamięci. W tym przypadku wygląda na to, że postanowiliśmy umieścić smap w 8-bajtowym wyrównaniu. Ponieważ int jest prawie na pewno cztery bajty, ostrzeżenie mówi, że w środku struktury znajdują się cztery bajty zmarnowanej przestrzeni.

Jeśli było więcej członków struktury, to jedną rzeczą, którą mógłbyś wypróbować, byłaby zmiana kolejności definicji. Na przykład, jeśli Tester miał członków:

struct Tester { 
    int foo; 
    std::map<int, int> smap; 
    int bar; 
}; 

wtedy ma sens, aby umieścić dwie ints obok siebie, aby zoptymalizować ustawienie i uniknąć marnowania miejsca. Jednak w tym przypadku masz tylko dwóch członków, a jeśli je zmienisz, kompilator prawdopodobnie doda jeszcze cztery bajty dopełnienia do końca struktury, aby zoptymalizować wyrównanie Tester s po umieszczeniu w tablicy.

+0

Dzięki! Umieszczenie 'foo' po' smap' powoduje wyświetlenie innego ostrzeżenia: 'rozmiar wypełnienia 'Tester' z 4 bajtami dla wyrównania granicy ', prawdopodobnie oznacza to, że te 4 bajty dopełniające są wstawiane przed zakończeniem klasy, a nie przed'. smap'. Mogę też wyeliminować ostrzeżenie, konwertując 'foo' na' long'. Pozostaje tylko pytanie: dlaczego nie otrzymam ostrzeżenia, gdy 'int foo' jest jedyną zmienną w' Testerze'? –

+0

@DunPeal Ponieważ liczby całkowite wymagają tylko 4-bajtowego wyrównania, więc nie trzeba dodawać dopełnienia na końcu struktury, aby optymalnie umieścić je w tablicy. Jeśli spróbujesz zmienić 'foo' na' short' lub 'bool', możesz zobaczyć ostrzeżenie pojawiające się ponownie (wtedy znowu, możesz nie, nie jestem pewien). –

+0

Z odpowiedzi na to pytanie myślałem, że kompilator na mojej platformie (LP64) zawsze dąży do tego, aby każda struktura miała przydzieloną liczbę 64? W przeciwnym razie, po co zawijać dopełnienie, gdy 'foo' jest ostatnim członkiem? –

4

Zakładam, że kompilujesz to w systemie 64-bitowym.

W systemach 64-bitowych wskaźniki mają 8 bajtów. Kompilatory będą dopasowywać elementy struktury do naturalnych granic, więc 8-bajtowy wskaźnik rozpocznie się od przesunięcia w strukturze, która jest wielokrotnością 8 bajtów.

Ponieważ int tylko cztery bajty, kompilator dodaje 4 bajty „wyściółkę”, po foo, tak że smap znajduje się na granicy 8 bajtów.

Edytuj: Podczas gdy smap nie jest wskaźnikiem, ale std::map, obowiązuje ta sama logika. Nie jestem pewien, jakie są dokładne zasady wyrównania obiektów, ale to samo dzieje się.

Co robić? Nic. Twój kod jest całkowicie w porządku, kompilator właśnie informuje, że to się stało. Nie ma się czym martwić. -Weverything oznacza włączenie every possible warning, co jest prawdopodobnie zbyteczne dla większości kompilacji.

+0

Dzięki! Wydaje się, że masz rację; kiedy zmieniam 'foo' na' long', ostrzeżenia znikają.Z twojej odpowiedzi rozumiem, że jedynym skutecznym znaczeniem tego ostrzeżenia jest to, że 4 bajty pamięci zostaną zmarnowane dla każdego wystąpienia 'Testera'. –

+0

Aby uzyskać dostęp do elementu 'smap', dostęp do niego byłby jeszcze droższy z przesunięciem o połowę rozmiaru wskaźnika (4 bajty) niż rozmiar pełnowymiarowy (8 bajtów), ponieważ rozmawiamy od początku struct. – bobobobo

+1

@DunPeal Nie chodzi tylko o to, że pamięć będzie _waszczona_, to także, że jeśli serializuje się binarnie tę strukturę danych, a następnie ją odserializujesz, powiedzmy na maszynie 32-bitowej, będziesz mieć tę tajemniczą wyściółkę, mógł nie znać wstawionego kompilatora. Chociaż nie serializowałbyś binarnie 'std :: map', nadal. Gdyby to była jakaś paczka prymitywów, która by się pożyczyła na łatwą serializację binarną, mogłaś to zrobić. Kompilator chce, abyś wiedział, że 'smap' będzie 4 bajty dalej, niż myślisz, że go umieścisz. – bobobobo