2012-07-08 7 views
33

Jaka jest różnica między sizeof i alignof?Jaka jest różnica między sizeof i alignof?

#include <iostream> 

#define SIZEOF_ALIGNOF(T) std::cout<< sizeof(T) << '/' << alignof(T) << std::endl 

int main(int, char**) 
{ 
     SIZEOF_ALIGNOF(unsigned char); 
     SIZEOF_ALIGNOF(char); 
     SIZEOF_ALIGNOF(unsigned short int); 
     SIZEOF_ALIGNOF(short int); 
     SIZEOF_ALIGNOF(unsigned int); 
     SIZEOF_ALIGNOF(int); 
     SIZEOF_ALIGNOF(float); 
     SIZEOF_ALIGNOF(unsigned long int); 
     SIZEOF_ALIGNOF(long int); 
     SIZEOF_ALIGNOF(unsigned long long int); 
     SIZEOF_ALIGNOF(long long int); 
     SIZEOF_ALIGNOF(double); 
} 

wyjście wola

1/1 1/1 2/2 2/2 4/4 4/4 4/4 4/4 4/4 8/8 8/8 8/8

Myślę, że nie rozumiem, jakie jest dopasowanie ...?

+16

Spróbuj ponownie za pomocą struktur, zamiast rodzimych typów. –

+1

'Zwraca wyrównanie w bajtach (liczba całkowita dwóch) wymagana dla każdej instancji danego typu" - http://pl.cppreference.com/w/cpp/language/alignof. 'sizeof' po prostu podaje rozmiar, oczywiście w bajtach. – chris

+1

Być może warto wspomnieć - [sizeof jest zawsze wielokrotnością alignof] (http://stackoverflow.com/questions/4637774/is-s-size-of-a-ctct-required-to-be-an-exact-multiple -of-the-wyrównanie-of-tha) – Steve314

Odpowiedz

3

Parametr sizeof operator podaje rozmiar w bajtach rzeczywistego typu lub instancji typu.

Ustawienie alignof operator daje wyrównanie w bajtach wymagane dla każdej instancji danego typu.

+6

co to jest "wyrównanie"? – user1494506

14

Dwóch operatorów robi zasadniczo różne rzeczy. sizeof podaje rozmiar typu (ile zajmuje pamięci), podczas gdy alignof podaje, do ilu bajtów typ musi zostać wyrównany. Tak się składa, że ​​testowane prymitywy mają wymaganie wyrównania takie same jak ich rozmiar (co ma sens, jeśli się nad tym zastanowić).

Pomyśl o tym, co się dzieje, jeśli masz struct Zamiast:

struct Foo { 
    int a; 
    float b; 
    char c; 
}; 

alignof(Foo) powróci 4.

+1

dlaczego? co oznacza "alignof"? – user1494506

+1

@tskuzzy powiedziałeś "alignof (Foo)' zwróci 4. Ale to zależy od docelowego ABI. Tak więc może to być prawda na ia32 (x86), ale nie na ARM, MIPS, PowerPC, itp. – ydroneaud

+3

4 ??? Naprawdę nie rozumiem – Offirmo

54

Cóż, "pamięć" jest po prostu ogromna tablica bajtów. Jednak większość większych rzeczy, takich jak liczby całkowite, potrzebuje więcej niż 1 bajt do ich zapisania - na przykład wartość 32-bitowa będzie wykorzystywać 4 kolejne bajty pamięci.

Teraz moduły pamięci w komputerze nie są zwykle "bajtami"; są również zorganizowane z kilkoma bajtami "równolegle", jak bloki 4 bajty.

Dla procesora, jest dużo łatwiej = bardziej wydajne = lepsza wydajność, aby nie „krzyż” takie blokowe granice podczas czytania coś jak integer:

memory byte 0 1 2 3  4 5 6 7  8 9 10 11 
integer  goooood 
        baaaaaaaaad 

to co mówi „wyrównanie”: AN wyrównanie 4 oznacza, że ​​dane tego typu powinny (lub muszą, zależnie od CPU) być przechowywane począwszy od adresu będącego wielokrotnością 4.

Obserwujesz, że sizeof == alignof jest nieprawidłowe; spróbuj struktur. Struktury również zostaną wyrównane (ponieważ ich poszczególni członkowie muszą trafić na właściwe adresy), ale ich rozmiar będzie znacznie większy.

+14

Dodatkowy punkt - chociaż x86 wykona niepodpisane odczyty i zapisy (powoli, ale poprawnie) dla większości rzeczy, niektóre architektury wymagają wyrównania wszystkich operacji, a nawet w x86 istnieją specjalne przypadki, które muszą być wyrównane (SIMD instrukcje, myślę). – Steve314

+0

dzięki! teraz dostaję to – user1494506

+2

@ user1494506 - Jeśli ta lub jakakolwiek inna odpowiedź poprawnie odpowie na twoje pytanie, * rozważ * zaznaczenie jej poprawnością. (Oczywiście jest to * wyłącznie twój wybór * Nie bądź zmuszany do przyjmowania odpowiedzi, ponieważ ludzie tak mówią (np. Tak jak mówię teraz)) :) – ArjunShankar

