Zauważam, że współczesny kod C i C ++ wydaje się używać size_t
zamiast int
/ unsigned int
prawie wszędzie - od parametrów funkcji łańcucha C po STL. Jestem ciekawy przyczyny tego i korzyści, jakie przynosi.
492
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ł wsize_t
celu wyeliminowania problemu przenoszeniaSzczegółowo wyjaśnione na stronie embedded.com (z bardzo dobrym przykładem)
źródło
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órejunsigned int
jest 32 bity, największa liczba, jaką może reprezentować, to4294967295
. 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 wymagalong
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 : „Typesize_t
to typedef, który jest aliasem dla jakiegoś typu liczb całkowitych bez znaku, zazwyczajunsigned int
lubunsigned long
, ale być może nawetunsigned 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. ”źródło
unsigned int
puszce i różni się w zależności od systemu. Wymagane jest co najmniej65536
, ale często4294967295
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 wint
; nie ma żadnej gwarancji, żeINT_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”).
źródło
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źmieszsize_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
źródło
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 nawetfoo[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.
źródło
Jeśli mój kompilator jest ustawiony na 32 bity,
size_t
nie jest niczym innym jak typedef dlaunsigned int
. Jeśli mój kompilator jest ustawiony na 64-bitowy,size_t
nie jest niczym innym jak typedef dlaunsigned long long
.źródło
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)
źródło
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.