Zastanawiam się, czy powinienem używać std::size_t
pętli i innych rzeczy zamiast int
? Na przykład:
#include <cstdint>
int main()
{
for (std::size_t i = 0; i < 10; ++i) {
// std::size_t OK here? Or should I use, say, unsigned int instead?
}
}
Ogólnie, jaka jest najlepsza praktyka dotycząca tego, kiedy stosować std::size_t
?
size_t
kiedy należy może prowadzić do błędów bezpieczeństwa .ssize_t
dla podpisanych wartości.size_t
jest typem wynikusizeof
operatora.Użyj
size_t
dla zmiennych, które modelują rozmiar lub indeks w tablicy.size_t
przekazuje semantykę: od razu wiesz, że reprezentuje rozmiar w bajtach lub indeksie, a nie tylko inną liczbę całkowitą.Ponadto użycie
size_t
do reprezentowania rozmiaru w bajtach pomaga uczynić kod przenośnym.źródło
size_t
Typu służy do określenia rozmiaru czegoś więc to naturalne, aby go użyć, na przykład, coraz długość łańcucha, a następnie przetwarza każdy znak:Ci nie muszą zwracać uwagę na warunki brzegowe oczywiście, ponieważ jest to typ unsigned. Granica na końcu górnej zwykle nie jest to ważne, ponieważ maksymalna to zazwyczaj duże (choć jest możliwe, aby się tam dostać). Większość ludzi po prostu używa
int
tego typu rzeczy, ponieważ rzadko mają struktury lub tablice, które stają się wystarczająco duże, aby przekroczyć ich możliwościint
.Ale uważaj na takie rzeczy jak:
co spowoduje nieskończoną pętlę z powodu zawijania się niepodpisanych wartości (chociaż widziałem, że kompilatory ostrzegają przed tym). Można to również złagodzić poprzez (nieco trudniejsze do zrozumienia, ale przynajmniej odporne na problemy z owijaniem):
Przesunięcie dekrementacji na efekt uboczny warunku kontynuacji po sprawdzeniu powoduje sprawdzenie kontynuacji wartości przed dekrementacją, ale nadal korzysta ze zmniejszonej wartości w pętli (dlatego pętla działa
len .. 1
raczej niżlen-1 .. 0
).źródło
strlen
każdej iteracji pętli. :) Możesz zrobić coś takiego:for (size_t i = 0, len = strlen(str); i < len; i++) ...
for (size_t i = strlen (str); i --> 0;)
-->
operatora „idzie do” (patrz stackoverflow.com/questions/1642028/... ). Włączyłem twoją sugestię do odpowiedzi.if (i == 0) break;
na końcu pętli for (npfor (size_t i = strlen(str) - 1; ; --i)
. (Ale bardziej lubię twój, ale zastanawiam się, czy to też by zadziałało).Z definicji
size_t
jest wynikiemsizeof
operatora.size_t
został stworzony w celu odniesienia do rozmiarów.Liczba przypadków, w których coś robisz (w twoim przykładzie 10), nie dotyczy rozmiarów, więc po co używać
size_t
?int
lubunsigned int
powinien być w porządku.Oczywiście istotne jest również to, co robisz
i
w pętli. Jeśli przekażesz go do funkcji, która bierzeunsigned int
na przykład pickunsigned int
.W każdym razie zalecam unikanie konwersji typu niejawnego. Wyraźnie wszystkie konwersje typów.
źródło
size_t
jest bardzo czytelnym sposobem na określenie wymiaru rozmiaru elementu - długości łańcucha, ilości bajtów, które zajmuje wskaźnik itp. Jest także przenośny na różnych platformach - przekonasz się, że 64-bitowy i 32-bitowy zachowują się ładnie dzięki funkcjom systemowym isize_t
- coś, counsigned int
może nie zrobić (np. kiedy należy użyćunsigned long
źródło
krótka odpowiedź:
prawie nigdy
długa odpowiedź:
Ilekroć musisz mieć wektor char większy niż 2 GB w systemie 32-bitowym. W każdym innym przypadku użycie podpisanego typu jest znacznie bezpieczniejsze niż użycie niepodpisanego typu.
przykład:
Podpisany odpowiednik
size_t
toptrdiff_t
nieint
. Aleint
w większości przypadków używanie jest znacznie lepsze niż size_t.ptrdiff_t
jestlong
w systemach 32 i 64-bitowych.Oznacza to, że zawsze musisz konwertować do iz size_t za każdym razem, gdy wchodzisz w interakcje ze std :: container, co nie jest zbyt piękne. Ale podczas trwającej konferencji natywnej autorzy c ++ wspomnieli, że zaprojektowanie std :: vector z niepodpisanym size_t było błędem.
Jeśli Twój kompilator wyświetla ostrzeżenia o niejawnych konwersjach z ptrdiff_t na size_t, możesz to wyraźnie wyrazić za pomocą składni konstruktora:
jeśli chcesz po prostu iterować kolekcję, bez sprawdzania granic, użyj zakresu opartego na:
tutaj kilka słów od Bjarne Stroustrup (autor C ++) na temat przejścia na język ojczysty
Dla niektórych osób ten błąd projektu podpisany / niepodpisany w STL jest wystarczającym powodem, aby nie używać std :: vector, ale zamiast własnej implementacji.
źródło
for(int i = 0; i < get_size_of_stuff(); i++)
. Oczywiście, możesz nie chcieć robić wielu surowych pętli, ale - daj spokój, też ich używasz.x + 1 < y
równoważnex < y - 1
, ale nie ma liczb całkowitych niebędących wypisanymi. To może z łatwością wprowadzić błędy, gdy rzeczy zostaną przekształcone, co do których zakłada się, że są równoważne.Użyj std :: size_t do indeksowania / liczenia tablic typu C.
W przypadku kontenerów STL będziesz mieć (na przykład)
vector<int>::size_type
, które powinny być używane do indeksowania i zliczania elementów wektorowych.W praktyce zazwyczaj są to ints bez znaku, ale nie jest to gwarantowane, szczególnie przy użyciu niestandardowych alokatorów.
źródło
std::size_t
jest zwykleunsigned long
(8 bajtów w systemach 64-bitowych), a nieunisgned int
(4 bajty).size_t
, ponieważ indeksy mogą być ujemne. Można jednak użyćsize_t
własnej instancji takiej tablicy, jeśli nie chce się przyjmować wartości ujemnej.+
na wskaźnikach, wydaje się, żeptrdiff_t
jest to jeden z indeksów.vector<T>::size_type
(i to samo dla wszystkich innych pojemników), to jest raczej raczej bezużyteczne, ponieważ skutecznie gwarantuje się, że jestsize_t
- jest napisane na maszynieAllocator::size_type
, a ograniczenia dotyczące kontenerów patrz w szczególności 20.1.5 / 4 - w szczególnościsize_type
muszą byćsize_t
idifference_type
musi byćptrdiff_t
. Oczywiście wartość domyślnastd::allocator<T>
spełnia te wymagania. Więc po prostu użyj krótszegosize_t
i nie zawracaj sobie głowy resztą partii :)Wkrótce większość komputerów będzie miała architekturę 64-bitową z 64-bitowym systemem operacyjnym: es z programami działającymi na kontenerach miliardów elementów. Następnie należy użyć
size_t
zamiastint
jako indeksu pętli, w przeciwnym razie indeks będzie zawijał się na elemencie 2 ^ 32: th, zarówno w systemach 32-, jak i 64-bitowych.Przygotuj się na przyszłość!
źródło
long int
a nieint
. Jeślisize_t
ma to znaczenie w 64-bitowym systemie operacyjnym, było tak samo istotne w 32-bitowym systemie operacyjnym.Korzystając z size_t, zwróć uwagę na następujące wyrażenie
Otrzymasz false w wyrażeniu if bez względu na to, jaką wartość masz dla x. Uświadomienie sobie tego zajęło mi kilka dni (kod jest tak prosty, że nie przeprowadziłem testu jednostkowego), chociaż ustalenie źródła problemu zajęło tylko kilka minut. Nie jestem pewien, czy lepiej jest wykonać rzut lub użyć zera.
Oba sposoby powinny działać. Oto mój testowy przebieg
Wyjście: i-7 = 18446744073709551614 (int) (i-7) = - 2
Chciałbym komentarze innych.
źródło
(int)(i - 7)
jest to niedomiar, który jest rzutowanyint
później, aleint(i) - 7
nie jest niedomiar, ponieważ najpierw konwertujeszi
naint
, a następnie odejmujesz7
. Dodatkowo twój przykład był mylący.size_t jest zwracany przez różne biblioteki, aby wskazać, że rozmiar tego kontenera jest różny od zera. Używasz go, gdy wrócisz: 0
Jednak w powyższym przykładzie zapętlenie na size_t jest potencjalnym błędem. Rozważ następujące:
użycie liczb całkowitych bez znaku może potencjalnie powodować tego rodzaju subtelne problemy. Dlatego imho wolę używać size_t tylko wtedy, gdy wchodzę w interakcje z kontenerami / typami, które tego wymagają.
źródło
size_t
jest typem bez znaku, który może przechowywać maksymalną wartość całkowitą dla twojej architektury, więc jest chroniony przed przepełnieniem liczb całkowitych z powodu znaku (podpisany int0x7FFFFFFF
zwiększony o 1 da -1) lub krótki rozmiar (niepodpisany krótki int 0xFFFF zwiększony o 1 da ci 0).Jest używany głównie w indeksowaniu tablic / pętlach / arytmetyki adresów i tak dalej. Funkcje takie jak
memset()
i podobne akceptująsize_t
tylko, ponieważ teoretycznie możesz mieć blok pamięci wielkości2^32-1
(na platformie 32-bitowej).Dla takich prostych pętli nie zawracaj sobie głowy i używaj tylko int.
źródło
size_t to typ całki bez znaku, który może reprezentować największą liczbę całkowitą w twoim systemie. Używaj go tylko wtedy, gdy potrzebujesz bardzo dużych tablic, macierzy itp.
Niektóre funkcje zwracają rozmiar_t, a twój kompilator ostrzega cię, jeśli spróbujesz dokonać porównań.
Unikaj tego, używając odpowiedniego podpisanego / niepodpisanego typu danych lub po prostu typecast dla szybkiego włamania.
źródło
size_t jest bez znaku int. więc kiedy tylko chcesz int bez znaku, możesz go użyć.
Używam go, gdy chcę określić rozmiar tablicy, przeciwdziałać ...
źródło
size_t
może być 64-bitowa liczba całkowita bez znaku, podczas gdy na maszynie 32-bitowej jest to tylko 32-bitowa liczba całkowita bez znaku.