Literały to wartości wstawiane bezpośrednio do kodu. Na przykład:
return 5; // 5 to literał całkowity
bool myNameIsAlex { true }; // true jest literałem boolowskim
double d { 3.4 }; // 3,4 to podwójny literał
std::cout << "Hello, world!"; // „Witaj, świecie!” to literał łańcuchowy w stylu CLiterały są czasami nazywane stałymi literałami ponieważ ich znaczenia nie można przedefiniować (5 zawsze oznacza wartość całkowitą 5).
Typ literału
Tak jak obiekty mają typ, tak i wszystkie literały mają typ. Rodzaj literału określa się na podstawie wartości literału. Na przykład, literał będący liczbą całkowitą (np. 5) jest dedukowany jako typu int.
Domyślnie:
| Wartość literału | Przykłady | Domyślny typ literału | Uwaga |
|---|---|---|---|
| wartość całkowita | 5, 0, -3 | int | |
| wartość logiczna | true, false | bool | |
| wartość zmiennoprzecinkowa | 1.2, 0.0, 3.4 | double (nie float!) | |
| znak | ’a’, ‘\n’ | char | |
| ciąg w stylu C | „Witaj, świecie!” | const char[14] | zobacz sekcję dotyczącą literałów łańcuchowych w stylu C poniżej |
Przyrostki literałów
Jeśli domyślny typ literału nie jest zgodny z oczekiwaniami, możesz zmienić typ literału, dodając przyrostek. Oto niektóre z bardziej powszechnych sufiksów:
| Typ danych | Suffix | Znaczenie |
|---|---|---|
| całka | u lub U | unsigned int |
| całka | l lub L | long |
| całka | ul, uL, Ul, UL, lu, lU, Lu, LU | unsigned long |
| całka | ll lub LL | long long |
| całka | ull, uLL, Ull, ULL, llu, llU, LLu, LLU | unsigned long long |
| całka | z lub Z | Podpisana wersja std::size_t (C++23) |
| całka | uz, uZ, Uz, UZ, zu, zU, Zu, ZU | std::size_t (C++23) |
| zmiennoprzecinkowy | f lub F | float |
| zmiennoprzecinkowy | l lub L | long double |
| ciąg | S | std::string |
| ciąg | sv | std::string_view |
Za chwilę omówimy literały całkowe i zmiennoprzecinkowe oraz przyrostki.
W większości przypadków sufiksy nie są potrzebne (z wyjątkiem f).
Powiązana treść
Klasa s i sv sufiksów, których użycie wymaga dodatkowej linijki kodu. Omówimy je w dalszej części lekcji 5.7 — Wprowadzenie do std::string i 5.8 — Wprowadzenie do std::string_view.
Dodatkowe (rzadko używane) sufiksy istnieją dla liczb zespolonych i literałów chrono (czasowych). Są one udokumentowane tutaj.
Dla zaawansowanych czytelników
Z wyjątkiem sufiksu f , sufiksy są najczęściej używane w przypadkach, gdy występuje dedukcja typu. Patrz 10.8 -- Dedukcja typu dla obiektów korzystających z auto słowo kluczowe i 13.14 -- Przewodniki dedukcji i dedukcji szablonu klasy (CTAD).
Wielkość przyrostków
Większość przyrostków nie uwzględnia wielkości liter. Wyjątkami są:
sisvmusi być pisany małymi literami.- Dwa kolejne
llubLznaki muszą mieć tę samą wielkość liter (lLiLlnie są akceptowane).
Ponieważ małe litery L mogą być stosowane. wyglądają jak numeryczne 1 w niektórych czcionkach niektórzy programiści wolą używać literałów wielkich liter, inni używają sufiksów małych liter, z wyjątkiem L.
Najlepsza praktyka
Preferują sufiks literalny L (wielkie litery) zamiast l (małe litery).
Literały całkowe
Zazwyczaj nie musisz używać sufiksów dla literałów całkowitych, ale tutaj są przykłady:
#include <iostream>
int main()
{
std::cout << 5 << '\n'; // 5 (bez przyrostka) wpisz int (przez domyślnie)
std::cout << 5L << '\n'; // 5L do długiego pisania
std::cout << 5u << '\n'; // 5u, aby wpisać unsigned int
return 0;
}W większości przypadków można używać literałów int bez przyrostka, nawet podczas inicjowania typów innych niż int :
int main()
{
int a { 5 }; // ok: typy się zgadzają
unsigned int b { 6 }; // ok: kompilator przekonwertuje wartość int 6 na wartość int bez znaku 6
long c { 7 }; // ok: kompilator przekonwertuje wartość int 7 na wartość long 7
return 0;
}W takich przypadkach kompilator przekonwertuje literał int na odpowiedni typ.
W pierwszym przypadku 5 jest już domyślnie int , więc kompilator może użyć tej wartości bezpośrednio do inicjalizacji int zmienną a. W drugim przypadku int wartości 6 nie pasuje do typu unsigned int b kompilator przekonwertuje wartość int 6 Do unsigned int wartości 6, a następnie użyje jej jako inicjatora dla b. W trzecim przypadku int wartości 7 nie pasuje do typu long c kompilator przekonwertuje wartość int 7 Do long wartości 7, a następnie użyje jej jako inicjatora dla c.
literałów zmiennoprzecinkowych
Domyślnie literały zmiennoprzecinkowe mają typ double. Aby zamiast tego utworzyć z nich float literały, należy użyć sufiksu f (lub F):
#include <iostream>
int main()
{
std::cout << 5.0 << '\n'; // 5.0 (bez przyrostka) jest typu double (domyślnie)
std::cout << 5.0f << '\n'; // 5.0f, aby wpisać float
return 0;
}Nowy programiści są często zdezorientowani, dlaczego następujące ostrzeżenia powodują ostrzeżenie kompilatora:
float f { 4.1 }; // ostrzeżenie: 4.1 to literał podwójny, a nie literał zmiennoprzecinkowyPonieważ 4.1 nie ma przyrostka, literał ma typ double, nie float. Kiedy kompilator określa typ literału, nie obchodzi go, co z nim zrobisz (np. w tym przypadku użyjesz go do zainicjowania zmiennej float ). Ponieważ typ literału (double) nie odpowiada typowi zmiennej, która jest używana do inicjalizacji (float), wartość literału należy przekonwertować na float więc można go następnie użyć do inicjalizacji zmiennej f. Konwersja wartości z double do float może spowodować utratę precyzji, dlatego kompilator wyświetli ostrzeżenie.
Rozwiązanie jest jednym z poniższych:
float f { 4.1f }; // użyj przyrostka „f”, aby literał był zmiennoprzecinkowy i pasował do zmiennej typu float
double d { 4.1 }; // zmienić zmienną na typ double tak, aby odpowiadała typowi dosłownemu doubleZapis naukowy dla literałów zmiennoprzecinkowych
Istnieją dwa różne sposoby zapisywania literałów zmiennoprzecinkowych.
- W notacji standardowej liczbę zapisujemy z ułamkiem dziesiętnym punkt:
double pi { 3.14159 }; // 3,14159 to podwójny literał w notacji standardowej
double d { -1.23 }; // literał może być ujemny
double why { 0. }; // składniczo akceptowalne, ale unikaj tego, ponieważ trudno zobaczyć przecinek dziesiętny (preferowane 0,0)- W notacji naukowej dodajemy
ew celu przedstawienia wykładnika:
double avogadro { 6.02e23 }; // 6,02 x 10^23 to podwójny literał w notacji naukowej
double protonCharge { 1.6e-19 }; // ładunek protonu wynosi 1,6 x 10^-19Literały łańcuchowe
W programowaniu ciąg to zbiór kolejnych znaków używanych do reprezentowania tekstu (takich jak nazwy, słowa i zdania).
Pierwszy napisany przez Ciebie program w C++ wyglądał prawdopodobnie mniej więcej tak:
#include <iostream>
int main()
{
std::cout << "Hello, world!";
return 0;
}"Hello, world!" jest literałem łańcuchowym. Literały łańcuchowe są umieszczane między cudzysłowami, aby zidentyfikować je jako ciągi znaków (w przeciwieństwie do literałów znakowych, które są umieszczane między pojedynczymi cudzysłowami).
Ponieważ ciągi są powszechnie używane w programach, większość współczesnych języków programowania zawiera podstawowy typ danych w postaci ciągu znaków. Ze względów historycznych ciągi nie są podstawowym typem w C++. Mają raczej dziwny, skomplikowany typ, z którym trudno się pracuje (omówimy, jak/dlaczego w przyszłej lekcji, gdy omówimy więcej podstaw wymaganych do wyjaśnienia, jak działają). Takie ciągi są często nazywane ciągami C lub Ciągów w stylu C, ponieważ są dziedziczone z języka C.
Istnieją dwie nieoczywiste rzeczy, które warto wiedzieć o literałach ciągu w stylu C.
- Wszystkie literały ciągu w stylu C mają niejawny terminator zerowy. Rozważmy ciąg taki jak
"hello". Chociaż wydaje się, że ten ciąg w stylu C ma tylko pięć znaków, w rzeczywistości ma ich sześć:'h','e','l‘,'l','o', i'\0'(znak z kodem ASCII 0). Ten końcowy znak „\0” jest znakiem specjalnym zwanym terminatorem zerowym i służy do wskazania końca łańcucha. Łańcuch kończący się terminatorem zerowym nazywa się ciągiem zakończonym zerem.
Dla zaawansowanych czytelników
To jest powód, dla którego ciąg "Hello, world!" ma typ const char[14] zamiast const char[13] -- ukryty terminator zerowy liczy się jako znak.
Powód użycia terminatora zerowego jest również historyczny: można go wykorzystać do określenia, gdzie kończy się ciąg znaków.
- W przeciwieństwie do większości innych literałów (które są wartości, a nie obiekty), literały łańcuchowe w stylu C są obiektami stałymi, które są tworzone na początku programu i mają gwarancję istnienia przez cały program. Fakt ten stanie się ważny podczas kilku lekcji, kiedy będziemy omawiać
std::string_view.
Kluczowa informacja
Literały łańcuchowe w stylu C to obiekty stałe, które są tworzone na początku programu i mają gwarancję istnienia przez cały program.
W przeciwieństwie do literałów łańcuchowych w stylu C, std::string i std::string_view literały tworzą obiekty tymczasowe. Tych obiektów tymczasowych należy użyć natychmiast, gdyż ulegają zniszczeniu na końcu pełnego wyrażenia, w którym zostały utworzone.
Powiązana treść
Omawiamy std::string i std::string_view Literały w lekcji 5.7 — Wprowadzenie do std::string i 5.8 — Wprowadzenie do std::string_view .
Liczby magiczne
liczba magiczna to literał (zwykle liczba), który albo ma niejasne znaczenie, albo może wymagać późniejszej zmiany.
Oto dwa stwierdzenia pokazujące przykłady magicznych liczb:
const int maxStudentsPerSchool{ numClassrooms * 30 };
setMax(30);Co robią literały 30 oznaczają w tych kontekstach? W tym pierwszym przypadku można się zapewne domyślić, że jest to liczba uczniów w klasie, ale nie jest to od razu oczywiste. W tym drugim, kto wie. Musielibyśmy przyjrzeć się tej funkcji, żeby wiedzieć, co ona robi.
W złożonych programach bardzo trudno jest wywnioskować, co reprezentuje literał, chyba że istnieje komentarz wyjaśniający to.
Używanie liczb magicznych jest ogólnie uważane za złą praktykę, ponieważ nie tylko nie dostarczają kontekstu, do czego są używane, ale stwarzają problemy w przypadku konieczności zmiany wartości. Załóżmy, że szkoła kupuje nowe ławki, które pozwalają zwiększyć liczebność klas z 30 do 35 osób i nasz program musi to uwzględnić.
Aby to zrobić, musimy zaktualizować jeden lub więcej literałów z 30 Do 35. Ale które literały? 30 w inicjatorze maxStudentsPerSchool wydaje się oczywiste. Ale co z 30 użytym jako argumentem setMax()? Czy to 30 ma to samo znaczenie co drugie 30? Jeśli tak, warto go zaktualizować. Jeśli nie, należy to zostawić w spokoju, w przeciwnym razie możemy przerwać nasz program gdzie indziej. Jeśli wykonasz globalne wyszukiwanie i zamienianie, możesz przypadkowo zaktualizować argument setMax() kiedy nie powinien się on zmieniać. Musisz więc przejrzeć cały kod pod kątem każdego wystąpienia literału 30 (których może być setki), a następnie indywidualnie określić, czy należy go zmienić, czy nie. Może to być bardzo czasochłonne (i podatne na błędy).
Na szczęście zarówno brak kontekstu, jak i problemy związane z aktualizacją można łatwo rozwiązać za pomocą stałych symbolicznych:
const int maxStudentsPerClass { 30 };
const int totalStudents{ numClassrooms * maxStudentsPerClass }; // teraz oczywiste, co do 30 oznacza
const int maxNameLength{ 30 };
setMax(maxNameLength); // teraz oczywiste, że 30 jest użyte w innym kontekścieNazwa stałej zapewnia kontekst i wystarczy zaktualizować wartość w jednym miejscu, aby zmienić wartość w całym programie.
Zauważ, że liczby magiczne nie zawsze są liczbami — mogą być również tekstem (np. nazwy) lub inne typy:
int main()
{
printAppWelcome("MyCalculator"); // zły: nazwa aplikacji może zostać użyta w innym miejscu lub zmienić future
}Literały użyte w oczywistych kontekstach, które prawdopodobnie nie ulegną zmianie, zazwyczaj nie są uważane za magiczne. Wartości -1, 0, 0.0, i 1 są często używane w takich kontekstach:
int idGenerator { 0 }; // OK: zaczynamy nasz generator identyfikatorów z wartością 0
idGenerator = idGenerator + 1; // OK: właśnie zwiększamy nasz generatorInne liczby również mogą być oczywiste w kontekście (i dlatego nie są uważane za magiczne):
int kmtoM(int km)
{
return km * 1000; // ok: to oczywiste, że 1000 to współczynnik konwersji
}Identyfikatory całkowe sekwencyjne również generalnie nie są uważane za magiczne:
int main()
{
// OK: do tylko kolejnych identyfikatorów/liczby
printPlayerInfo(1); // „1” tak naprawdę nie zyskałby na nazwie „gracz1” zamiast tego
printPlayerInfo(2);
}Najlepsza praktyka
Unikaj magicznych liczb w swoim kodzie (zamiast tego używaj zmiennych constexpr, zobacz lekcję 5.6 -- Zmienne Constexpr).

