Według mojego zrozumienia intpoczątkowo miał to być "natywny" typ liczby całkowitej z dodatkową gwarancją, że powinien mieć rozmiar co najmniej 16 bitów - coś, co było wtedy uważane za "rozsądny" rozmiar.
Gdy platformy 32-bitowe stały się bardziej popularne, możemy powiedzieć, że „rozsądny” rozmiar zmienił się na 32 bity:
- Nowoczesny system Windows używa wersji 32-bitowej
intna wszystkich platformach.
- POSIX gwarantuje, że
intjest to co najmniej 32 bity.
- C # Java ma typ,
intktóry gwarantuje dokładnie 32 bity.
Ale kiedy platforma 64-bitowa stała się normą, nikt nie rozwinął się intdo 64-bitowej liczby całkowitej z powodu:
- Przenośność: wiele kodu zależy od
int32-bitowego rozmiaru.
- Zużycie pamięci: podwojenie użycia pamięci dla każdego
intmoże być nieracjonalne w większości przypadków, ponieważ w większości przypadków używane liczby są znacznie mniejsze niż 2 miliardy.
Teraz, dlaczego wolisz uint32_tsię uint_fast32_t? Z tego samego powodu języki, C # i Java zawsze używają liczb całkowitych o stałym rozmiarze: programista nie pisze kodu z myślą o możliwych rozmiarach różnych typów, piszą dla jednej platformy i testują kod na tej platformie. Większość kodu niejawnie zależy od określonych rozmiarów typów danych. I dlatego uint32_tw większości przypadków jest lepszym wyborem - nie pozwala na niejednoznaczność co do jego zachowania.
Co więcej, czy uint_fast32_trzeczywiście jest to najszybszy typ na platformie o rozmiarze równym lub większym niż 32 bity? Nie całkiem. Rozważmy ten kompilator kodu przez GCC dla x86_64 w systemie Windows:
extern uint64_t get(void);
uint64_t sum(uint64_t value)
{
return value + get();
}
Wygenerowany zespół wygląda następująco:
push
sub $0x20,
mov
callq d <sum+0xd>
add
add $0x20,
pop
retq
Teraz, jeśli zmienisz get()wartość zwracaną na uint_fast32_t(czyli 4 bajty w systemie Windows x86_64), otrzymasz to:
push %rbx
sub $0x20,%rsp
mov %rcx,%rbx
callq d <sum+0xd>
mov %eax,%eax ; <-- additional instruction
add %rbx,%rax
add $0x20,%rsp
pop %rbx
retq
Zwróć uwagę, że wygenerowany kod jest prawie taki sam, z wyjątkiem dodatkowych mov %eax,%eaxinstrukcji po wywołaniu funkcji, które mają na celu rozszerzenie wartości 32-bitowej do wartości 64-bitowej.
Nie ma takiego problemu, jeśli używasz tylko wartości 32-bitowych, ale prawdopodobnie będziesz używać tych ze size_tzmiennymi (prawdopodobnie rozmiary tablic?), A są to 64 bity na x86_64. W Linuksie uint_fast32_tjest 8 bajtów, więc sytuacja jest inna.
Wielu programistów używa go, intgdy muszą zwrócić małą wartość (powiedzmy w zakresie [-32,32]). To działałoby idealnie, gdyby intbył to natywny rozmiar całkowitych platformy, ale ponieważ nie ma go na platformach 64-bitowych, lepszym wyborem jest inny typ, który pasuje do natywnego typu platformy (chyba że jest często używany z innymi liczbami całkowitymi o mniejszym rozmiarze).
Zasadniczo, niezależnie od tego, co mówi standard, i tak uint_fast32_tjest zepsuty w niektórych implementacjach. Jeśli zależy Ci na dodatkowych instrukcjach generowanych w niektórych miejscach, powinieneś zdefiniować własny „natywny” typ liczby całkowitej. Lub możesz użyć size_tdo tego celu, ponieważ zwykle będzie pasował do nativerozmiaru (nie uwzględniam starych i niejasnych platform, takich jak 8086, tylko platformy, które mogą obsługiwać Windows, Linux itp.).
Innym znakiem, który pokazuje, że intpowinien być natywny typ liczby całkowitej, jest „reguła promocji liczby całkowitej”. Większość procesorów może wykonywać operacje tylko na rodzimym komputerze, więc 32-bitowy procesor zwykle może wykonywać tylko 32-bitowe dodawanie, odejmowanie itp. (Wyjątkiem są procesory Intel). Typy liczb całkowitych innych rozmiarów są obsługiwane tylko przez instrukcje ładowania i przechowywania. Na przykład, wartość 8-bitowa powinna zostać załadowana odpowiednią instrukcją „załaduj 8-bitowy znak ze znakiem” lub „załaduj 8-bitowy bez znaku”, a po załadowaniu wartość zostanie rozszerzona do 32 bitów. Bez reguły C promowania liczb całkowitych kompilatory musiałyby dodać trochę więcej kodu dla wyrażeń, które używają typów mniejszych niż typ natywny. Niestety, nie ma to już miejsca w przypadku architektur 64-bitowych, ponieważ kompilatory muszą teraz w niektórych przypadkach emitować dodatkowe instrukcje (jak pokazano powyżej).