Literały, czyli stałe dosłowne w języku C++.

Wprowadzenie

W tej lekcji dowiesz się, czym w C++ są literały i do czego służą. Mimo że były one już wykorzystywane w naszych programach, nie zostały przedstawione ich wszystkie możliwości. Czas zatem nadrobić zaległości.

Czym są literały

Literały, czyli inaczej stałe dosłowne to wszelkiego rodzaju dane pojawiające się w programie. Literałów używaliśmy w programach już wielokrotnie, jednak jak do tej pory tak ich nie nazywaliśmy.

Poniżej przedstawiam bardzo prosty program, który ma Ci uświadomić, że rzeczywiście literały były już do tej pory wielokrotnie wykorzystywane:

#include <iostream>
#include <cstring>

using namespace std;

int main()
{
  int ilosc=23; // literal calkowity
  double wysokosc=8868.2; // literal zmiennopozycyjny
  char znak='b'; // literal znakowy
  string napis="Napis"; // literal napisowy
 
  cout <<"Tekst\n"; // to tez jest literal napisowy
 
  cout <<ilosc<<' '<<wysokosc<<' '<<znak<<' '<<napis<<endl;
 
  cout <<"Nacisnij ENTER aby zakonczyc"<<endl;
  getchar();
  return 0;  
}
program nr 31.1

Jak zatem widzisz, rzeczywiście literały były już wykorzystywane i nie było w tym nic trudnego.

Czas jednak poznać trochę szczegółów dotyczących literałów. Jak się domyślasz z przykładowego programu, wszystkie literały możemy podzielić na literały całkowite, zmiennopozycyjne, znakowe i napisowe. Dodatkowo istnieją jeszcze literały typu logicznego.

To co jest również charakterystyczne dla literałów to to, że podobnie jak zmienne w C++ są określonego typu, podobnie literały mają swoje ściśle określone typy. Nawet jeśli typ literału nie zostaje przez nas jawnie określony, to literał będzie domyślnego typu.

Literały całkowite

Jak stosunkowo łatwo się domyśleć, literały całkowite służą do przechowywania danych typu całkowitego, czyli inaczej liczb, które nie posiadają części ułamkowej.

Literały całkowite możemy zapisywać w notacji dziesiętnej, ósemkowej lub szesnastkowej. Jeśli chcemy użyć notacji dziesiętnej, literał zapisujemy po prostu tak jak do tego przywykliśmy.

Z kolei, aby zapisać literał całkowity w notacji ósemkowej, poprzedzamy literał cyfrą 0. W końcu, aby zapisać literał całkowity w postaci szesnastkowej, poprzedzamy go ciągiem 0x.

Warto jednak zaznaczyć, że powyższe reguły dotyczą tylko literałów. Jeśli literał przypiszemy do zmiennej, to przy wypisaniu zmiennej, jej wartość i tak będzie wypisana domyślnie w systemie dziesiętnym, co może spowodować początkowo małe nieporozumienia. Tak samo, jeśli po prostu wypiszemy literał - zostanie on wypisany według innych reguł niż je tutaj definiujemy.

Wykorzystanie różnych systemów liczbowych dla literałów całkowitych zostało przedstawione w poniższym programie:

#include <iostream>

using namespace std;

int main()
{
  int ilosc=23; // system dziesietny
  cout <<"Wartosc zmiennej: "<<ilosc<<endl;
  ilosc=023; // system ?semkowy
  cout <<"Wartosc zmiennej: "<<ilosc<<endl;
  ilosc=0x23; // system szesnastkowy
  cout <<"Wartosc zmiennej: "<<ilosc<<endl<<endl;
 
  /* 23 dziesietnie to 27 ?semkowo oraz 17 szesnastkowo
     Postaramy sie to sprawdzia
  */

  if (23==027)    
     cout <<"23 dziesietnie to 27 osemkowo"<<endl;      
  if (23==0x17)
     cout <<"23 dziesietnie to 13 szesnastkowo"<<endl;      
  if ((23==027) && (23==0x17))
     cout <<"23 dziesietnie to 27 osemkowo oraz 13 szesnastkowo"<<endl;            
 
  cout <<endl<<"Nacisnij ENTER aby zakonczyc"<<endl;
  getchar();
  return 0;  
}
program nr 31.2

