Menu

Typy danych w C++: int, double, char, bool i inne

Praktyczny przegląd podstawowych typów danych w C++: liczb całkowitych, zmiennoprzecinkowych, char i bool, a do tego rozmiary, signed i unsigned, literały z przyrostkami, przepełnienie i wybór właściwego typu.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Dlaczego typy mają znaczenie w C++

Gdy deklarujesz wartość z jawnym typem, na przykład int age = 30;, typ to nie tylko etykieta. Mówi kompilatorowi, ile bajtów zarezerwować, jak te bajty interpretować i jakie operacje są dozwolone. Pomyl typ, a możesz po cichu stracić precyzję, doprowadzić do przepełnienia albo wywołać niezdefiniowane zachowanie.

C++ dzieli wbudowane typy na kilka rodzin: liczby całkowite, liczby zmiennoprzecinkowe, typ znakowy i typ logiczny. Przyjrzyjmy się każdej z nich, a potem regułom, na których ludzie się potykają.

Typy podstawowe

Oto po jednym z każdego podstawowego typu w jednym programie. Zwróć uwagę na przyrostki literałów (L, f, u) i pojedyncze cudzysłowy przy char:

bool domyślnie wypisuje się jako 1 albo 0, a nie true/false. char używa pojedynczych cudzysłowów: 'A' to jeden znak, a "A" (podwójne cudzysłowy) to literał napisowy, zupełnie inny typ. Te dwie pomyłki są na początku niezwykle częste.

Rozmiary nie są stałe

To największe zaskoczenie dla osób przychodzących z języków takich jak Java. Standard C++ gwarantuje tylko minimalne rozmiary i względną kolejność (short ≤ int ≤ long ≤ long long). Faktyczny rozmiar zależy od kompilatora i platformy. Zawsze sprawdzaj go przez sizeof:

W typowej 64-bitowej kompilacji na Linuksie zobaczysz int = 4, long = 8. Na 64-bitowym Windowsie long ma jednak tylko 4 bajty. Ta luka w przenośności to dokładnie powód, dla którego nie należy pisać kodu zakładającego, że long jest 64-bitowy.

Gdy potrzebujesz dokładnej szerokości, sięgnij po typy całkowite o stałej szerokości z <cstdint>:

Używaj int32_t/int64_t w formatach plików, protokołach sieciowych i wszędzie tam, gdzie zachowanie musi być identyczne na różnych maszynach. Zwróć uwagę na rzutowanie (int)a: wysłanie 8-bitowego typu do strumienia wypisuje go jako znak, a nie liczbę, więc najpierw go rzutuj.

signed kontra unsigned

Każdy typ całkowity występuje w dwóch odmianach. Typ signed może przechowywać liczby ujemne; typ unsigned nie może, ale w zamian za zakres ujemny ma wyższe maksimum dodatnie. Zwykły int jest domyślnie ze znakiem.

Odejmowanie od 0 bez znaku zawija się do ogromnej liczby dodatniej zamiast zejść poniżej zera. To ciągle gryzie ludzi, zwłaszcza przy size_t (typie bez znaku) zwracanym przez .size():

vector<int> v = {1, 2, 3};
// UWAGA: v.size() jest bez znaku. Jeśli v jest pusty, v.size() - 1 zawija się
// do gigantycznej liczby, a pętla kręci się niemal w nieskończoność.
for (size_t i = 0; i <= v.size() - 1; i++) { /* ... */ }

Wybieraj i < v.size() (nigdy <= size() - 1) albo całkiem omiń problem dzięki pętli for po zakresie.

Przepełnienie liczb całkowitych to niezdefiniowane zachowanie

W przeciwieństwie do zawijania liczb bez znaku (które jest dobrze zdefiniowane), przepełnienie liczb całkowitych ze znakiem to w C++ niezdefiniowane zachowanie. Kompilator może zrobić cokolwiek: zwrócić śmieci, wyoptymalizować sprawdzenie albo doprowadzić do awarii:

Rozwiązanie jest takie samo jak przy pułapce przepełnienia w każdym języku: wykonuj obliczenia w szerszym typie. Rzutuj jeden z operandów na long long przed +, aby dodawanie odbyło się na 64 bitach. Rzutowanie wyniku po fakcie jest spóźnione, bo przepełnienie już nastąpiło.

Wybór właściwego typu

W większości kodu wartości domyślne są w porządku: int dla liczb całkowitych, double dla ułamków. Po coś innego sięgaj tylko wtedy, gdy masz powód.

TypTypowy rozmiarKiedy używać
int32 bityDomyślny dla liczb całkowitych
long long64 bityWartości powyżej ok. 2 miliardów: znaczniki czasu, duże liczniki
double64 bityDomyślny dla ułamków, dobra precyzja
float32 bityTablice, w których liczy się pamięć, a precyzja może ucierpieć
bool1 bajtFlaga prawda/fałsz
int32_t / int64_tdokładnyFormaty międzyplatformowe, protokoły, operacje na bitach

Kilka pułapek do zapamiętania. float ma tylko około 7 znaczących cyfr dziesiętnych, więc 0.1f + 0.2f to nie dokładnie 0.3; wybieraj double, chyba że naprawdę musisz oszczędzać pamięć. A char może być ze znakiem lub bez, zależnie od platformy, więc jeśli wykonujesz obliczenia na surowych bajtach, pisz jawnie signed char albo unsigned char.

Dalej: słowo kluczowe auto

Wypisywanie typu za każdym razem bywa żmudne, a czasem typ jest długi albo trudny do nazwania. C++ pozwala kompilatorowi wywnioskować go za ciebie dzięki słowu kluczowemu auto: auto x = 42; sprawia, że x jest typu int, a auto it = v.begin(); oszczędza ci wpisywania rozwlekłego typu iteratora. Następna strona pokazuje, kiedy auto czyni kod czytelniejszym, a kiedy ukrywa za dużo.

Najczęściej zadawane pytania

Jakie są podstawowe typy danych w C++?

Typy podstawowe to liczby całkowite (short, int, long, long long), zmiennoprzecinkowe (float, double, long double), typ znakowy char i typ logiczny bool. Każdy typ całkowity może też być signed lub unsigned. Wszystko inne, czyli std::string, tablice i twoje własne klasy, jest zbudowane na ich bazie.

Jaka jest różnica między int a long w C++?

Oba przechowują liczby całkowite, ale long ma gwarancję, że jest co najmniej tak szeroki jak int (często 64-bitowy na platformach 64-bitowych, ale na Windowsie tylko 32-bitowy). Standard ustala jedynie minimalne rozmiary, więc gdy potrzebujesz gwarantowanej szerokości, użyj typów o stałej szerokości z <cstdint>, takich jak int32_t i int64_t.

Ile zajmuje int w C++?

Standard gwarantuje tylko, że int ma co najmniej 16 bitów, ale praktycznie na każdym nowoczesnym komputerze i serwerze ma 32 bity. Rozmiary zależą od platformy, więc nigdy ich nie zakładaj: wypisz sizeof(int), aby sprawdzić, albo użyj typów z <cstdint>, takich jak int32_t, gdy potrzebujesz dokładnej szerokości.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