Odpowiedzi:
size_t
Typem jest unsigned typu Liczba całkowita, która jest wynikiem sizeof
operatora (a offsetof
operatorem), więc na pewno będzie wystarczająco duży, aby zawierać wielkość największego obiektu system może obsłużyć (np statyczna tablica z 8GB).
size_t
Typ może być większa niż, równa lub mniejsza od unsigned int
i kompilator może przyjąć założenia o tym do optymalizacji.
Dokładniejsze informacje można znaleźć w standardzie C99, sekcja 7.17, którego wersja robocza jest dostępna w Internecie w formacie pdf , lub w standardzie C11, sekcja 7.19, dostępna również jako wersja pdf .
size_t
mogą reprezentować! Jeśli nie, to kto?
Klasyczny C (wczesny dialekt języka C opisany przez Briana Kernighana i Dennisa Ritchie w The C Programming Language, Prentice-Hall, 1978) nie zapewniał size_t
. Komitet normalizacyjny C wprowadził w size_t
celu wyeliminowania problemu przenoszenia
Szczegółowo wyjaśnione na stronie embedded.com (z bardzo dobrym przykładem)
Krótko mówiąc, size_t
nigdy nie jest ujemny i maksymalizuje wydajność, ponieważ zostałby wpisany jako liczba całkowita bez znaku, która jest wystarczająco duża - ale nie za duża - aby reprezentować rozmiar możliwie największego obiektu na platformie docelowej.
Rozmiary nigdy nie powinny być ujemne i rzeczywiście size_t
są typem niepodpisanym. Ponadto, ponieważ size_t
jest bez znaku, możesz przechowywać liczby, które są około dwa razy większe niż w odpowiednim typie ze znakiem, ponieważ możemy użyć bitu znaku do reprezentowania wielkości, podobnie jak wszystkich innych bitów w liczbie całkowitej bez znaku. Kiedy zyskujemy jeszcze jeden bit, mnożymy zakres liczb, które możemy reprezentować, około dwa razy.
Pytasz więc, dlaczego po prostu nie użyć unsigned int
? Może nie być w stanie pomieścić wystarczająco dużych liczb. W implementacji, w której unsigned int
jest 32 bity, największa liczba, jaką może reprezentować, to 4294967295
. Niektóre procesory, takie jak IP16L32, mogą kopiować obiekty większe niż 4294967295
bajty.
Pytasz więc, dlaczego nie użyć unsigned long int
? Wymaga opłaty za wydajność na niektórych platformach. Standard C wymaga long
zajmowania co najmniej 32 bitów. Platforma IP16L32 implementuje każdą 32-bitową długość jako parę 16-bitowych słów. Prawie wszyscy operatorzy 32-bitowi na tych platformach wymagają dwóch instrukcji, jeśli nie więcej, ponieważ pracują z 32 bitami w dwóch 16-bitowych porcjach. Na przykład przenoszenie 32-bitowej długości zwykle wymaga dwóch instrukcji maszyny - po jednej do przeniesienia każdej 16-bitowej porcji.
Używanie size_t
pozwala uniknąć tego kosztu wydajności. Zgodnie z tym fantastycznym artykułem : „Type size_t
to typedef, który jest aliasem dla jakiegoś typu liczb całkowitych bez znaku, zazwyczaj unsigned int
lub unsigned long
, ale być może nawet unsigned long long
. Każda implementacja Standard C ma wybierać liczbę całkowitą bez znaku, która jest wystarczająco duża - ale nie większa niż potrzeba - reprezentować rozmiar największego możliwego obiektu na platformie docelowej. ”
unsigned int
puszce i różni się w zależności od systemu. Wymagane jest co najmniej 65536
, ale często 4294967295
i może być 18446744073709551615
(2 ** 64-1) w niektórych systemach.
unsigned char
). Standard nie wydaje się zawierać łańcucha „65535” ani „65536” nigdzie, a „+32767” występuje tylko (1,9: 9) w nucie jako możliwie największa liczba całkowita reprezentowana w int
; nie ma żadnej gwarancji, że INT_MAX
nie może być mniejsza!
Typ size_t to typ zwracany przez operator sizeof. Jest to liczba całkowita bez znaku, która jest w stanie wyrazić rozmiar w bajtach dowolnego zakresu pamięci obsługiwanego przez maszynę hosta. Jest (zazwyczaj) związany z ptrdiff_t, ponieważ ptrdiff_t jest liczbą całkowitą ze znakiem, taką, że sizeof (ptrdiff_t) i sizeof (size_t) są równe.
Pisząc kod C, zawsze powinieneś używać size_t, gdy masz do czynienia z zakresami pamięci.
Z drugiej strony typ int jest zasadniczo zdefiniowany jako wielkość (całkowitej) wartości (podpisanej), którą maszyna hosta może wykorzystać do najbardziej wydajnego wykonywania arytmetyki liczb całkowitych. Na przykład na wielu starszych komputerach typu PC wartość sizeof (size_t) wynosiłaby 4 (bajty), ale sizeof (int) wynosiłaby 2 (bajty). 16-bitowa arytmetyka była szybsza niż 32-bitowa arytmetyka, chociaż procesor mógł obsłużyć (logiczną) przestrzeń pamięci do 4 GiB.
Używaj typu int tylko wtedy, gdy zależy Ci na wydajności, ponieważ jej rzeczywista precyzja zależy w dużym stopniu zarówno od opcji kompilatora, jak i architektury maszyny. W szczególności standard C określa następujące niezmienniki: sizeof (char) <= sizeof (short) <= sizeof (int) <= sizeof (long) nie nakładając żadnych innych ograniczeń na rzeczywistą reprezentację precyzji dostępnej dla programisty dla każdego z te prymitywne typy.
Uwaga: NIE jest to to samo, co w Javie (która faktycznie określa precyzję bitową dla każdego typu „char”, „byte”, „short”, „int” i „long”).
size_t
Jest w stanie reprezentować rozmiar dowolnego pojedynczego obiektu (np. Liczba, tablica, struktura). Cały zakres pamięci może przekroczyćsize_t
size_t
- Mam nadzieję, że nie masz tego na myśli. Przez większość czasu nie mamy do czynienia z tablicami, w których liczy się liczność przestrzeni adresowej + przenośność. W takich przypadkach weźmiesz size_t
. W każdym innym przypadku bierzesz indeksy z (podpisanych) liczb całkowitych. Ponieważ zamieszanie (które pojawia się bez ostrzeżenia) wynikające z nieoczekiwanego niedomykania zachowania niepodpisanych jest częstsze i gorsze niż problemy z przenośnością, które mogą wystąpić w innych przypadkach.
Wpisz rozmiar_t musi być wystarczająco duży, aby przechowywać rozmiar dowolnego możliwego obiektu. Unsigned int nie musi spełniać tego warunku.
Na przykład w systemach 64-bitowych int i unsigned int mogą mieć szerokość 32 bitów, ale rozmiar_t musi być wystarczająco duży, aby przechowywać liczby większe niż 4G
size_t
musiałoby być tak duże, gdyby kompilator mógł zaakceptować typ X taki, że sizeof (X) dałoby wartość większą niż 4G. Większość kompilatorów odrzuciłaby np. typedef unsigned char foo[1000000000000LL][1000000000000LL]
A nawet foo[65536][65536];
mogłaby zostać legalnie odrzucona, gdyby przekroczyła udokumentowany limit zdefiniowany w implementacji.
Ten fragment podręcznika glibc 0.02 może być również istotny podczas badania tematu:
Istnieje potencjalny problem z typem size_t i wersjami GCC przed wydaniem 2.4. ANSI C wymaga, aby size_t zawsze był typem bez znaku. W celu zapewnienia zgodności z plikami nagłówkowymi istniejących systemów GCC definiuje rozmiar_t w stddef.h' to be whatever type the system's
sys / types.h 'definiuje go jako. Większość systemów uniksowych, które definiują size_t w `sys / types.h ', definiują go jako typ podpisany. Niektóre kody w bibliotece zależą od tego, że size_t jest typem bez znaku i nie będzie działać poprawnie, jeśli zostanie podpisane.
Kod biblioteki GNU C, który oczekuje, że rozmiar_t będzie bez znaku, jest poprawny. Definicja size_t jako typu podpisanego jest niepoprawna. Planujemy, że w wersji 2.4 GCC zawsze zdefiniuje size_t jako typ bez znaku, a fixincludes' script will massage the system's
sys / types.h ', aby nie kolidować z tym.
W międzyczasie omawiamy ten problem, mówiąc GCC wprost, aby używał typu niepodpisanego dla size_t podczas kompilacji biblioteki GNU C. `config 'automatycznie wykryje, jakiego typu GCC używa dla size_t, aby go zastąpić, jeśli to konieczne.
Jeśli mój kompilator jest ustawiony na 32 bity, size_t
nie jest niczym innym jak typedef dla unsigned int
. Jeśli mój kompilator jest ustawiony na 64-bitowy, size_t
nie jest niczym innym jak typedef dla unsigned long long
.
unsigned long
dla obu przypadków w niektórych systemach operacyjnych.
size_t to rozmiar wskaźnika.
Tak więc w 32 bitach lub wspólnym modelu ILP32 (liczba całkowita, długi, wskaźnik) size_t wynosi 32 bity. oraz w 64 bitach lub wspólny model LP64 (długi, wskaźnik) size_t wynosi 64 bity (liczby całkowite to nadal 32 bity).
Istnieją inne modele, ale są to te, których używa g ++ (przynajmniej domyślnie)
size_t
niekoniecznie jest tego samego rozmiaru co wskaźnik, choć zwykle jest. Wskaźnik musi być w stanie wskazywać dowolne miejsce w pamięci; size_t
musi być wystarczająco duży, aby reprezentować rozmiar największego pojedynczego obiektu.