Czas porozmawiać o tym, jakie są typy literałów całkowitych. Jeśli chodzi o literały całkowite, to domyślnym ich typem jest typ signed int.

Nie zawsze może nam to odpowiadać, dlatego też możemy to zmienić. Aby określić typ literału całkowitego dodajemy odpowiednią literę na końcu.

Dodając literę L (lub małe l) mówimy, że literał jest typu long. Z kolei umieszczając na końcu literału literę U (lub małe u) dajemy do zrozumienia, że literał jest bez znaku, czyli unsigned. Oczywiście dla jednego literału możemy określić jako typ unsigned long - wówczas kolejność dwóch liter U i L na końcu literału nie ma znaczenia.

Chociaż modyfikatory określające czy literał całkowity jest typu unsigned czy long można zapisywać zarówno za pomocą liter dużych, jak i małych, sugeruję Ci używać liter dużych, dlatego że są one o wiele czytelniejsze, a ponadto mała litera l jest bardzo podobna do cyfry 1.

Oczywiście można dodatkowo połączyć zastosowanie notacji ósemkowej lub szesnastkowej wraz z określeniem typu long lub unsigned. Poniższy program przedstawia wykorzystanie literałów całkowitych różnych typów:

#include <iostream>

using namespace std;

int main()
{
  int ilosc=23; // literal typu signed int (domyslnie)
  long int longIlosc = 23L; // literal typu signed long int
  unsigned int bezIlosc = 23U; // literal typu unsigned int
  unsigned long int bezLongIlosc = 23UL; // literal typu unsigned long int
 
  cout <<ilosc<<' '<<longIlosc<<' '<<bezIlosc<<' '<<bezLongIlosc<<endl;
 
  ilosc=023; // literal typu signed int w notacji ?semkowej
  longIlosc = 0x23L; // literal typu signed long w notacji szesnastkowej
 
  cout <<endl<<endl<<ilosc<<' '<<longIlosc<<endl;
 
  if (23L == 23U)
     cout <<endl<<"wartosc 23L i 23U jest taka sama"<<endl;
 
  if (023 == 023UL)
     cout <<endl<<"wartosc 023 i 023UL jest taka sama"<<endl;        
 
  cout <<endl<<"Nacisnij ENTER aby zakonczyc"<<endl;
  getchar();
  return 0;  
}
program nr 31.3

Sam program jest prosty i w zasadzie nie muszę nic tłumaczyć. Chcę jednak zwrócić Twoją uwagę na fragmenty, w których porównujemy literały tak naprawdę różnych typów - jak widać wartości wybranych literałów są sobie równe.

Literały zmiennopozycyjne

Literały zmiennopozycyjne służą do przechowywania liczb, które składają się z części całkowitej i części ułamkowej. Typem domyślnym dla literałów zmiennopozycyjnych jest typ double.

Jeśli chcemy otrzymać literał typu float, wówczas należy na końcu literału dopisać literę F (lub małe f). Z kolei jeśli chcemy otrzymać literał typu long double, należy na końcu literału umieścić literę L (lub małe l).

Zwróć uwagę, że litera L nie oznacza tutaj jak w przypadku typu całkowitego samego przedrostka long, a cały typ long double. Dlatego też nie wolno zastosować literału, w którym na końcu pojawią się jednocześnie litery L i F, bo żaden literał nie może być jednocześnie dwóch różnych typów.

Dodatkowo warto wspomnieć, że tak jak w przypadku literałów całkowitych były dostępne notacje dziesiętna, ósemkowa i szesnastkowa, to w przypadku literałów zmiennopozycyjnych jest dostępna notacja w postaci zwykłego ułamka oraz notacja wykładnicza.

Notacja w postaci zwykłego ułamka, to notacja jaka była do tej pory stosowana w kursie i jakiej będziesz najprawdopodobniej najczęściej używać. Polega ona na zapisaniu części całkowitej liczby i części ułamkowej rozdzielonej znakiem kropki.

