Część techniczna: Typ znakowy char
W części technicznej lekcji wprowadzimy nowy typ zmiennych – typ znakowy char. Pozwala on przechowywać pojedyncze znaki (małe i wielkie litery, cyfry, znaki przestankowe itp.). Wartości typu char są w języku C++ otoczone apostrofami, np. 'a', '8', '+', '.'. Jako pierwszy przykład napiszmy program, który wczytuje dany znak, o którym wiemy, że jest małą literą, i sprawdza, czy jest to samogłoska, czy spółgłoska.
#include <iostream>
using namespace std;
int main() {
char c;
cin >> c;
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' || c == 'y')
cout << "samogloska" << endl;
else
cout << "spolgloska" << endl;
}
Każdy ze znaków typu char ma przypisany numer będący liczbą całkowitą. To przyporządkowanie, używane powszechnie w komputerach do reprezentowania znaków, nazywa się kodem ASCII. Wygląda ono tak:
kod | znak | kod | znak | kod | znak | kod | znak | kod | znak | |||||
0-31 | znaki specjalne | 51 | 3 | 71 | G | 91 | [ | 111 | o | |||||
32 | spacja | 52 | 4 | 72 | H | 92 | \ | 112 | p | |||||
33 | ! | 53 | 5 | 73 | I | 93 | ] | 113 | q | |||||
34 | " | 54 | 6 | 74 | J | 94 | ^ | 114 | r | |||||
35 | # | 55 | 7 | 75 | K | 95 | _ | 115 | s | |||||
36 | $ | 56 | 8 | 76 | L | 96 | ` | 116 | t | |||||
37 | % | 57 | 9 | 77 | M | 97 | a | 117 | u | |||||
38 | & | 58 | : | 78 | N | 98 | b | 118 | v | |||||
39 | ' | 59 | ; | 79 | O | 99 | c | 119 | w | |||||
40 | ( | 60 | < | 80 | P | 100 | d | 120 | x | |||||
41 | ) | 61 | = | 81 | Q | 101 | e | 121 | y | |||||
42 | * | 62 | > | 82 | R | 102 | f | 122 | z | |||||
43 | + | 63 | ? | 83 | S | 103 | g | 123 | { | |||||
44 | , | 64 | @ | 84 | T | 104 | h | 124 | | | |||||
45 | - | 65 | A | 85 | U | 105 | i | 125 | } | |||||
46 | . | 66 | B | 86 | V | 106 | j | 126 | ~ | |||||
47 | / | 67 | C | 87 | W | 107 | k | 127 | znak specjalny | |||||
48 | 0 | 68 | D | 88 | X | 108 | l | |||||||
49 | 1 | 69 | E | 89 | Y | 109 | m | |||||||
50 | 2 | 70 | F | 90 | Z | 110 | n |
Oczywiście nie trzeba pamiętać kodów ASCII poszczególnych znaków. Warto jedynie wiedzieć, że małe litery oraz wielkie litery alfabetu angielskiego (łacińskiego) są ustawione w kodzie kolejno w porządku alfabetycznym, a cyfry – od najmniejszej do największej. Porównywanie znaków typu char za pomocą operatorów <, <=, >, >= odbywa się według kodów ASCII, tak więc małe litery oraz wielkie litery są porównywane alfabetycznie, a cyfry od najmniejszej do największej. Znaki o kodach od 0 do 31 oraz znak o kodzie 127 to tzw. kody sterujące. Znajdują się wśród nich m.in. znaki końca wiersza i tabulacji; wiele z tych znaków wyszło już z użycia.
Wartości zmiennych typu char możemy więc traktować jako niewielkie liczby całkowite. Dokładniej, zmienna typu char przyjmuje wartości od -128 do 127, przy czym wartości nieujemne odpowiadają znakom kodu ASCII, a pozostałe mogą służyć do reprezentowania innych symboli (np. polskich znaków ą, ę, ź, ć itp. w niektórych kodowaniach). Typ char jest więc typem całkowitym jednobajtowym, którego brakowało w komentarzu do lekcji 2. Odpowiadającym mu typem całkowitym nieujemnym (o wartościach od 0 do 255) jest typ unsigned char.
Przyjrzyjmy się, jakie konsekwencje ma ta dwoista natura typu char.
Przypisując wartość zmiennej typu char, możemy to zrobić, albo wstawiając żądany znak w apostrofy, albo podając numer tego znaku w kodzie ASCII. Czyli np. oba poniższe przypisania są równoważne:
char znak = 'a';
char znak = 97;
Z przyczyn technicznych przy wczytywaniu i wypisywaniu to już tak łatwo nie zadziała. Chodzi o to, że typ char wczytuje i wypisuje znak, a nie liczbę. Jeśli więc przy takim fragmencie programu:
char znak;
cin >> znak;
wpiszemy na wejściu liczbę 97, to zamiast znaku 'a' o kodzie ASCII 97 zostanie wczytany po prostu znak '9', jako pierwszy znak na wejściu!
Aby wczytać znak o danym kodzie ASCII, należy wczytać ten kod jako liczbę całkowitą innego typu niż char (np. typu int) i przypisać zmiennej znak wczytaną wartość:
int kod;
char znak;
cin >> kod;
znak = kod;
Oczywiście zadziała to tylko wtedy, gdy kod będzie faktycznie liczbą z zakresu typu char – w przeciwnym razie wystąpi znany nam już błąd przekroczenia zakresu typu.
Podobnie z wypisywaniem kodu ASCII znaku:
char znak;
int kod;
kod = znak;
cout << kod << endl;
Jest to szczególny przykład tzw. konwersji typów (inaczej: rzutowania typów), czyli zmiany jednego typu na inny. W przypadku typów całkowitych w C++ dokonuje się ona automatycznie, w momencie przypisania zmiennej jednego typu wartości zmiennej (bądź wyrażenia) innego typu. Można też jawnie "poprosić" kompilator C++, aby dokonał konwersji. Robi się to, umieszczając nazwę typu przed zmienną:
char znak;
cout << (int)znak << endl;
Trochę więcej o konwersjach opowiemy w następnych lekcjach. Tymczasem jeszcze jeden, "złośliwy" przykład. Powiedzmy, że chcielibyśmy napisać program, który wczyta liczbę i∈{1,…,26}i∈{1,…,26}, bowiem alfabet angielski ma 26 liter). Moglibyśmy to próbować zrobić tak:
int numer;
cin >> numer;
cout << 'a' + numer - 1;
Gdy na wejściu wprowadzimy np. liczbę 3, na wyjściu otrzymamy... liczbę 99, czyli kod ASCII litery c! Jest tak dlatego, że wyniki działań arytmetycznych są w C++ domyślnie interpretowane jako liczby (w tym przypadku typu int). Aby otrzymać na wyjściu rzeczywiście literę c, musimy użyć konwersji na typ char:
int numer;
cin >> numer;
cout << (char)('a' + numer - 1);
Zadania
Zagadka- Mosty