W kilku przykładach C ++ widzę użycie typu, w size_t
którym użyłbym prostego int
. Jaka jest różnica i dlaczego size_t
powinno być lepiej?
W kilku przykładach C ++ widzę użycie typu, w size_t
którym użyłbym prostego int
. Jaka jest różnica i dlaczego size_t
powinno być lepiej?
Odpowiedzi:
Pliki nagłówkowe stdlib.h i stddef.h definiują typ danych o nazwie size_t, który jest używany do reprezentowania rozmiaru obiektu. Funkcje biblioteczne, które przyjmują rozmiary, oczekują, że będą one typu size_t, a operator sizeof oblicza rozmiar_t.
Rzeczywisty typ size_t jest zależny od platformy; częstym błędem jest założenie, że size_t jest tym samym, co unsigned int, co może prowadzić do błędów w programowaniu, zwłaszcza gdy coraz bardziej rozpowszechnione są architektury 64-bitowe.
Sprawdź też, dlaczego rozmiar_t ma znaczenie
/usr/include/stdlib.h
pobiera definicję skąd /usr/lib/gcc/x86_64-redhat-linux/5.3.1/include/stddef.h
i tam jest ona ustawiona domyślnie, long unsigned int
chyba że inny plik nagłówkowy mówi inaczej.
size_t to typ używany do reprezentowania rozmiarów (jak sugeruje jego nazwa). Jego platforma (a nawet potencjalnie implementacja) jest zależna i powinien być używany tylko w tym celu. Oczywiście, reprezentując rozmiar, size_t jest bez znaku. Wiele funkcji z biblioteki standardowej, w tym malloc, sizeof i różne funkcje operacji na łańcuchach, używa size_t jako typu danych.
Wartość int jest domyślnie podpisywana i chociaż jego rozmiar jest również zależny od platformy, na większości nowoczesnych maszyn będzie to stały 32-bitowy (i chociaż size_t to 64 bity w architekturze 64-bitowej, na tych architekturach wartość int pozostaje 32-bitowa).
Podsumowując: użyj size_t, aby przedstawić rozmiar obiektu i int (lub long) w innych przypadkach.
size_t
Typu określa się jako unsigned integralną typu sizeof
operatora. W prawdziwym świecie często widzisz int
definicję jako 32 bity (dla kompatybilności wstecznej), ale size_t
zdefiniowaną jako 64 bity (więc możesz zadeklarować tablice i struktury o rozmiarze większym niż 4 GiB) na platformach 64-bitowych. Jeśli a long int
jest również 64-bitowy, nazywa się to konwencją LP64; jeśli long int
ma 32 bity, ale long long int
wskaźniki mają 64 bity, to jest LLP64. Możesz również otrzymać odwrotną stronę, program, który używa 64-bitowych instrukcji prędkości, ale 32-bitowych wskaźników do oszczędzania pamięci. Ponadto int
jest podpisany i size_t
niepodpisany.
Historycznie istniało wiele innych platform, na których adresy były szersze lub krótsze niż natywny rozmiar int
. W rzeczywistości w latach 70. i na początku 80. było to bardziej powszechne niż nie: wszystkie popularne 8-bitowe mikrokomputery miały 8-bitowe rejestry i 16-bitowe adresy, a przejście między 16 a 32 bitami również tworzyło wiele maszyn, które mieli adresy szersze niż ich rejestry. Czasami wciąż widzę tutaj pytania o Borland Turbo C dla MS-DOS, którego tryb ogromnej pamięci miał 20-bitowe adresy przechowywane w 32 bitach na 16-bitowym procesorze (ale które mogą obsługiwać 32-bitowy zestaw instrukcji 80386); Motorola 68000 miała 16-bitową jednostkę ALU z 32-bitowymi rejestrami i adresami; istniały komputery mainframe IBM z 15-bitowymi, 24-bitowymi lub 31-bitowymi adresami. Nadal widzisz różne rozmiary ALU i magistrali adresowej w systemach wbudowanych.
Za każdym razem, gdy int
jest mniejszy niż size_t
i próbujesz zapisać rozmiar lub przesunięcie bardzo dużego pliku lub obiektu w pliku unsigned int
, istnieje możliwość, że może się przepełnić i spowodować błąd. W przypadku znaku int
istnieje również możliwość uzyskania liczby ujemnej. Jeśli int
lub unsigned int
jest szerszy, program będzie działał poprawnie, ale marnuje pamięć.
Jeśli chcesz mieć możliwość przenoszenia, na ogół powinieneś używać odpowiedniego typu do tego celu. Wiele osób poleci używanie matematyki ze znakiem zamiast bez znaku (aby uniknąć nieprzyjemnych, subtelnych błędów, takich jak 1U < -3
). W tym celu Standard definiuje biblioteczne ptrdiff_t
w <stddef.h>
tak podpisanej typu wyniku odjęcia wskaźnik od drugiego.
To powiedziawszy, obejściem może być sprawdzenie granic wszystkich adresów i przesunięć względem INT_MAX
i albo 0
lub INT_MIN
odpowiednio, i włączenie ostrzeżeń kompilatora o porównywaniu podpisanych i niepodpisanych ilości na wypadek, gdybyś je pominął. Zawsze powinieneś zawsze sprawdzać dostęp do tablicy pod kątem przepełnienia w C.
Dzieje się tak, ponieważ size_t może być czymkolwiek innym niż int (może być strukturą). Chodzi o to, że oddziela swoje zadanie od typu podstawowego.
size_t
jest określony jako typ liczby całkowitej bez znaku . C11 §6.5.3.4 5 „Wartość wyniku obu operatorów ( sizeof
_Alignof
) jest zdefiniowana w ramach implementacji, a jego typ (liczba całkowita bez znaku) to size_t
,”.
Definicja SIZE_T
znajduje się na:
https://msdn.microsoft.com/en-us/library/cc441980.aspx i https://msdn.microsoft.com/en-us/library/cc230394.aspx
Wklejanie tutaj wymaganych informacji:
SIZE_T
jest ULONG_PTR
reprezentowanie maksymalną liczbę bajtów, do którego wskaźnik może wskazywać.
Ten typ jest deklarowany w następujący sposób:
typedef ULONG_PTR SIZE_T;
A ULONG_PTR
to typ długi bez znaku, używany do dokładności wskaźnika. Jest używany podczas rzutowania wskaźnika na długi typ w celu wykonania arytmetyki wskaźnika.
Ten typ jest deklarowany w następujący sposób:
typedef unsigned __int3264 ULONG_PTR;
SIZE_T
nie jest size_t
, o co pytał OP.
SIZE_T
jest zupełnie inny niż size_t
. Nie możesz zadeklarować zmiennej typu SIZE_T
.