Z kolei notacja wykładnicza polega na tym, że zapisujemy ułamek tak jak w poprzedniej notacji, po nim zapisujemy literę E (lub małe e) a następnie potęgę, która może być również ułamkiem. Zapis taki oznacza, że liczba znajdująca się przed literą E, zostaje pomnożona przez 10 do potęgi, jaką podaliśmy po literze E. Inaczej mówiąc, jeśli wprowadzimy oznaczenie pow(x,y) - x zostaje podniesione do potęgi y, to zapis 2.2E2 będzie oznaczał 2.2 ·pow(10,2) = 2.2 · 100 = 220.

Podobnie jak w przypadku liczb całkowitych, reguły te dotyczą tylko literałów. Natomiast już w przypadku wypisywania na ekran rządzą inne reguły, którymi na razie się nie zajmujemy. Oto przykładowy program:

#include <iostream>

using namespace std;

int main()
{  
  double ilosc=23.3443; // literal typu double (domyslnie)
  float floatIlosc = 23.3443F; // literal typu float
  long double ldoubleIlosc = 23.3443L; // literal typu long double
 
  cout <<ilosc<<' '<<floatIlosc<<' '<<ldoubleIlosc<<endl;
 
  ilosc=2.33443E1; // = 2.33443 * 10 = 23.3443
  floatIlosc = 2.33443E5F; // literal typu float w notacji wykladniczej
 
  cout <<endl<<endl<<ilosc<<' '<<floatIlosc<<endl;
 
  if (23.3443 == 2.33443E1)
     cout <<endl<<"wartosc 23.3443 i 2.33443E1 jest taka sama"<<endl;
 
  if (23.3443F == 233443E-4F)
     cout <<endl<<"wartosc 23.3443F i 233443E-4F jest taka sama"<<endl;        
 
  cout <<endl<<"Nacisnij ENTER aby zakonczyc"<<endl;
  getchar();
  return 0;  
}
program nr 31.4

Literały znakowe

Literały znakowe przechowują znaki i oczywiście były już wielokrotnie w programach wykorzystywane. Charakterystyczną cechą literałów znakowych jest to, że ujmuje się je w pojedyncze cudzysłowy. W cudzysłowach może znajdować się tylko jeden znak zwykły. Jeśli umieścimy tam więcej znaków, wówczas często kompilator błędu nie zasygnalizuje, ale program nie będzie działać prawidłowo.

Oprócz znaków zwykłych, literałem mogą być również znaki specjalne. Są one zawsze poprzedzone znakiem \. Znane Ci już z jednej z początkowych lekcji znaki specjalne to:

'\n'  - nowa linia
'\t'   - tabulacja pozioma (czyli "normalny" tabulator)
'\v'  - tabulacja pionowa
'\b'  - cofnięcie (skasowanie ostatniego znaku)
'\r'   - powrót karetki (przesunięcie do początku wiersza)
'\f'   - nowa strona
'\a'  - sygnał dźwiękowy
'\\'  - ukośnik (backslash), czyli znak \
'\?'  - znak zapytania
'\''   - pojedynczy cudzysłów
'\"'  - podwójny cudzysłów

Dodatkowo, literały znakowe możemy zapisywać jako sekwencję liczby co najwyżej 3-cyfrowej poprzedzonej znakiem \ czyli np. '\062', jednak ta metoda nie jest w zasadzie w ogóle wykorzystywana.

Ponadto, ponieważ podstawowy typ znakowy zawiera tylko 256 symboli, to nie jest możliwe odzwierciedlenie wszystkich znaków - na przykład znaków alfabetu chińskiego. Dodając na początku literału znakowego literę L uzyskujemy tzw. rozszerzony literał znakowy, który umożliwia już wypisanie takich symboli (zmienna, do której przypiszemy taki literał powinna być wówczas typu wchar_t zamiast char). Typem wchar_t nie będziemy się jednak jak na razie zajmować.

Poniższy przykład przedstawia wykorzystanie literałów znakowych zapisywanych w różnej postaci:

#include <iostream>

using namespace std;