5

Wartość alignof jest taka sama jak wartość sizeof dla typów podstawowych.

Różnica polega na używanych zdefiniowanych typach danych, takich jak użycie struct; dla np.

typedef struct { int a; double b; } S; 
//cout<<alignof(s);        outputp: 8; 
//cout<<sizeof(S);        output: 12; 

dlatego wielkość sizeof jest całkowitą wielkością wymaganą dla danego typu danych; a wartość alignof jest wymaganiem wyrównania największego elementu w strukturze.

Użycie opcji alignof: przydziela pamięć na określonej granicy wyrównania.

6

Stare pytanie (chociaż nie jest oznaczone jako odebrane), ale ten przykład pokazuje różnicę nieco bardziej wyraźną w uzupełnieniu do odpowiedzi Christiana Stiebera. Także odpowiedź Meluha zawiera błąd jak sizeof (S) wyjście jest 16 nie 12.

// c has to occupy 8 bytes so that d (whose size is 8) starts on a 8 bytes boundary 
//   | 8 bytes | | 8 bytes | | 8 bytes | 
struct Bad { char c;  double d;  int i;  }; 
cout << alignof(Bad) << " " << sizeof(Bad) << endl; // 8 24 

//    | 8 bytes | | 8 bytes |  
struct Good { double d;  int i; char c;   }; 
cout << alignof(Good) << " " << sizeof(Good) << endl; // 8 16 

To również pokazuje, że jest to najlepsze członków zamawiasz przez wielkości z największych pierwszy (double w tym przypadku), jak inni członkowie są ograniczony przez tego członka.

2

Dla udzielonych odpowiedzi wydaje się, że istnieje pewne niejasności co do tego, czym jest wyrównywanie. Zamieszanie powstaje prawdopodobnie dlatego, że istnieją dwa rodzaje wyrównania.

1. Wyrównanie Użytkownik

Jest to jakościowy środek, który precyzuje, jak duże instancja jest w bajtach dla konkretnego zamawiającego członków ciągu typu struktura/klasa. Ogólnie rzecz biorąc, kompilatory mogą kompaktować instancje struktury/klasy, jeśli elementy są uporządkowane według ich wielkości bajtów w porządku malejącym (tj. Najwięksi pierwsi, najmniejsi członkowie ostatni) w strukturze. Rozważ:

struct A 
{ 
    char c; float f; short s; 
}; 

struct B 
{ 
    float f; short s; char c; 
}; 

Obie struktury zawierają dokładnie te same informacje. Ze względu na ten przykład; typ float zajmuje 4 bajty, krótki typ zajmuje 2, a postać zajmuje 1 bajt. Jednak pierwsza struktura A ma członków w kolejności losowej, podczas gdy druga struktura B porządkuje członków zgodnie z ich rozmiarem bajtów (może to być inne na niektórych architekturach, zakładam architekturę x86 intel CPU z 4-bajtowym wyrównaniem w tym przykładzie). Teraz pod uwagę wielkość struktur:

