Literały to wartości wstawiane bezpośrednio do kodu. Na przykład:
return 5; // 5 is an integer literal
bool myNameIsAlex { true }; // true is a boolean literal
double d { 3.4 }; // 3.4 is a double literal
std::cout << "Hello, world!"; // "Hello, world!" is a C-style string literalLiterał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 (no suffix) is type int (by default)
std::cout << 5L << '\n'; // 5L is type long
std::cout << 5u << '\n'; // 5u is type 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: types match
unsigned int b { 6 }; // ok: compiler will convert int value 6 to unsigned int value 6
long c { 7 }; // ok: compiler will convert int value 7 to long value 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 (no suffix) is type double (by default)
std::cout << 5.0f << '\n'; // 5.0f is type float
return 0;
}Nowy programiści są często zdezorientowani, dlaczego następujące ostrzeżenia powodują ostrzeżenie kompilatora:
float f { 4.1 }; // warning: 4.1 is a double literal, not a float literalPonieważ 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 }; // use 'f' suffix so the literal is a float and matches variable type of float
double d { 4.1 }; // change variable to type double so it matches the literal type 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 is a double literal in standard notation
double d { -1.23 }; // the literal can be negative
double why { 0. }; // syntactically acceptable, but avoid this because it's hard to see the decimal point (prefer 0.0)- W notacji naukowej dodajemy
ew celu przedstawienia wykładnika:
double avogadro { 6.02e23 }; // 6.02 x 10^23 is a double literal in scientific notation
double protonCharge { 1.6e-19 }; // charge on a proton is 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
A 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 }; // now obvious what this 30 means
const int maxNameLength{ 30 };
setMax(maxNameLength); // now obvious this 30 is used in a different contextNazwa 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"); // bad: app name may be used in other places or change in the 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 }; // okay: we're starting our id generator with value 0
idGenerator = idGenerator + 1; // okay: we're just incrementing our 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; // okay: it's obvious 1000 is a conversion factor
}Identyfikatory całkowe sekwencyjne również generalnie nie są uważane za magiczne:
int main()
{
// okay: these are just sequential ids/counts
printPlayerInfo(1); // `1` would not really benefit from being named `player1` instead
printPlayerInfo(2);
}Najlepsza praktyka
Unikaj magicznych liczb w swoim kodzie (zamiast tego używaj zmiennych constexpr, zobacz lekcję 5.6 -- Zmienne Constexpr).