int main()
{
  char znaki[6]; // tablica 6 znak?w
 
  znaki[0]='A';    // litera a
  znaki[1]='\n';   // znak nowej linii
  znaki[2]='\\';   /* znak \  */
  znaki[3]='\154'; // litera l
  znaki[4]=' ';    // spacja
  znaki[5]='\141'; // litera a
 
  for (unsigned int i=0; i<6;++i) // wypisanie wszystkich znakow
      cout <<znaki[i];              
 
  cout <<endl<<"Nacisnij ENTER aby zakonczyc"<<endl;
  getchar();
  return 0;  
}
program nr 31.5

Literały napisowe

Za pomocą literałów napisowych przechowujemy wszystkie informacje, które przekazujemy użytkownikowi podczas działania programu. Literały napisowe ujmujemy w podwójne cudzysłowy.

Zawartością literału mogą być wszystkie znaki drukowalne, a także wszystkie niedrukowane znaki specjalne - uzyskiwane tak samo jak w przypadku literałów znakowych.

Gdyby zdarzyło się, że literał jest tak długi, że nie mieści się w jednej linii, możemy na końcu jednej linii napisać znak \ i kontynuować literał w wierszu następnym. Zaznaczam, że znak \ musi być ostatnim znakiem w linii, nie może być za nim żadnego białego znaku, typu spacja czy tabulator. Mimo wszystko ze względu na czytelność, odradzam stosowanie tej metody.

Dodatkowo, podobnie jak w przypadku literałów znakowych, możemy chcieć, aby literał napisowy mógł reprezentować znaki z rozszerzonego alfabetu. Wówczas podobnie jak w przypadku literałów znakowych, poprzedzimy literał napisowy literą L. Również z tej metody jak na razie korzystać nie będziemy.

Poniżej znajduje się przykładowy program prezentujący literały napisowe:

#include <iostream>
#include <cstring>

using namespace std;

int main()
{
  string napis;
  napis="Literal\n \\napisowy\n\n";
 
  cout <<napis;
 
  /*
    problem - wciecia zostana potraktowane jako czesc napisu
  */

  napis="Literal \
  napisowy \
       tak dlugi \
  ze nie miesci sie \
  w jednej \
  linii\n\n";
 
  cout <<napis;
 
  /*
    napis poprawny, ale kod bez wciec wyglada fatalnie
  */

  napis="Literal \
napisowy \
tak dlugi \
ze nie miesci sie \
w jednej \
linii\n\n";

  cout <<napis;

  cout <<endl<<"Nacisnij ENTER aby zakonczyc"<<endl;
  getchar();
  return 0;  
}
program nr 31.6

Literały typu logicznego

Ostatnim rodzajem literałów są literały typu logicznego. Literały takie przyjmują tylko dwie wartości: true oraz false, a oba te słowa są jednocześnie słowami kluczowymi języka C++.

Jak już mam nadzieję doskonale pamiętasz, w przypadku wypisywania zmiennych (a tym samym literałów) typu logicznego na ekranie nie uzyskamy wartości true czy false, tylko wartości te zostaną zastąpione odpowiedni wartościami 1 i 0.

Poniższy program przedstawia wykorzystanie literałów typu logicznego:

#include <iostream>

using namespace std;

int main()
{
  bool logiczny;
 
  cout <<true<<' '<<false<<endl;
 
  logiczny=true;
  cout <<logiczny<<endl;
  logiczny=false;
  cout <<logiczny<<endl;

  cout <<endl<<"Nacisnij ENTER aby zakonczyc"<<endl;
  getchar();
  return 0;  
}
program nr 31.7

Podsumowanie

Literały, mimo że wykorzystywane w każdym programie, w rzeczywistości nie są aż tak ważne. Jeśli sobie przypomnisz nasze poprzednie programy, zauważysz od razu, że wielokrotnie dokonując przypisania wartości do zmiennej np. typu float nie musieliśmy używać literału z literą F na końcu.

Dlatego też większość szczegółów dotyczących literałów nie musisz wcale pamiętać, bowiem nie będziesz ich często wykorzystywać i na szczęście nie trzeba dla każdego typu zmiennej definiować literału przez dodawanie liter na ich końcu.

Warto natomiast zapamiętać, że w przypadku literałów zmiennopozycyjnych możemy stosować również notację wykładniczą. Koniecznie natomiast trzeba zapamiętać, że domyślnym typem literałów całkowitych jest typ signed int, a dla literałów zmiennopozycyjnych - typ double.

powrót