printf("size of A: %d", sizeof (A)); // size of A: 12; 
printf("size of B: %d", sizeof (B)); // size of B: 8; 

Jeśli można oczekiwać rozmiar będzie 7 bajtów, można byłoby przy założeniu, że członkowie są pakowane do konstrukcji za pomocą wyrównanie 1-bajtowy. Podczas gdy niektóre kompilatory na to zezwalają, na ogół większość kompilatorów używa 4-bajtowych lub nawet 8-bajtowych dopasowań z powodów historycznych (większość pracy procesora przy użyciu rejestrów ogólnego przeznaczenia DWORD (double-word) lub QWORD (quad-word)).

Istnieją 2 mechanizmy wyściółki w pracy, aby osiągnąć opakowanie.

  1. pierwsze, każdy element, który ma rozmiar mniejszy niż bajt bajt jest wyrównanie „połączonej” do następnego elementu (ów), jeśli uzyskany rozmiar bajtów jest mniejsza lub równa bajty wyrównania. W strukturze B elementy s i c można łączyć w ten sposób; ich łączny rozmiar wynosi 2 bajty dla s + 1 bajta dla c == 3 bajty < = wyrównanie 4 bajtowe. W przypadku struktury A nie ma takiego scalania, a każdy element efektywnie zużywa 4 bajty w strukturze opakowania.

  2. Łączna wielkość struktury jest ponownie dopełniana, aby następna struktura mogła rozpocząć się na granicy wyrównania. W przykładzie B całkowita liczba bajtów wynosiłaby 7. Następna 4-bajtowa granica leży na bajcie 8, dlatego struktura jest wypełniona 1 bajtem, aby umożliwić przydział tablicy jako napięty ciąg instancji.

Należy zauważyć, że Visual C++/GCC umożliwiają różne wyrównania 1 bajtu, 2 i wyższych wielokrotności 2 bajtów.Zrozum, że działa to wbrew możliwości kompilatora do tworzenia optymalnego kodu dla architektury. Rzeczywiście, w poniższym przykładzie każdy bajt byłby czytany jako pojedynczy bajt przy użyciu instrukcji jednobajtowej dla każdej operacji odczytu. W praktyce sprzęt będzie nadal pobierał całą linię pamięci zawierającą każdy bajt odczytany w pamięci podręcznej i wykonuje instrukcję 4 razy, nawet jeśli 4 bajty znajdują się w tym samym DWORD i mogą być załadowane do rejestru CPU w 1 instrukcji.

#pragma pack(push,1) 
struct Bad 
{ 
    char a,b,c,d; 
}; 
#pragma pack(pop) 

2. wyrównanie Przydział

ten jest ściśle związany z mechanizmem 2 do napawania opisane w poprzedniej sekcji, jednak środki przydzielone trasowania może być określona w wariantach malloc/memalloc funkcje alokacji. W związku z tym możliwe jest przydzielenie obiektu przy innej (zazwyczaj większej niż wielokrotność) granicy wyrównania niż sugeruje to wyrównanie bajtów typu struktura/obiekt.

size_t blockAlignment = 4*1024; // 4K page block alignment 
void* block = malloc(sizeof(T) * count, blockAlignment); 

Kod umieści blok przypadkach morfologii typu T adresów, które kończą się na wielokrotności 4096.

Powodem stosowania takich dopasowań alokacji ponownie wyłącznie architektonicznych. Na przykład czytanie i pisanie bloków z adresów wyrównanych do strony jest szybsze, ponieważ zakres adresów pasuje do warstw pamięci podręcznej. Zakresy podzielone na różne "strony" powodują usunięcie zawartości pamięci podręcznej podczas przekraczania granicy strony. Różne nośniki (architektury magistrali) mają różne wzorce dostępu i mogą korzystać z różnych dopasowań. Zasadniczo wyrównania wielkości stron o rozmiarach 4, 16, 32 i 64 K nie są rzadkie.