��������������������������������������������� ���������������������������������������������
����������������������������������������������������������������� ����������������������������������������������������������������� ���������������������������������������������������������������� ���������������������������������������������������������������� ��������������������������������������������������������������� ��������������������������������������������������������������� �������������������������������������������������� �������������������������������������������������� ���������������������������������������������������������������������������������� �������������������������������������������������������������������������������� ���������������������������������������������������������������������������������� ����������������������������������������������������������������������������������� �������������������������������������������������������������������������������� ������������������������������������������������������������������������������������� ����������������������������������������������������������������������������������� ������� ������������ ������ �� ������������ ���������� ��������� ������������� ����������� ������������������������������������������������������������������������������������� ���������� ����������� ����� ����������� ������ ������������ ����� ������������� ������� ������������ ������ �� ������������ ���������� ��������� ������������� ����������� ������� ��� ���������� �������� ��� ��������� ���������� ��������� ��������� �������� ���������� ����������� ����� ����������� ������ ������������ ����� ������������� ���������������������������������������������������������������������������������� ������� ��� ���������� �������� ��� ��������� ���������� ��������� ��������� �������� �������������������������������������������������������������������������������� ���������������������������������������������������������������������������������� ���������������������������������������������������������������������������� �������������������������������������������������������������������������������� ������� ��� ������������ ������������ ������ ������� �������������� ������� ��������� ���������������������������������������������������������������������������� ������������������������������������������������������������������������������������ ������� ��� ������������ ������������ ������ ������� �������������� ������� ��������� ����������������������������������������������������� ������������������������������������������������������������������������������������ �����������������������������������������������������
����������������������������������
��������������������������������������������� ����������������������������������������������������������������� ���������������������������������������������������������������� ��������������������������������������������������������������� �������������������������������������������������� ���������������������������������������������������������������������������������� �������������������������������������������������������������������������������� ����������������������������������������������������������������������������������� ������������������������������������������������������������������������������������� ������� ������������ ������ �� ������������ ���������� ��������� ������������� ����������� ���������� ����������� ����� ����������� ������ ������������ ����� ������������� ������� ��� ���������� �������� ��� ��������� ���������� ��������� ��������� �������� ���������������������������������������������������������������������������������� �������������������������������������������������������������������������������� ���������������������������������������������������������������������������� ������� ��� ������������ ������������ ������ ������� �������������� ������� ��������� ������������������������������������������������������������������������������������ �����������������������������������������������������
����������������������������������
Upozornění pro čtenáře a uživatele této knihy Všechna práva vyhrazena. Žádná část této tištěné či elektronické knihy nesmí být reprodukována a šířena v papírové, elektronické či jiné podobě bez předchozího písemného souhlasu nakladatele. Neoprávněné užití této knihy bude trestně stíháno.
Jazyky C a C++ kompletní průvodce – 2., aktualizované vydání Miroslav Virius Vydala Grada Publishing, a.s. U Průhonu 22, Praha 7 jako svou 4475. publikaci Odpovědný redaktor Pavel Němeček Sazba Tomáš Brejcha Počet stran 368 První vydání, Praha 2011 © Grada Publishing, a.s., 2011 V knize použité názvy programových produktů, firem apod. mohou být ochrannými známkami nebo registrovanými ochrannými známkami příslušných vlastníků. Vytiskla Tiskárna PROTISK, s.r.o., České Budějovice ISBN 978-80-247-3917-5 (tištěná verze) ISBN 978-80-247-7417-6 (elektronická verze ve formátu PDF) © Grada Publishing, a.s. 2012
Obsah Předmluva ������������������������������������������������������������������������������������������������������������������� 17
1.
Úvod 1.1 První program ����������������������������������������������������������������������������������������������� 19 1.1.1 Co je co ����������������������������������������������������������������������������������������������������� 20 1.1.2 Překlad a sestavení ������������������������������������������������������������������������������� 23
1.2 Programovací jazyky C a C++ ��������������������������������������������������������������� 25 1.2.1 Standardy ������������������������������������������������������������������������������������������������� 26
1.3 Objektově orientované programování ����������������������������������������������� 27 1.3.1 Základní pojmy OOP ����������������������������������������������������������������������������� 28 1.3.2 Některé další pojmy ����������������������������������������������������������������������������� 30
2.
Základní pojmy 2.1 Popis jazyků C a C++ ��������������������������������������������������������������������������������� 31 2.2 Množina znaků ��������������������������������������������������������������������������������������������� 32 2.2.1 Univerzální jména znaků ��������������������������������������������������������������������� 33
2.3 Identifikátor ��������������������������������������������������������������������������������������������������� 34 2.3.1 Oblast platnosti, oblast viditelnosti ������������������������������������������������� 35
2.4 Klíčová slova ������������������������������������������������������������������������������������������������� 35 2.5 Lexikální konvence a zápis programu ������������������������������������������������� 37 2.6 Průběh překladu ����������������������������������������������������������������������������������������� 37 2.6.1 Průběh překladu podrobně ��������������������������������������������������������������� 38 2.6.2 Pozorovatelné chování programu ��������������������������������������������������� 39
2.7 Definice a deklarace ����������������������������������������������������������������������������������� 40 2.7.1 Deklarace ��������������������������������������������������������������������������������������������������� 40 2.7.2 Definice ����������������������������������������������������������������������������������������������������� 40 2.7.3 Pravidlo jediné definice ����������������������������������������������������������������������� 41
2.8 L-hodnota a r-hodnota ����������������������������������������������������������������������������� 42 2.8.1 L-hodnota, r-hodnota a další (C++0X) ������������������������������������������� 43
2.9 Zarovnání ������������������������������������������������������������������������������������������������������� 43 2.10 Běh programu ����������������������������������������������������������������������������������������������� 44 2.10.1 Inicializace globálních proměnných ����������������������������������������������� 44 2.10.2 Ukončení programu ����������������������������������������������������������������������������� 45
Obsah 5
3.
Základní datové typy 3.1 Celá čísla ��������������������������������������������������������������������������������������������������������� 47 3.1.1 Celočíselné literály ��������������������������������������������������������������������������������� 49 3.1.2 Další celočíselné typy v C99 a v C++0x ����������������������������������������� 50
3.2 Znakové typy ������������������������������������������������������������������������������������������������� 51 3.2.1 Znakové literály ��������������������������������������������������������������������������������������� 52
3.3 Logické hodnoty ������������������������������������������������������������������������������������������� 53 3.3.1 Typ bool (C++) ��������������������������������������������������������������������������������������� 53 3.3.2 Typ _Bool (C99) ��������������������������������������������������������������������������������������� 54
3.4 Operace s celými čísly ��������������������������������������������������������������������������������� 54 3.4.1 Přiřazování ������������������������������������������������������������������������������������������������� 54 3.4.2 Aritmetické operace ����������������������������������������������������������������������������� 54 3.4.3 Relace ��������������������������������������������������������������������������������������������������������� 56 3.4.4 Logické operace ������������������������������������������������������������������������������������� 56 3.4.5 Bitové operace ��������������������������������������������������������������������������������������� 56 3.4.6 Další operace ������������������������������������������������������������������������������������������� 58
3.5 Reálná čísla ��������������������������������������������������������������������������������������������������� 58 3.5.1 Reálné typy v C99 ��������������������������������������������������������������������������������� 59 3.5.2 Reálné literály ������������������������������������������������������������������������������������������ 59 3.5.3 Operace s reálnými čísly ��������������������������������������������������������������������� 60
3.6 Komplexní čísla (jen C99) ������������������������������������������������������������������������� 61 3.6.1 Operace s komplexními čísly ������������������������������������������������������������� 62 3.6.2 Přiřazování ������������������������������������������������������������������������������������������������� 62 3.6.3 Aritmetické operace ����������������������������������������������������������������������������� 62 3.6.4 Logické operace ������������������������������������������������������������������������������������� 62 3.6.5 Další operace s komplexními čísly ������������������������������������������������� 63
4.
3.7 Typ void ����������������������������������������������������������������������������������������������������������� 63
Výčtové typy, struktury a unie 4.1 Výčtové typy ��������������������������������������������������������������������������������������������������� 65 4.1.1 4.1.2 4.1.3 4.1.4 4.1.5
Deklarace výčtového typu ����������������������������������������������������������������� 65 Použití výčtového typu ����������������������������������������������������������������������� 68 Rozsah výčtového typu ����������������������������������������������������������������������� 68 Operace s výčtovými typy ����������������������������������������������������������������� 69 Přetěžování operátorů ������������������������������������������������������������������������� 70
4.2 Struktury ��������������������������������������������������������������������������������������������������������� 70 4.2.1 Deklarace struktury ������������������������������������������������������������������������������� 70 4.2.2 Složky struktur ����������������������������������������������������������������������������������������� 72 4.2.3 Inicializace ������������������������������������������������������������������������������������������������� 73 6 Jazyky C a C++
4.2.4 4.2.5 4.2.6 4.2.7
Bitová pole ����������������������������������������������������������������������������������������������� 74 Otevřená pole ����������������������������������������������������������������������������������������� 75 Literály typu struktura ������������������������������������������������������������������������� 75 Přetěžování operátorů ������������������������������������������������������������������������� 76
4.3 Unie ������������������������������������������������������������������������������������������������������������������� 76 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6
5.
Deklarace unie ��������������������������������������������������������������������������������������� 76 Složky unií ������������������������������������������������������������������������������������������������� 77 Inicializace unií ��������������������������������������������������������������������������������������� 78 Literály typu unie ����������������������������������������������������������������������������������� 78 Anonymní unie (jen C++) ����������������������������������������������������������������� 78 Přetěžování operátorů ������������������������������������������������������������������������� 79
Ukazatele, pole a reference 5.1 Pole ������������������������������������������������������������������������������������������������������������������� 81 5.1.1 Jednorozměrná pole ���������������������������������������������������������������������������� 81 5.1.2 Inicializace ������������������������������������������������������������������������������������������������� 82 5.1.3 Použití polí ����������������������������������������������������������������������������������������������� 83 5.1.4 Vícerozměrná pole ��������������������������������������������������������������������������������� 85 5.1.5 Předávání pole jako parametru funkce ����������������������������������������� 86 5.1.6 Literály typu pole (C99) ����������������������������������������������������������������������� 87
5.2 Ukazatele ������������������������������������������������������������������������������������������������������� 87 5.2.1 Inicializace ukazatelů ��������������������������������������������������������������������������� 89 5.2.2 Dereferencování ������������������������������������������������������������������������������������� 90 5.2.3 Dynamické přidělování a navracení paměti ������������������������������� 91 5.2.4 Uvolňování dynamicky alokované paměti ����������������������������������� 94 5.2.5 Aritmetické operace s ukazateli ������������������������������������������������������� 95 5.2.6 Ukazatele na funkce ����������������������������������������������������������������������������� 96 5.2.7 Ukazatele na statické prvky tříd ������������������������������������������������������� 97 5.2.8 Restringované ukazatele v C99 ������������������������������������������������������� 97
5.3 Reference (jen v C++) ������������������������������������������������������������������������������� 99 5.3.1 Reference na funkce �������������������������������������������������������������������������� 101 5.3.2 „Konstantní“ reference ���������������������������������������������������������������������� 101 5.3.3 Reference na r-hodnotu (C++0x) �������������������������������������������������� 101
6.
Proměnné a deklarace 6.1 Syntax deklarace �������������������������������������������������������������������������������������� 103 6.1.1 Mnemotechnické uspořádání deklarace ���������������������������������� 103 6.1.2 Popis deklarace ������������������������������������������������������������������������������������ 103 6.1.3 Význam základních tvarů deklarátoru ���������������������������������������� 104 Obsah 7
6.1.4 6.1.5 6.1.6 6.1.7
Automatické odvození typu (C++0x) ������������������������������������������ Specifikace decltype (C++0x) �������������������������������������������������������� Označení typu �������������������������������������������������������������������������������������� Deklarace nového jména typu ������������������������������������������������������
105 106 106 106
6.2 Paměťové třídy ������������������������������������������������������������������������������������������ 107 6.2.1 Specifikátory paměťových tříd ������������������������������������������������������ 6.2.2 Automatické proměnné (paměťová třída auto) �������������������� 6.2.3 Registrové proměnné (paměťová třída register) �������������������� 6.2.4 Statické proměnné (paměťová třída static) ������������������������������ 6.2.5 Externí proměnné (paměťová třída extern) ������������������������������ 6.2.6 Měnitelné složky konstant (paměťová třída mutable) �������� 6.2.7 Proměnné lokální v podprocesu (paměťová třída thread_local) ����������������������������������������������������������������������������������������
107 107 108 108 109 109 109
6.3 Jiné specifikátory �������������������������������������������������������������������������������������� 109 6.3.1 Cv-modifikátory ���������������������������������������������������������������������������������� 109 6.3.2 Volací konvence ���������������������������������������������������������������������������������� 112 6.3.3 Další modifikátory ������������������������������������������������������������������������������ 112
6.4 Atributy (C++0x) �������������������������������������������������������������������������������������� 113 6.4.1 Specifikace zarovnání ���������������������������������������������������������������������� 113 6.4.2 Další atributy ���������������������������������������������������������������������������������������� 113
6.5 Doba života, oblast platnosti a viditelnost ������������������������������������ 114 6.5.1 Oblast platnosti identifikátoru ������������������������������������������������������ 114 6.5.2 Viditelnost identifikátoru ������������������������������������������������������������������ 115 6.5.3 Doba života proměnné �������������������������������������������������������������������� 116
6.6 Rozdělení identifikátorů ������������������������������������������������������������������������ 116 6.6.1 Jazyk C ���������������������������������������������������������������������������������������������������� 116 6.6.2 Jazyk C++ ���������������������������������������������������������������������������������������������� 117
7.
6.7 Deklarace asm �������������������������������������������������������������������������������������������� 118 6.8 Deklarace static_assert (C++0x) ������������������������������������������������������ 118
Jmenné prostory 7.1 Deklarace jmenného prostoru ������������������������������������������������������������ 119 7.2 Přejmenování prostoru jmen �������������������������������������������������������������� 120 7.3 using �������������������������������������������������������������������������������������������������������������� 121 7.3.1 Direktiva using �������������������������������������������������������������������������������������� 121 7.3.2 Deklarace using ���������������������������������������������������������������������������������� 123
7.4 Koenigovo vyhledávání �������������������������������������������������������������������������� 124 7.4.1 K čemu to je ���������������������������������������������������������������������������������������� 125 7.4.2 Koenigovo vyhledávání a šablony ������������������������������������������������ 126 8 Jazyky C a C++
8.
Operátory a výrazy 8.1 Výraz �������������������������������������������������������������������������������������������������������������� 127 8.1.1 Konstantní výraz �������������������������������������������������������������������������������� 127
8.2 Přehled operátorů ������������������������������������������������������������������������������������ 129 8.2.1 Priorita a asociativita ������������������������������������������������������������������������ 131 8.2.2 Pořadí vyhodnocování operandů ���������������������������������������������� 131
8.3 Konverze ������������������������������������������������������������������������������������������������������ 131 8.3.1 8.3.2 8.3.3 8.3.4 8.3.5
Celočíselná a reálná rozšíření ������������������������������������������������������ Obvyklé aritmetické konverze ���������������������������������������������������� Konverze číselných typů ���������������������������������������������������������������� Konverze ukazatelů �������������������������������������������������������������������������� Standardní konverze ������������������������������������������������������������������������
132 135 135 136 137
8.4 Popis jednotlivých operátorů �������������������������������������������������������������� 137 8.4.1 Operátory pro přístup k datům �������������������������������������������������� 8.4.2 Aritmetické operátory �������������������������������������������������������������������� 8.4.3 Inkrementace a dekrementace ++,-- ���������������������������������������� 8.4.4 Relační operátory ������������������������������������������������������������������������������ 8.4.5 Bitové operace ���������������������������������������������������������������������������������� 8.4.6 Logické operátory ���������������������������������������������������������������������������� 8.4.7 Přiřazovací operátory ���������������������������������������������������������������������� 8.4.8 Alokace a uvolňování paměti ������������������������������������������������������ 8.4.9 Přetypování ���������������������������������������������������������������������������������������� 8.4.10 Dynamická identifikace typu typeid ���������������������������������������� 8.4.11 Operátor sizeof ���������������������������������������������������������������������������������� 8.4.12 Zjištění adresy & �������������������������������������������������������������������������������� 8.4.13 Podmínkový operátor (podmíněný výraz) ?: �������������������������� 8.4.14 Operátor čárka , �������������������������������������������������������������������������������� 8.4.15 Operátor throw ���������������������������������������������������������������������������������� 8.4.16 Operátor decltype (C++0x) ����������������������������������������������������������
9.
137 144 146 147 150 153 156 157 162 170 172 173 174 176 177 178
Příkazy 9.1 Jednoduché příkazy �������������������������������������������������������������������������������� 179 9.1.1 Výrazový příkaz ���������������������������������������������������������������������������������� 179 9.1.2 Prázdný příkaz ������������������������������������������������������������������������������������ 180 9.1.3 Deklarace jako příkaz �������������������������������������������������������������������� 180
9.2 Složený příkaz (blok) ������������������������������������������������������������������������������ 180 9.3 Větvení programu ������������������������������������������������������������������������������������ 180 9.3.1 Podmíněný příkaz if ������������������������������������������������������������������������ 180 9.3.2 Příkaz switch �������������������������������������������������������������������������������������� 182 Obsah 9
9.4 Cykly �������������������������������������������������������������������������������������������������������������� 185 9.4.1 9.4.2 9.4.3 9.4.4
Příkaz while ���������������������������������������������������������������������������������������� Příkaz for ���������������������������������������������������������������������������������������������� Cyklus do-while �������������������������������������������������������������������������������� Příkaz cyklu pro procházení kontejneru (jen C++0x) ��������
186 187 188 189
9.5 Přenos řízení ���������������������������������������������������������������������������������������������� 190 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.5.6 9.5.7 9.5.8
Příkaz continue ���������������������������������������������������������������������������������� Příkaz break ���������������������������������������������������������������������������������������� Příkaz return ���������������������������������������������������������������������������������������� Příkaz goto a návěští ������������������������������������������������������������������������ Příkaz throw ���������������������������������������������������������������������������������������� Příkaz __leave ������������������������������������������������������������������������������������ Ukončení programu pomocí funkce exit() ���������������������������� Dlouhý skok pomocí longjmp( ) ������������������������������������������������
190 191 191 192 193 193 193 193
9.6 Příkaz asm �������������������������������������������������������������������������������������������������� 193
10.
Funkce 10.1 Definice a deklarace funkce ������������������������������������������������������������������ 195 10.1.1 10.1.2 10.1.3 10.1.4 10.1.5 10.1.6 10.1.7 10.1.8 10.1.9
Definice funkce ���������������������������������������������������������������������������������� Deklarace funkce ������������������������������������������������������������������������������ Parametry funkce ������������������������������������������������������������������������������ Lokální proměnné ���������������������������������������������������������������������������� Paměťová třída funkcí �������������������������������������������������������������������� Modifikátor inline ������������������������������������������������������������������������������ Zastaralý způsob deklarace ���������������������������������������������������������� Spolupráce C s C++ ������������������������������������������������������������������������ Konstantní funkce (C++0x) ����������������������������������������������������������
195 198 199 200 200 200 201 202 203
10.2 Výměna dat mezi funkcemi ������������������������������������������������������������������ 203 10.2.1 Vracená hodnota ���������������������������������������������������������������������������� 10.2.2 Parametry �������������������������������������������������������������������������������������������� 10.2.3 Funkce s proměnným počtem parametrů ���������������������������� 10.2.4 Globální proměnné �������������������������������������������������������������������������� 10.2.5 Statické proměnné ��������������������������������������������������������������������������
204 206 211 213 213
10.3 Volací konvence ���������������������������������������������������������������������������������������� 213 10.3.1 10.3.2 10.3.4 10.3.5
Volací konvence jazyka C �������������������������������������������������������������� Volací konvence jazyka Pascal ���������������������������������������������������� Standardní konvence ���������������������������������������������������������������������� Registrová konvence ����������������������������������������������������������������������
214 214 214 215
10.4 Funkce main() �������������������������������������������������������������������������������������������� 215 10 Jazyky C a C++
10.4.1 Parametry funkce main() ��������������������������������������������������������������� 215
10.5 Rekurze a režie volání funkcí �������������������������������������������������������������� 216 10.5.1 Rekurzivní volání funkce ���������������������������������������������������������������� 216 10.5.2 Režie volání funkce �������������������������������������������������������������������������� 217
10.6 Přetěžování funkcí ������������������������������������������������������������������������������������ 217 10.6.1 10.6.2 10.6.3 10.6.4
Přetěžování obyčejných funkcí ���������������������������������������������������� Přetěžování metod (členských funkcí) �������������������������������������� Která přetížená funkce se zavolá? ���������������������������������������������� Přetěžování, překrytí, zastínění ����������������������������������������������������
218 218 219 220
10.7 Lambda-výrazy (C++0x) ���������������������������������������������������������������������� 220 10.7.1 Deklarace lambda-výrazu �������������������������������������������������������������� 221 10.7.2 Záchyt ��������������������������������������������������������������������������������������������������� 222
10.8 Modulární programování a funkce ���������������������������������������������������� 224
11.
Třídy a objekty 11.1 Deklarace třídy ������������������������������������������������������������������������������������������ 225 11.1.1 Specifikace přístupových práv ���������������������������������������������������� 226 11.1.2 Třídy a OOP ���������������������������������������������������������������������������������������� 227
11.2 Datové složky ���������������������������������������������������������������������������������������������� 229 11.2.1 Nestatické datové složky ��������������������������������������������������������������� 229 11.2.2 Statické datové složky �������������������������������������������������������������������� 230
11.3 Členské funkce (metody) ���������������������������������������������������������������������� 232 11.3.1 11.3.2 11.3.3 11.3.4
Nestatické členské funkce ������������������������������������������������������������ Statické členské funkce ������������������������������������������������������������������ Definice metody uvnitř třídy �������������������������������������������������������� Definice vně definice třídy ������������������������������������������������������������
232 234 235 235
11.4 Ukazatel this ���������������������������������������������������������������������������������������������� 236 11.5 Přístup ke složkám tříd �������������������������������������������������������������������������� 237 11.5.1 Přístup zevnitř třídy �������������������������������������������������������������������������� 237 11.5.2 Přístup z vnějšku třídy �������������������������������������������������������������������� 238 11.5.3 Spřátelené funkce ���������������������������������������������������������������������������� 238
11.6 Dědění ���������������������������������������������������������������������������������������������������������� 240 11.6.1 Předkové ���������������������������������������������������������������������������������������������� 11.6.2 Přístupová práva pro zděděné složky �������������������������������������� 11.6.3 Přetěžování a zastínění ������������������������������������������������������������������ 11.6.4 Virtuální dědění ��������������������������������������������������������������������������������
240 241 242 243
11.7 Polymorfizmus ������������������������������������������������������������������������������������������ 245 11.7.1 Časná a pozdní vazba �������������������������������������������������������������������� 245
Obsah 11
11.7.2 Virtuální metody (C++03) �������������������������������������������������������������� 246 11.7.3 Virtuální destruktor �������������������������������������������������������������������������� 248 11.7.4 Abstraktní třídy, čistě virtuální metody ������������������������������������ 248
11.8 Upřesnění v deklaraci třídy (C++0x) ������������������������������������������������ 249 11.8.1 Třídy, od nichž nelze odvozovat potomky ������������������������������ 250 11.8.2 Explicitní deklarace překrytí a zastínění ���������������������������������� 250
11.9 Zvláštní metody ���������������������������������������������������������������������������������������� 251 11.9.1 Konstruktory �������������������������������������������������������������������������������������� 11.9.2 Kopírovací konstruktor �������������������������������������������������������������������� 11.9.3 Dědění konstruktorů (C++0x) ������������������������������������������������������ 11.9.4 Konstruktor pro konstantní výrazy (C++0x) �������������������������� 11.9.5 Volání jiného konstruktoru téže třídy (C++0x) ���������������������� 11.9.6 Destruktory ����������������������������������������������������������������������������������������� 11.9.7 Pořadí volání konstruktorů a destruktorů �������������������������������� 11.9.8 Volání virtuálních metod z konstruktorů a destruktorů ������
251 256 258 259 259 260 260 261
11.10 Vytváření instancí ������������������������������������������������������������������������������������ 262 11.10.1 Konstantní a nestálé instance ������������������������������������������������������ 262 11.10.2 Pole instancí �������������������������������������������������������������������������������������� 262
11.11 Lokální třídy ������������������������������������������������������������������������������������������������ 263 11.12 Vnořené typy ���������������������������������������������������������������������������������������������� 263 11.12.1 Vnořené třídy �������������������������������������������������������������������������������������� 264
11.13 Ukazatele na instance ���������������������������������������������������������������������������� 265 11.14 Struktury a unie ���������������������������������������������������������������������������������������� 266 11.14.1 Struktury ���������������������������������������������������������������������������������������������� 266 11.14.2 Unie �������������������������������������������������������������������������������������������������������� 267
11.15 Třídní ukazatele ���������������������������������������������������������������������������������������� 267 11.15.1 Ukazatel na datovou složku ���������������������������������������������������������� 267 11.15.2 Ukazatel na metodu ������������������������������������������������������������������������ 269 11.15.3 Ukazatele na statické složky �������������������������������������������������������� 271
12.
Přetěžování operátorů 12.1 Základní pravidla �������������������������������������������������������������������������������������� 273 12.1.1 Omezení ���������������������������������������������������������������������������������������������� 273
12.2 Operátory, které lze přetěžovat jako metody i jako volné funkce ����������������������������������������������������������������������������������������������� 274 12.2.1 Přetěžování unárních operátorů ������������������������������������������������ 274 12.2.2 Přetěžování binárních operátorů ������������������������������������������������ 276
12.3 Operátory, které lze přetěžovat jen jako metody ������������������������ 277
12 Jazyky C a C++
12.3.1 12.3.2 12.3.3 12.3.4 12.3.5
Přetěžování operátoru volání funkce ���������������������������������������� Přetěžování přiřazovacího operátoru ���������������������������������������� Přetěžování operátoru indexování �������������������������������������������� Přetěžování operátoru -> ������������������������������������������������������������ Operátor přetypování (konverzní funkce) ������������������������������
277 278 280 281 281
12.4 Operátory pro práci s pamětí �������������������������������������������������������������� 282 12.4.1 12.4.2 12.4.3 12.4.4
Co můžeme změnit ������������������������������������������������������������������������ Přetěžování operátoru new ���������������������������������������������������������� Přetěžování operátoru delete ������������������������������������������������������ Operátory new a delete a výjimky ��������������������������������������������
282 283 285 286
12.5 Uživatelem definované literály (C++0x) ���������������������������������������� 288 12.5.1 Parametry literálového operátoru ���������������������������������������������� 288 12.5.2 Surové a hotové literály ������������������������������������������������������������������ 289
13.
Výjimky 13.1 Proč výjimky? ��������������������������������������������������������������������������������������������� 291 13.1.1 Oč jde ���������������������������������������������������������������������������������������������������� 292
13.2 Klasické řešení v C: dlouhý skok ���������������������������������������������������������� 292 13.2.1 Použití dlouhého skoku ������������������������������������������������������������������ 292
13.3 Výjimky v C++ ������������������������������������������������������������������������������������������ 294 13.3.1 13.3.2 13.3.3 13.3.4 13.3.5 13.3.6 13.3.7
Schéma použití výjimek ���������������������������������������������������������������� Co se děje �������������������������������������������������������������������������������������������� Částečné ošetření ���������������������������������������������������������������������������� Výjimky a funkce ������������������������������������������������������������������������������ Neošetřené a neočekávané výjimky ���������������������������������������� Standardní třídy výjimek ���������������������������������������������������������������� Výjimky a alokace paměti ��������������������������������������������������������������
294 295 297 297 299 300 303
13.4 Strukturované výjimky v jazyce C ������������������������������������������������������ 303 13.4.1 Schéma použití SEH ������������������������������������������������������������������������ 13.4.2 Co se děje �������������������������������������������������������������������������������������������� 13.4.3 Obsluha a filtr ������������������������������������������������������������������������������������ 13.4.4 Vznik strukturovaných výjimek ���������������������������������������������������� 13.4.5 Filtr ���������������������������������������������������������������������������������������������������������� 13.4.6 Nepokračovatelné výjimky ����������������������������������������������������������� 13.4.7 Koncovka bloku �������������������������������������������������������������������������������� 13.4.8 Neošetřené výjimky ������������������������������������������������������������������������
Obsah 13
304 305 305 306 307 308 308 309
14.
Šablony 14.1 Deklarace šablony ������������������������������������������������������������������������������������ 313 14.1.1 Instance šablony ������������������������������������������������������������������������������ 314
14.2 Parametry šablon ������������������������������������������������������������������������������������ 314 14.2.1 Typové parametry ���������������������������������������������������������������������������� 315 14.2.2 Hodnotové parametry �������������������������������������������������������������������� 315 14.2.3 Šablonové parametry ���������������������������������������������������������������������� 316
14.3 Šablony volných funkcí �������������������������������������������������������������������������� 316 14.3.1 Vytváření instancí ������������������������������������������������������������������������������ 317 14.3.2 Explicitní (úplná) specializace ������������������������������������������������������ 318 14.3.3 Přetěžování šablon volných funkcí �������������������������������������������� 319
14.4 Šablony objektových typů �������������������������������������������������������������������� 322 14.4.1 Šablony metod ���������������������������������������������������������������������������������� 14.4.2 Šablony statických datových složek ������������������������������������������ 14.4.3 Vnořené šablony ������������������������������������������������������������������������������ 14.4.4 Vytváření instancí ������������������������������������������������������������������������������ 14.4.5 Specializace ���������������������������������������������������������������������������������������� 14.4.6 Přátelé �������������������������������������������������������������������������������������������������� 14.4.7 Dědění ��������������������������������������������������������������������������������������������������
323 324 325 327 327 329 333
14.5 Organizce programu �������������������������������������������������������������������������������� 334 14.5.1 Exportní šablony (C++03) ������������������������������������������������������������ 334 14.5.2 Externí šablony (C++0x) ���������������������������������������������������������������� 335
14.6 Šablony s proměnným počtem parametrů (C++0x) ������������������ 335 14.6.1 Parametry variadické šablony ������������������������������������������������������ 335 14.6.2 Rozvoj balíku parametrů ���������������������������������������������������������������� 335
14.7 Alias ���������������������������������������������������������������������������������������������������������������� 339 14.8 Různá omezení ������������������������������������������������������������������������������������������ 339
15.
Dynamická identifikace typů 15.1 Určení typu za běhu �������������������������������������������������������������������������������� 341 15.1.1 Operátor typeid �������������������������������������������������������������������������������� 341 15.1.2 Třída type_info ���������������������������������������������������������������������������������� 342 15.1.3 Operátor dynamic_cast ������������������������������������������������������������������ 342
15.2 Příklady užití RTTI ������������������������������������������������������������������������������������ 342 15.2.1 Rozhodování podle typu �������������������������������������������������������������� 342 15.2.2 Ladění ���������������������������������������������������������������������������������������������������� 343 15.2.3 Příslušnost k hierarchii �������������������������������������������������������������������� 343
14 Jazyky C a C++
16.
Preprocesor 16.1 Úvod �������������������������������������������������������������������������������������������������������������� 345 16.2 Direktivy preprocesoru �������������������������������������������������������������������������� 347 16.2.1 16.2.2 16.2.3 16.2.4 16.2.5 16.2.6 16.2.7 16.2.8
Prázdná direktiva ������������������������������������������������������������������������������ Vkládání souborů ������������������������������������������������������������������������������ Makra ���������������������������������������������������������������������������������������������������� Zrušení definice makra ������������������������������������������������������������������ Podmíněný překlad �������������������������������������������������������������������������� Vyvolání chyby ���������������������������������������������������������������������������������� Číslování řádků ���������������������������������������������������������������������������������� Direktiva závislá na implementaci ����������������������������������������������
347 347 348 352 352 355 355 355
16.3 Předdefinovaná makra �������������������������������������������������������������������������� 357 Literatura ������������������������������������������������������������������������������������������������������������������ 359 Rejstřík ���������������������������������������������������������������������������������������������������������������������� 361
Obsah 15
Předmluva Otevřeli jste knihu, která vám poskytne referenční příručku programovacích jazyků C a C++ podle platných standardů, a to včetně připravovaného nového standardu jazyka C++ označovaného zatím C++0x.
Co v této knize najdete V úvodní kapitole najdete příklad jednoduchého programu, základní informace o vytváření zdrojového textu programu a o postupu při překladu. Tato kapitola se poněkud vymyká z rámce celé knihy, neboť spíše než referenční příručku připomíná první kapitolu učebnice. Jejím cílem je poskytnout rámec, který by mu usnadnil pochopení dalších kapitol i čtenářům, kteří s jazyky C a C++ nemají žádnou zkušenost, nebo ji mají jen velmi malou. V závěru první kapitoly najdete také několik slov o historii těchto jazyků, o jejich mezinárodních standardech, a také velice stručný výklad základních pojmů objektově orientovaného programování se zřetelem k C a C++. Ve druhé kapitole se seznámíte se způsobem popisu jazyků C a C++ a se základními stavebními prvky, jako je množina znaků, identifikátory, klíčová slova atd. Následující kapitoly popisují základní datové typy, uživatelem definované neobjektové typy, výrazy, příkazy, operátory atd. – ostatně to najdete v obsahu. Každý významový celek začíná zpravidla popisem syntaxe, za nímž následuje stručné vysvětlení významu a podstatné informace shrnuté do bodů a doplněné příklady. Tento postup ale nemělo smysl dodržovat vždy. Převážnou většinu příkladů jsem odzkoušel na současných překladačích jazyků C a C++ na PC. V několika případech jsem převzal příklady přímo ze standardů [1] a [3]; zpravidla se jednalo o rysy jazyka, které běžné současné překladače ještě neimplementují nebo je neimplementují v souladu se standardy. (Může vám připadat podivné, že překladače jazyka C z roku 2010 neimplementují v plném rozsahu novinky standardu [3] tohoto jazyka z roku 1999, ale je to tak; pro tvůrce většiny překladačů je ovšem důležitější kompatibilita s jazykem C++ – a ještě standard [1] jazyka C++ z roku 2003 byl založen na standardu [2] jazyka C z roku 1990. Většiny příkladů týkajících se C++0x jsem převzal z návrhu nového standardu [4]; některé z nich bylo možno vyzkoušet v současných překladačích, ovšem zdaleka ne všechny. Poznamenejme, že dokument [4], z něhož jsem čerpal, je označen jako konečný návrh standardu.
Jak tato kniha vznikla V roce 1999 vydalo nakladatelství Grada Publishing knihu Programovací jazyky C a C++podle normy ANSI/ISO – kompletní kapesní průvodce (D. Louis, P. Mejzlík, M. Virius), která shrnovala oba jazyky i jejich knihovny do jediného svazku. V době, kdy jsme ji psali, tedy v letech 1997–1998, ovšem nebylo ještě k dispozici konečné znění prvního standardu jazyka C++, vydané v září 1998, ani nový standard jazyka C, vydaný v r. 1999. Neobsahovala tedy řadu důležitých informací. Také příklady byly přizpůsobeny nejrozšířenějším překladačům té doby – a ty se od standardu v mnoha ohledech odchylovaly. Ostatně ani struktura knihy nebyla právě šťastná, neboť byla přizpůsobena striktním požadavkům edice (10 kapitol, co nejvíce „postupů“ apod.).
Předmluva 17
*
Proto jsem se v po dohodě s nakladatelstvím Grada Publishing rozhodl tuto knihu od základu přepracovat. Z původního díla jsem převzal některé příklady a části textu, avšak i ty jsem přizpůsobil novým standardům obou jazyků. Tato verze vyšla v nakladatelství Grada Publishing pod názvem Jazyky C a C++ – Kompletní kapesní průvodce programátora (M. Virius, 2005). Na začátku roku 2011 mne nakladatelství Grada požádalo, abych tuto knihu připravil k novému vydání. Protože koncem roku 2011 má vyjít nový standard C++ s řadou zásadních novinek, rozhodl jsem se k dalšímu přepracování celé knihy, aby obsahovala oba jazyky podle standardů, jež budou platit v době, kdy vyjde. Výsledek držíte v ruce.
Typografické a jiné konvence Domnívám se, že nemá smysl zdůrazňovat, že kurziva znamená zdůraznění a neproporcionální písmo že používám k zápisu ukázek zdrojového textu, identifikátorů, klíčových slov apod. a výstupu počítače. To pozná každý průměrně inteligentní čtenář na první pohled (a programátoři bývají více než průměrně inteligentní). Způsob a význam popisu syntaktických konstrukcí, který v této knize používám, je vysvětlen v kapitole 2.1 na str. 31. [1] V hranatých závorkách jsou uvedeny odkazy na seznam literatury (str. 359). C90 Pro jazyk C podle normy ISO 9899:1990 [2] používám zkratku C90. C99 Pro jazyk C podle normy ISO 9899:1999 [3], která nahradila standard [2], používám zkratku C99. C++03 Pro jazyk C++ podle standardu ISO 14882:2003 [1] používám zkratku C++03. C++0x Pro jazyk C++ podle připravovaného standardu ISO, jehož vydání se očekává koncem roku 2011, používám zkratku C++0x.
Poděkování Chci poděkovat všem, kteří mi při přípravě této knihy pomohli svými radami a připomínkami. Přes veškerou péči, kterou jsem této knize věnoval, se v ní mohou vyskytnout chyby. Pokud na nějakou přijdete, dejte mi prosím vědět; na webové stránce http://tjn.fjfi. cvut.cz/~virius/errata.htm uveřejním opravu. Miroslav Virius
[email protected]
18 Jazyky C a C++
1.
Úvod Je zvykem, že knihy o programovacím jazyce C nebo C++ začínají příkladem, který vypíše nějaký vtipný text a skončí. Pak následuje stručné vysvětlení jeho významu, informace o způsobu překladu apod. Autor si tím vytvoří rámec, který mu umožní předvádět příklady na spustitelných programech. I když v referenční příručce nic takového není nezbytné, přidržíme se této tradice, abychom usnadnili její používání i čtenářům, kteří tyto jazyky znají jen povrchně a potřebují je rychle začít používat.
1.1 První program V tomto oddílu si ukážeme první programy v jazyce C++ a v C, seznámíme se s pravidly pro vytváření zdrojového textu, s postupem překladu atd. Protože některé z těchto věcí mohou do značné míry záviset na použitém překladači, uvedeme pouze základní informace; podrobnosti musíte hledat v dokumentaci ke svému překladači. Zdrojový text našeho prvního programu v C++ je jednoduchý: /* Soubor PrvniPlus.CPP První program v C++ */ #include
// 4 using namespace std; // 5 int main() { // 6 cout << "Hello, World << endl"; // 7 return 0; // 8 } // 9 Podobný program v jazyce C může vypadat takto: /* Soubor Prvni.c První program v jazyce C */ #include <stdio.h> int main() { printf("Hello, World\n"); return 0; }
/* 6 */
Úvod 19
Tento program uložíme do textového souboru. Pro programy v jazyce C++ se typicky používá přípona .cpp , pro programy v jazyce C přípona .c. Lze se ale setkat i s jinými – např. v některých unixových systémech se pro programy v C++ používají přípony .C nebo .CC Podobně jako v jiných programovacích jazycích, i v C a v C++ platí, že tento soubor nesmí obsahovat formátování, tedy např. velikost a řez písma apod. Zpravidla jde o soubory v kódování ASCII (nebo v jiném jednobajtovém kódování, které váš počítač používá), dnešní překladače však už zpravidla akceptují zdrojové soubory v kódování Unicode. Jestliže tento program přeložíme a spustíme, vypíše ❚ H ello, World Než se ale pustíme do překladu a sestavování tohoto programu, vysvětlíme si (bez nároku na úplnost) význam jeho jednotlivých částí.
1.1.1 Co je co Pokud výslovně nezdůrazním něco jiného, týká se výklad jak C, tak C++. Veškeré pojmy z jazyků C a C++, které si zde naznačíme, budou znovu vysvětleny v referenční části této knihy. Začneme nepochybně nejnápadnější součástí zdrojového textu – komentářem.
Komentář První tři řádky v obou programech představují víceřádkový komentář. Ten začíná dvojicí znaků /* (zapsaných bezprostředně zas sebou) a končí dvojicí znaků */, opět zapsaných bezprostředně zas sebou. Překladač ignoruje tyto znaky a vše mezi nimi. Vedle toho máme v C++ a v C99 k dispozici jednořádkový komentář. Ten začíná dvojicí lomítek, //, a končí na konci řádku. Jednořádkové komentáře jsme v prvním výpisu použili k očíslování některých řádků.
Struktura programu a funkce main() Programy v jazycích C a C++ jsou tvořeny posloupností deklarací. To zní možná podivně, ale v principu to znamená, že se skládají z deklarací funkcí,1 typů a proměnných. Zjednodušeně řečeno, právě jedna z těchto funkcí musí mít jméno main a program začne prvním příkazem této funkce. Po skončení funkce main()2 program skončí. Podívejme se na první z výše uvedených výpisů. Deklarace funkce main() začíná v prvním výpisu na 6. řádku. Nejprve je uvedena specifikace typu výsledku (zde je to typ int, tedy celé číslo se znaménkem), pak následuje identifikátor funkce a za ním prázdné závorky, které říkají, že tato funkce nemá žádné parametry. Tyto závorky jsou nezbytné. Pak následuje otevírací složená závorka {, jíž začíná tělo funkce. Toto tělo končí uzavírací složenou závorku } na 9. řádku. Tělo funkce tvoří příkazy uzavřené mezi těmito závorkami.
V C a v C++ se nerozlišuje mezi funkcí a procedurou; jakýkoli podprogram se nazývá „funkce“. Jméno (identifikátor) této funkce je main. V textu knihy budeme k identifikátorům funkcí připisovat závorky, aby bylo na první pohled jasné, že se jedná o funkci. Bude-li to potřebné, uvedeme v závorkách i typy parametrů.
1 2
20 Jazyky C a C++
Řetězcová konstanta V 7. řádku souboru PrvniPlus.CPP najdeme zápis "Hello, World". To je řetězcová konstanta – posloupnost znaků uzavřená mezi uvozovky.
Výstup v C++ Výstup je jazycích C a C++ je realizován pomocí funkcí nebo objektů, které se nacházejí ve standardní knihovně. Tzv. standardní výstupní proud (typicky přesměrovatelný výstup na konzolu) je v C++ představován objektem cout. Vystupující hodnoty do tohoto proudu vkládáme pomocí operátoru <<. Zápisem ❚ c out << "Hello, World" << endl; do tohoto proudu vkládáme postupně dvě věci: zaprvé řetězcovou konstantu představující vypisovaný text a zadruhé tzv. manipulátor endl, který způsobí přechod na nový řádek.
Výstup v C Podívejme se nyní na druhý výpis, tj. na soubor Prvni.c. Jazyk C používá pro vstupní a výstupní operace knihovní funkce. Jednou z nich je funkce prinft(), která vypíše zadaný znakový řetězec do standardního výstupu. Tuto funkci lze volat i s více parametry.
Hlavičkové soubory Ani v C, ani v C++ nesmíme použít nic, co překladač nezná. To platí i pro součásti standardních knihoven – překladač je nezná, dokud mu o nich neřekneme. Proto je třeba překladači předem oznámit, že budeme používat objekt cout, resp. funkci printf(), které jsou definovány ve standardní knihovně. Abychom však nemuseli jejich deklarace vypisovat do svých programů ručně, jsou připraveny v tzv. hlavičkových souborech. Tyto hlavičkové soubory vkládáme do svých programů tzv. direktivou preprocesoru #include, tedy např. zápisem ❚ # include Tuto direktivu musíme zapsat na samostatný řádek. Jméno souboru uzavřeme mezi lomené závorky tvořené znaky „menší než“ a „větší než“. Poznamenejme, že standardní hlavičkové soubory v jazyce C++ nemají žádnou příponu. Hlavičkový soubor 3 obsahuje základní nástroje pro vstupní a výstupní operace. V jazyce C mají standardní hlavičkové soubory typicky příponu .h. Soubor <stdio.h> obsahuje nástroje jazyka C pro vstupní a výstupní operace. Poznamenejme, že standardní knihovnu jazyka C můžeme používat i v C++. To znamená, že uvedený program můžeme přeložit i překladačem C++. Překladače C++ mají vlastní verzi hlavičkových souborů z jazyka C. Jejich obsah je prakticky stejný jako v C90, jazyk C++ však umožňuje používat je dvojím způsobem:
Jméno tohoto souboru je iostream. Lomené závorky připojujeme, abychom zdůraznili, že jde o hlavičkový soubor. S touto praxí se běžně setkáváme nejen v odborné literatuře, ale i v textu standardu těchto jazyků.
3
Úvod 21
❚ S tejně jako v jazyce C. Můžeme tedy napsat #include <stdio.h> a pak můžeme používat funkci printf(). ❚ M ůžeme pro hlavičkový soubor použít jméno, které vznikne z původního jména v jazyce C připojením znaku c před první písmeno názvu a vynecháním přípony .h (tedy například místo <stdio.h>). V takovém případě budou všechny identifikátory, deklarované v tomto souboru, ležet ve jmenném prostoru std, takže budeme muset psát např. std::printf() místo pouhého printf() nebo použít deklaraci nebo direktivu using (viz dále).
Blok příkazů Tělo funkce (nejen funkce main()) představuje z hlediska syntaxe jazyků C a C++ blok. To je skupina příkazů ohraničená složenými závorkami { a }. Bloky příkazů obsahují příkazy, které patří k sobě, jako například příkazy tvořící tělo funkce (viz kapitola 10.1.1), tělo cyklu (viz kapitola 9.4) nebo větve příkazu if (viz kapitola 9.3.1). Z hlediska syntaxe se blok příkazů chová jako jeden příkaz.
Příkazy, zápis programu Každý příkaz v programu (kromě bloku) musí být ukončen středníkem. To znamená, že středník je součástí příkazu. Funkce main() v obou výše uvedených příkladech obsahuje dva příkazy. Oba musí být ukončeny středníkem (i poslední příkaz před uzavírací závorkou }). Příkaz může být zapsán i na několik řádků a naopak, na jednom řádku může být několik příkazů. Poznamenejme, že direktiva #include se nepovažuje za příkaz. Nepíšeme za ni středník a musí být na samostatném řádku.
Příkaz return Na 8. řádku funkce main()najdeme příkaz ❚ r eturn 0; Tento příkaz určuje místo, kde provádění funkce končí, a určuje její návratovou hodnotu (co bude výsledkem volání funkce). V případě funkce main() to je zpravidla kód ukončení programu, hodnota, kterou lze využít v některých prostředích k testům, zda program skončil úspěšně nebo chybou (a jakou). Příkaz return lze použít na kterémkoli místě v těle funkce.
Jmenné prostory (jen C++) Jmenné prostory (též prostory jmen) jsou jakási „příjmení“, které lze připojit k identifikátorům. Je to nástroj, který má pomoci vyhnout se konfliktům jmen v rozsáhlých projektech a při používání několika různých knihoven. Plné jméno funkce, typu, proměnné nebo šablony, deklarované ve jmenném prostoru, se skládá z identifikátoru, ke kterému je prostřednictvím operátoru :: připojeno jméno jmenného prostoru. Například všechny identifikátory ze standardní knihovny jazyka C++ leží ve jmenném prostoru std. To znamená, že plné jméno objektu představujícího standardní výstupní proud je std::cout, plné jméno manipulátoru pro přechod na nový řádek je std::endl atd. 22 Jazyky C a C++
Jestliže nehrozí konflikt jmen, můžeme překladači říci, že budeme jméno prostoru jmen vynechávat. K tomu slouží direktiva using, která má tvar using namespace jméno_jmenného_prostoru ; Pak můžeme psát jen cout, endl atd.
1.1.2 Překlad a sestavení Zdrojový program je třeba přeložit do strojového kódu a vytvořit spustitelný program. Přitom lze postupovat několika způsoby: ❚ P řekladač lze spustit z příkazového řádku. ❚ P řekladač lze spustit z integrovaného vývojového prostředí (IDE). ❚ P řekladač lze spustit pomocí utility make nebo jiného podobného nástroje (Ant, nmake apod.). Zde se podrobněji zastavíme pouze u první možnosti.
Překlad z příkazového řádku: Jeden zdrojový soubor Pro určitost vezmeme překladač g++, který je součástí vývojového nástroje MinGW32. Poznamenejme, že MinGW32 je zdarma a je šířen pod licencí GNU. Překlad našeho programu, který se skládá z jediného zdrojového souboru, obstará příkaz ❚ g ++ -o Prvni.exe PrvniPlus.cpp Výsledkem bude spustitelný soubor Prvni.exe. Po spuštění příkazem ❚ P rvni vypíše ❚ H ello, World Přepínač -o Prvni.exe určuje jméno výsledného spustitelného souboru. U některých překladačů ho lze vynechat a překladač použije jméno překládaného souboru. (V případě g++ by však měl výsledný soubor jméno a.exe.)
Určujeme jazyk zdrojového souboru Většina překladačů rozlišuje programovací jazyk podle přípony zdrojového souboru. Přípona .c typicky znamená jazyk C, přípona .cpp jazyk C++. Podrobnosti najdete v dokumentaci ke svému překladači. Překladač g++, který zde budeme převážně používat, se však chová jinak. Současná verze tohoto překladače zachází se všemi zdrojovými soubory implicitně jako s programem v jazyce C++03. Chceme-li, aby daný soubor překládal jako program v jazyce C90, musíme mu v příkazovém řádku zadat volbu –x c. To znamená, že musíme napsat např. ❚ g ++ –x c -o Prvni.exe Prvni.c Chceme-li, aby tento překladač akceptoval konstrukce, které nejsou v C90, ale jsou v C99, musíme to explicitně povolit dalším přepínačem -std=c99 v příkazovém řádku. Chceme-li, aby tento překladač – při překladu programu v C++ – akceptoval vybrané konstrukce zavedené v C++0x, musíme v příkazovém řádku zadat volbu -std=c++0x. Úvod 23
Program složený z několika souborů Jestliže se náš program skládá z několika zdrojových souborů, můžeme je v příkazovém řádku vypsat všechny. Například skládá-li se ze souborů Prvni.cpp a Pomocny.cpp, napíšeme ❚ g ++ -o Prvni.exe Prvni.cpp Pomocny.cpp
Oddělený překlad Jazyky C a C++ používají model odděleného překladu. To znamená, že různé zdrojové soubory téhož programu lze překládat samostatně a pak je sestavit. Chceme-li přeložit pouze soubor Prvni.cpp, napíšeme ❚ g ++ -c -o Prvni.obj Prvni.cpp Přepínač -c přikazuje překladači, že má zdrojový soubor přeložit, nikoli však sestavovat. Přepínač -o opět určuje jméno výstupního souboru. Tím vznikne tzv. relativní soubor (object file) Prvni.obj, který obsahuje překlad zdrojového souboru do strojového kódu. Neobsahuje však nic více, tzn., nejsou tam vyřešeny odkazy na funkce a proměnné definované v jiných souborech nebo v knihovně. Poznamenejme, že u většiny překladačů lze přepínač –o vynechat, překladač mu automaticky dá jméno shodné se jménem zdrojového souboru a pod Windows také příponu .obj. Překladač g++ mu dá příponu .o. Relativní soubory je třeba sestavit ve výsledný spustitelný soubor. K tomu slouží specializovaný program, označovaný jako sestavovací program nebo linker. U mnoha dnešních vývojových nástrojů (včetně MinGW) je však linker součástí překladače. Chceme-li sestavit výsledný program z relativních souborů Prvni.obj a Pomocny.obj, napíšeme ❚ g ++ -o Prvni.exe Prvni.obj Pomocny.ob
Vývojová prostředí a projekty Integrovaná vývojová prostředí (IDE) obvykle spojují editor, překladač, ladicí nástroje, nápovědu a další pomocné programy, které usnadňují vytváření aplikací. Zpravidla pracují s tzv. projektem. Na projekt se můžeme dívat jako na seznam souborů, z nichž se má vytvořit výsledná aplikace. V IDE lze pomocí dialogových oken nastavovat volby pro překlad nejen celého projektu, ale někdy i jednotlivých souborů. Podrobnosti o tom, jak definovat projekt a jak ho přeložit, je třeba hledat v dokumentaci k danému vývojovému prostředí.
Utilita MAKE Jiným nástrojem pro automatizaci překladu je utilita make. To je pomocný program volaný z příkazového řádku, který vyhledá soubor se jménem makefile obsahující návod pro vytvoření požadovaného spustitelného souboru. V nejjednodušší podobě se skládá z komentářů začínajících znakem # a končících na konci řádku, z popisu závislostí a z pravidel, podle kterých se mají požadované soubory vytvářet. Popis závislostí začíná vždy jménem souboru, za ním následuje dvojtečka a výčet souborů, na nichž daný soubor závisí. (To znamená, že když se změní některý z uvedených souborů, musí se znovu závislý soubor vytvořit. Například zápis 24 Jazyky C a C++
❚ p rvni.exe: prvni.obj pomocny.obj říká, že soubor prvni.exe závisí na souborech prvni.obj a pomocny.obj. Za tímto řádkem musí následovat pravidlo pro vytvoření souboru prvni.exe, tedy volání překladače: ❚ g ++ -o prvni.exe prvni.obj pomocny.obj Podobně je třeba definovat i závislosti souborů prvni.obj a pomocny.obj a pravidla pro jejich vytvoření. Celý soubor makefile pro překlad programu složeného ze zdrojových souborů prvni.cpp a pomocny.cpp může vypadat takto: # Soubor makefile # Popis vytvoření souboru prvni.exe # ze souborů prvni.cpp a pomocny.cpp # pomocí překladače g++ (MinGW32) prvni.exe: prvni.obj pomocny.obj g++ -o prvni.exe prvni.obj pomocny.obj prvni.obj: prvni.cpp g++ -c -o prvni.obj prvni.cpp pomocny.obj: pomocny.cpp g++ -c -o pomocny.obj pomocny.cpp Řádek se závislostmi začíná zpravidla na počátku řádku, řádek s pravidlem je obvykle odsazen o 1 zarážku tabulátoru. Překlad spustíme z příkazového řádku příkazem ❚ m ake Utilita make vždy nejprve vyhledá soubor jménem makefile a pak podle informací v něm kontroluje, zda se změnil některý ze souborů, na nichž jiné soubory závisí. Pokud ano, vytvoří znovu závislý soubor. To znamená, že překládá jen ty zdrojové soubory, které se od posledního překladu změnily. ❚ N ehodí-li se nám jméno makefile, lze použít jakékoli jméno s příponou .mak – např. prvni.mak. Utilitu make pak zavoláme příkazem make jméno_souboru, tedy např. make prvni.mak ❚ V souboru makefile lze definovat makra, implicitní pravidla apod. a tak ušetřit v případě rozsáhlých projektů mnoho psaní. To však přesahuje možnosti této knihy; podrobnosti lze najít např. na adrese [9].
1.2 Programovací jazyky C a C++ I když jsou si jazyky C a C++ velmi blízké, je třeba zdůraznit, že C++ není pouhé rozšíření jazyka C, neboť při návrhu C++ byla sice kompatibilita s jazykem C brána v potaz, nikoli však za každou cenu. V jazyce C existují konstrukce, které nejsou v C++ dovoleny; vedle toho narazíme i na konstrukce, které jsou sice správné v obou jazycích, ale v každém z nich mohou mít jiný význam. Je jich ovšem velmi málo a zpravidla jde o konstrukce, které nepatří do dobrého programátorského stylu. V textu na ně samozřejmě upozorním. Jazyk C navrhl a implementoval na počátku sedmdesátých let 20. století Denis Ritchie a použil ho při implementací jedné z verzí operačního systému Unix. Brzy se ukázalo, že jde Úvod 25
o velice příjemný a přitom i mocný programovací jazyk, a začal být používán k nejrůznějším účelům. S tím ovšem procházel i řadou změn – nejstarší verze tohoto jazyka např. místo operátorů +=, -= apod. používaly operátory =+, =- atd. V roce 1978 vydal D. Ritchie spolu s B. W. Kernighanem proslulou knihu The C Programming Language [6], která se stala na dlouhou dobu neoficiálním standardem jazyka C. V roce 1990 byl jazyk C poprvé standardizován na mezinárodní úrovni. Jedním z prvních kroků na cestě k jazyku C++ bylo tzv. „C s třídami“. V roce l985 vznikla verze, kterou již lze považovat za řádný objektově orientovaný programovací jazyk. Známe ji pod názvem C++. Jazyk C++ od té doby dozrál a je stejně úspěšný jako jazyk C. Jedním z důvodů úspěchu je jistě téměř úplná kompatibilita C++ s C, která programátorům pracujícím v Céčku usnadnila přechod k objektově orientovanému programování a zaručila použití již existujících kódů napsaných v jazyce C.
1.2.1 Standardy Prvním, neoficiálním standardem jazyka C se stalo první vydání už zmíněné knihy B. W. Kernighana a D. Ritchieho The C Programming Language [6].4 Zde popsaná verze jazyka bývá označována jako „jazyk C podle Kernighana a Ritchieho“, případně zkratkou K&R. Oficiálně byl jazyk C poprvé standardizován na národní úrovni v USA v r. 1989. Tento dokument ANSI měl číslo X3J11/90-013. O rok později, v r. 1990, byl přijat mezinárodní standard ISO/IEC 9899:1990 [2]. V této knize pro něj budu používat zkratku C90. Jazyk podle tohoto standardu je dnes běžně používán, implementují ho prakticky všechny běžně dostupné překladače. Poznamenejme, že zároveň s tím byl v USA stažen standard ANSI a byl nahrazen standardem ISO. Na tom nic nemění skutečnost, že mnozí výrobci překladačů se stále odvolávají na standard ANSI – to, co dodávají, je (nebo by měl být) překladač vyhovující mezinárodnímu standardu ISO. V roce 1995 byl přijat technický dodatek k tomuto standardu, který především rozšířil jazyk C o nástroje pro práci s tzv. širokými (tj. dvoubajtovými) znaky. V roce 1999 byla přijata druhá verze standardu jazyka C, označená ISO/IEC 9899:1999 [3], kterou budu označovat zkratkou C99. Přinesla řadu významných novinek – především do jazyka zahrnula nejběžnější rozšíření a odstranila některé potenciálně nebezpečné rysy. Jazyk C++ byl od svého vytvoření v první polovině osmdesátých let spravován B. Stroustrupem pod záštitou firmy AT&T. Program v C++ byl původně překládán nejprve do jazyka C a teprve z něj překladačem jazyka C do strojového kódu. Proto se první verze tohoto jazyka označovaly Cfront. Do počátku devadesátých let bylo uvolněno několik verzí, z nichž za nejvýznamnější bývají pokládány verze Cfront 1.0 (objektové rozšíření jazyka C), Cfront 1.2, která přinesla mj. přetěžování operátorů, dále verze Cfront 2, jež přinesla vícenásobné dědění; verze Cfront 2.1 připojila šablony. V roce 1991 vyšla kniha The Annotated C++Reference Manual [8], označovaná často zkratkou ARM, která se stala základem návrhu standardu jazyka. Popisovala vedle šablon výjimky, jmenné prostory a dynamickou identifikaci typů. V roce 1998 byl přijat mezinárodní standard jazyka C++ ISO/IEC 14882:1998. Tento standard doplnil, upřesnil a v některých detailech i změnil jazyk popisovaný v ARM a přidal standardní Poznamenejme, že druhé vydání této knihy z roku 1988 popisuje tehdy připravovaný standard ANSI.
4
26 Jazyky C a C++
šablonovou knihovnu. V roce 2003 byla publikována nová verze standardu [1], která odstranila některé drobné chyby a upřesnila některé drobnosti; nepřinesla však žádnou podstatnou změnu. Poznamenejme, že jazyk C++ podle ISO/IEC 14882:2003, který zde budu označovat zkratkou C++03, se opírá o jazyk C podle ISO 9899:1990, tedy o C90. Jazyk C++03 ovšem obsahoval několik problematických míst: ❚ P ředevším jeho standardní knihovna nebyla úplná, neboť v knihovně kontejnerů chybí mj. implementace hešových tabulek nebo uspořádané n-tice hodnot, nástroje pro práci s regulárními výrazy atd. ❚ N ěkteré syntaktické rysy, které byly do standardu C++03 zahrnuty, se neosvědčily, protože vedly k příliš provázanému kódu (to se týká např. specifikace výjimek v deklaraci funkce). ❚ N ěkteré syntaktické rysy znemožňovaly nebo ztěžovaly diagnostiku určitého druhu chyb. To se týká např. chyb možných při deklaraci virtuální metody v odvozené třídě. ❚ C hyběla možnost definovat omezení formálních typů v deklaraci šablony atd. Proto byl již delší dobu připravován nový standard, který by měl tyto problémy odstranit. Jeho vydání se očekávalo v roce 2008 nebo 2009, proto se pro něj vžilo označení C++0x, a toto označení se používá stále, i když je nyní jasné, že nový standard bude publikován nejspíš koncem roku 2011. V této knize budeme hovořit převážně o jazyku C++ podle standardu C++03 a C++0x. Přitom ale mějte na paměti, že v době, kdy tento text připravuji k vydání, ještě nebyl nový standard publikován, takže jsem čerpal informace z návrhu standardu publikovaného na počátku roku 2011; ty jsem pak korigoval podle konečného návrhu [4] publikovaného začátkem května 2011. V současné době implementují některé překladače vybrané novinky C++0x, žádný z běžných překladačů však neimplementuje všechny, a ani ty, které implementuje, neodpovídají vždy budoucímu standardu.
1.3 Objektově orientované programování Objektově orientované programování (dále též OOP) neznamená, že jednoduše využijeme pohodlí, které skýtá C++ oproti jazyku C, nebo že prostě budeme využívat knihovny tříd namísto knihoven napsaných v Céčku. V tomto smyslu nelze například označit náš první program v jazyce C++ za objektově orientovaný, přestože jsme v něm použili instance cout třídy ostream a přetížený operátor << místo funkce printf() z jazyka C. Objektově orientované programování vlastně začíná tím, že si definujeme třídy a že smysluplně využíváme zapouzdření a polymorfismu. Objektově orientované programování se snaží popsat problém, který program řeší, pomocí vhodně navržených tříd (objektových typů) a jejich instancí (proměnných, konstant atd.). Tím, že nabízí třídy, které lze navíc seskupit do hierarchií příbuzných typů, usnadňuje programátorovi přemýšlení o problému a usnadňuje nejen analýzu řešeného problému, ale i návrh a ladění programu. Snadno přitom vznikají knihovny tříd, které pak lze snadno a bezpečně opakovaně používat. Všechny podstatné principy objektově orientovaného programování směřují k urychlení procesu vypracování programu. Prostředkem vedoucím k tomuto cíli může být ❚ o pakované užití kódu, který již byl odladěn;
Úvod 27
❚ r ychlejší návrh datových typů s využitím skládání, dědění a polymorfismu; ❚ m enší náchylnost k výskytu chyb, a to nejen díky využití zapouzdření; ❚ l epší čitelnost programu, daná objektově orientovaným způsobem návrhu. Tam, kde jsou tato kritéria nepodstatná, není důvod neimplementovat program v jazyce C, což sice znamená, že se vzdáme objektově orientovaných principů, avšak někdy tím získáme menší a rychlejší program.
1.3.1 Základní pojmy OOP Chcete-li využít všech možností, které OOP v C++ nabízí, je důležité osvojit si napřed objektově orientovaný způsob myšlení a porozumět jeho nejdůležitějším principům, a teprve pak se snažit pochopit, jaký je jejich význam ve struktuře jazyka. Zejména pro nováčka není proniknutí do způsobu uvažování používaného v objektově orientovaném programování jednoduchou záležitostí, neboť to znamená zvládnout zcela novou terminologii. Bohužel, definice mnoha pojmů nejsou v literatuře sjednoceny. Další potíže pak má na svědomí používání anglické terminologie v češtině a nejednotnost překladů. Abych tomuto zmatení pojmů alespoň trochu zabránil, uvedu v následujících odstavcích nejdůležitější pojmy, krátce je vysvětlím a upozorním na synonyma a na odlišné definice. ❚ T řída (objektový typ): Znamená datový typ, který v programu reprezentuje třídu objektů z reálného světa (přesněji z oblasti řešeného problému), je její abstrakcí. (Do programové reprezentace přeneseme pouze ty vlastnosti, které jsou pro nás podstatné, ostatní vynecháme – „abstrahujeme od nich“. Proto se o třídách také hovoří jako o abstraktních datových typech, i když ve skutečnosti nejsou o nic abstraktnější, než řekněme typ int představující celá čísla.) ❚ I nstance (objekt): Znamená proměnnou, konstantu nebo parametr objektového typu. ❚ M etoda: Operace s instancí nějaké třídy nebo s třídou jako celkem. Vyjadřuje některou ze součástí chování třídy nebo jednotlivé instance. ❚ Č lenská funkce: Implementace metody v programu. (V C++ se ale pojmy metoda a členská funkce zpravidla pokládají za synonyma.) ❚ D atové složky: Data, uložená v jednotlivých instancích dané třídy, nebo ve třídě jako celku. Uchovávají stav instance. ❚ A bstraktní třída: Třída, v níž některé z metod nelze implementovat a jejíž instance nemá smysl vytvářet. Typicky jde o třídu, která shrnuje řadu konkrétních pojmů. Představme si např., že implementujeme třídy Bod, Úsečka a Kružnice. Při analýze zjistíme, že mají mnoho společného, a tyto společné vlastnosti shrneme do společného předka těchto tříd, do třídy GrafickýObjekt. V programu budeme vytvářet mnoho bodů, úseček i kružnic, ale žádný grafický objekt. Ten ani neumíme nakreslit, zatímco bod, úsečku i kružnice nakreslíme bez problémů. To znamená, že metodu nakresli() ve třídě GrafickýObjekt nemá smysl implementovat. (Musíme ji tam ale deklarovat, abychom mohli s body, úsečkami i kružnicemi zacházet jednotně, jako s grafickými objekty). GrafickýObjekt je tedy abstraktní třída. (V C++ se jako abstraktní označují třídy, které mají alespoň jednu čistě virtuální metodu.) ❚ A bstraktní metoda: Metoda v abstraktní třídě, kterou nemá smysl definovat, neboť je „příliš abstraktní“, kterou ale musíme deklarovat, neboť v odvozených třídách ji budeme
28 Jazyky C a C++
používat. Typickým příkladem je metoda nakresli() ve třídě GrafickýObjekt z předchozího bodu. ❚ Z apouzdření: Vlastně ukrývání implementace. S instancemi třídy i s třídou jako celkem zacházíme jen prostřednictvím jejich veřejně přístupných metod. Tyto metody tvoří uživatelské rozhraní třídy. Využíváme-li důsledně zapouzdření, pak nezměníme-li rozhraní třídy, nezpůsobí změna implementace třídy nutnost měnit implementaci zbytku programu. ❚ D ědění: Mechanizmus, který umožňuje od jedné třídy odvodit jinou, příbuznou třídu. Třídu, od které odvozujeme, označujeme jako předka, rodiče nebo bázovou třídu, odvozenou třídu označujeme také jako potomka nebo dceřinnou třídu. Potomek je vždy zvláštním případem předka. Odvozené třídy přebírají („dědí“) vlastnosti bázových tříd (obsahují stejné datové složky i metody.) K nim mohou připojit další, své vlastní datové složky a metody. Mohou také překrýt některé z metod předka. ❚ D ědická hierarchie: Skupina tříd, která vznikla odvozováním od společných předků. ❚ P olymorfizmus (mnohotvarost): Obecně znamená, že s instancemi různých tříd můžeme zacházet stejným způsobem. V C++ dosahujeme polymorfizmu pomocí dědění. Jedním ze základních principů OOP je, že potomek – instance odvozené třídy – může vždy zastoupit předka, tj. instanci bázové třídy. To lze říci i jinak: Objekt odvozené třídy je zároveň objektem bázové třídy. (Je-li bázová třída pes a odvozená třída jezevčík, platí, že jezevčík je pes.) Potomek je tedy podtypem předka. V C++ je polymorfizmus implementován pomocí tzv. virtuálních metod. ❚ S kládání (kompozice): Zdaleka nejčastější způsob odvozování nových tříd. Spočívá v tom, že nová třída využívá služeb tříd, které již existují – buď tak, že obsahuje datovou složku existující třídy, nebo že obsahuje ukazatel či referenci na instanci jiné třídy. ❚ T est je–má: Umožňuje určit, zda máme novou třídu navrhnout jako potomka existující třídy nebo zda máme použít skládání. Položíme si otázky: Je nová třída zvláštním případem třídy, kterou chceme použít jako předka? Nebo má (využívá) složku tohoto typu? Pouze je-li odpověď na první otázku kladná, má smysl uvažovat o dědění. Ovšem ani kladná odpověď na první otázku nezaručuje, že dědění je správnou alternativou. ❚ O bjektově orientovaný program: V teorii OOP se zpravidla říká, že objektově orientovaný program se skládá z objektů, které si navzájem posílají zprávy. V C++ to znamená, že objekty volají metody (své i jiných instancí). Řešení problémů v OOP se skládá z určení objektů, jejich implementace ve formě tříd a další práce s těmito třídami. Nejobtížnější přitom zpravidla je právě nalezení a implementace tříd, které musí správně reprezentovat třídy objektů z řešeného problému. Zbytek práce při vývoji programu se tím zjednoduší. Přitom programátor se již nemusí nadále starat o to, jak jsou třídy implementovány (může se na ně dívat jako na černé skříňky, které poskytují určité služby). Tak mohou vzniknout knihovny tříd, které se dají opakovaně použít. Objektově orientovaný přístup se tím ve srovnání s prací s elementárními datovými typy více blíží lidskému způsobu uvažování, které se rovněž vyznačuje tendencí ke klasifikaci (rozřazování pojmů do tříd). ❚ P řekrytí: Nová definice zděděné funkce v odvozené třídě. Smyslem je, aby odvozená třída reagovala na odpovídající volání funkce správným způsobem. Má-li být implementace korektní, musíme překryté funkce deklarovat jako virtuální. Překrytí je tedy principem, který umožňuje polymorfismus. (Někdy se hovoří také o předefinování.) ❚ P řístupová práva: Třída může označit některé své součásti jako veřejně přístupné a některé jako soukromé. Veřejně přístupné součásti může používat kdokoli (kterákoli část Úvod 29
programu); tyto složky tvoří rozhraní třídy. Soukromé složky smí používat jen třída sama (typicky její metody). Představují implementační detaily, které třída skrývá před uživatelem (programátorem, který třídu ve svém programu využívá). V některých programovacích jazycích jsou k dispozici i další úrovně řízení přístupu ke složkám mezi těmito dvěma extrémy. Např. v C++ mohou být některé složky označeny jako chráněné; ty smí používat pouze třída sama a třídy od ní odvozené (potomci). Chráněné složky představují rozšířené rozhraní třídy pro odvozování potomků.
1.3.2 Některé další pojmy ❚ P řetěžování funkcí: Definice několika funkcí se stejným identifikátorem. Překladač je bude při volání rozlišovat podle počtu a typu parametrů. Příležitostně se tento termín používá i v souvislosti s hierarchií tříd a polymorfismem, přestože v této souvislosti je přesnější a intuitivně srozumitelnější pojem překrytí nebo předefinování. ❚ P řetěžování operátorů: Rozšíření operátoru na uživatelem definované typy (objektové a výčtové). I zde překladač určuje podle počtu a typu parametrů, kterou operaci chceme provést. ❚ R ozhraní (interface): Též protokol – popis služeb, které funkce, modul nebo třída poskytuje, a způsobu, jak ji o tyto služby požádat. Rozhraní funkce je například určeno jejími parametry a vracenou hodnotou; součástí rozhraní mohou být i globální proměnné (i když to je z hlediska bezpečnosti velice nešťastné řešení). Rozhraní třídy pro celý zbytek programu představují především metody nebo datové složky označené jako veřejné (public) a funkce specifikované jako spřátelené (friend). Složky označené chráněné (protected) představují rozšířené rozhraní pro odvozování potomků. Rozhraní by na jedné straně mělo být co možná nejmenší, na druhé straně musí být úplné, tedy obsahovat vše, co je pro smysluplné využití třídy nezbytné. ❚ Š ablona: Nástroj, který umožňuje v C++ deklarovat celou množinu objektových typů nebo celou množinu funkcí, které se liší jen v některých typech nebo konstantách („parametrech šablony“). ❚ V ýjimka: Mechanizmus pro ošetřování chyb, které nastanou za běhu programu. Umožňuje přenést řízení z místa, kde byla chyba detekována, do místa, kde ji lze ošetřit (to může být i v jiné funkci). Pozor: Termínem výjimka označujeme jak samotnou chybu, tak i objekt, který nese o této chybě informace z místa vzniku do místa ošetření. ❚ A tribut: Ve starší literatuře se tento termín používal pro složky objektových typů (jak pro datové složky, tak pro metody). V 90. letech se používal (a dodnes občas používá) už pouze pro datové složky. V současné době se však prosazuje jako označení pro dodatečné informace o deklarovaném objektu, které zadáváme v deklaraci. V tomto smyslu se používá také v C++0x.
Termín objekt se také používá ve významu manipulovatelná oblast paměti (tedy vlastně proměnná jakéhokoli typu nebo funkce). V takovém případě nemá samozřejmě s OOP nic společného. I tento význam budu v této knize používat, vždy však na to výslovně upozorním.
30 Jazyky C a C++
2.
Základní pojmy Tato kapitola vysvětluje způsob popisu jazyků C a C++ používaný v této knize, poskytuje přehled klíčových slov, informace o lexikální konvenci, o způsobu překladu programu v C a v C++ a podobné.
Poznamenejme, že v této kapitole používáme termín objekt výhradně ve smyslu manipulovatelná oblast paměti.
2.1 Popis jazyků C a C++ Při popisu libovolného programovacího jazyka se rozlišují terminální a neterminální symboly. Terminální symbol je např. klíčové slovo, operátor apod. – něco, co lze do programu přímo opsat; neterminální symbol představuje pojem, který je třeba dále vysvětlit. Základní způsob popisu bude vypadat takto: ❚ V záhlaví popisu bude název vysvětlovaného neterminálního symbolu, následovaný dvojtečkou. ❚ P ak bude následovat popis vysvětlovaného neterminálního symbolu – vlastně jeho rozklad na posloupnost terminálních a neterminálních symbolů. ❚ T erminální symboly budeme zapisovat tučně, neterminální symboly kurzivou. ❚ P opis jednoho neterminálního symbolu může mít několik alternativ. Jednotlivé možnosti budou uvedeny odrážkou „•“. ❚ I ndex nep označuje „nepovinné“ součásti popisované konstrukce, tj. součásti, které lze vynechat. Význam popisované konstrukce se tím může, ale nemusí změnit. ❚ D vě bezprostředně za sebou následující lomítka uvádějí komentář, který končí na konci řádku. Například popis příkaz_while: • while ( podmínka ) příkaz znamená, že příkaz_while začíná terminálním symbolem (zde klíčovým slovem) while, za nímž následuje levá kulatá závorka, podmínka, pravá kulatá závorka a příkaz. Neterminální symboly podmínka a příkaz musí být vysvětleny jinde. V některých situacích bude výhodnější nahradit takovýto popis prostým výčtem (pak uvedeme v záhlaví popisu jeden z) nebo slovním opisem.
Základní pojmy 31
Poznamenejme, že nebude-li hrozit nedorozumění, budeme v neterminálních symbolech vynechávat spojovací podtžítko; např. v záhlaví syntaktického popisu budeme psát příkaz while místo příkaz_while.
2.2 Množina znaků Množinu znaků, které lze používat v programech v C++ se skládá ze čtyř základních skupin: znak: • písmeno • číslice • speciální_znak • bílý_znak Zde říkáme, že znak je písmeno nebo číslice nebo speciální_znak nebo bílý_znak. Tyto neterminální symboly je třeba dále definovat. písmeno: jedno z • univerzální_jméno_znaku • a b c d e f g h i j k l m n o p q r s t u v w x y z • A B C D E F G H I J K L M N O P Q R S T U V W X Y Z Pod pojmem písmeno se tedy skrývá buď univerzální jméno znaku, nebo malé nebo velké písmeno anglické abecedy. O univerzálních jménech znaků budeme hovořit dále (viz kapitola 2.2.1). číslice: jedna z • 0 123456789 speciální_znak: jeden z • _ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " ’ bílý_znak: jeden z • m ezera, horizontální_tabulátor, vertikální_tabulátor, konec_řádku, konec_stránky Definice číslice a speciálního znaku již obsahují výčty terminálních symbolů. To znamená, že uvedené znaky se mohou objevovat v programech a budou mít pro překladač nějaký význam. ❚ T zv. základní množina znaků je tvořena písmeny (kromě univerzálních jmen znaků), číslicemi, speciálními znaky a bílými znaky. ❚ B udeme-li hovořit o „velikosti“ písmen, budeme tím myslet, zda se jedná o velká či malá písmena. ❚ T ermín číslice zde znamená číslice desítkové soustavy. Budeme-li potřebovat číslice šestnáctkové (hexadecimální) soustavy, výslovně to zdůrazníme. (Jako číslice s významem 10–15 se používají písmena A–F, příp. a–f. Je jedno, zda použijeme velká nebo malá písmena.) ❚ P ojmenování, která budeme pro některé znaky používat, shrnuje tab. 2.1.
32 Jazyky C a C++
Tabulka 2.1: Jména některých znaků ( ) { } #
[ ]
okrouhlé (kulaté) závorky
_
složené závorky
^
uvozovka
%
apostrof
< >
obrácené lomítko
"
vlnovka (tilda)
'
svislice
\
lomítko
~
ampersand
|
stříška
/
podtržení (podtržítko)
&
mříž (kriminál)
hranaté závorky
procento
lomené závorky
2.2.1 Univerzální jména znaků Univerzální jména znaků poskytují způsob, jak zapsat znaky dostupné v kódování Unicode (ISO 10646). Standard [1] v dodatku E přesně vyjmenovává, o jaké znaky jde; zpravidla však naprosto stačí vědět, že jde o malá a velká písmena tak, jak je chápeme my, a to včetně písmen s háčky, čárkami, kroužky, vokáni, přehláskami, akcenty atd. v latince, cyrilici, řeckém písmu, hebrejském písmu, arménském písmu, arabském písmu, sanskrtu, písmu podle japonského průmyslového standardu a v řadě dalších. univerzální jméno znaku: • \ u hex_čtveřice • \ U hex_čtveřice hex_čtveřice V této definici hex_čtveřice znamená čtveřici hexadecimálních číslic. ❚ J e-li H libovolná hexadecimální číslice, znamená \uHHHH totéž co \U0000HHHH. ❚ V zápisu \UHHHHHHHH představuje HHHHHHHH kód znaku podle normy ISO 10646. Tabulka 2.2 ukazuje univerzální kódy znaků specifických pro češtinu a slovenštinu. Tabulka 2.2: Univerzální jména českých a slovenských znaků á ä č ď é ě í ľ ĺ ň
\u00E1 \u00E4
ó ô
\u010D
ŕ
\u00E9
š
\u010F \u011B \u00ED \u013E \u013A \u0148
ř ť ú ů ý ž
\u00F3 \u00F4 \u0155 \u0159 \u0161 \u0165 \u00FA \u016F \u00FD \u017E
Á Ä Č Ď É Ě Í Ľ Ĺ Ň
\u00C1 \u00C4 \u010C \u010E \u00C9 \u011A \u00CD \u013D \u0139 \u0147
Ó Ô Ŕ Ř Š Ť Ú Ů Ý Ž
\u00D3 \u00D4 \u0154 \u0158 \u0160 \u0164 \u00DA \u016E \u00DD \u017D
Standard jazyka C++ dovoluje používat v identifikátorech (jménech) nejen univerzální jména znaků, ale i odpovídající znaky, pokud je to v daném prostředí možné. Na druhé straně Základní pojmy 33
standard jazyka C++ nedovoluje pomocí univerzálních jmen znaků vyjadřovat malá a velká písmena anglické abecedy.
Digrafy a trigrafy Standardy jazyků C a C++ umožňují nahradit některé ze speciálních znaků kombinacemi jiných znaků. Tyto kombinace se nazývají digrafy a shrnuje je tabulka 2.3. Tabulka 2.3: Digrafy znak
náhrada
{
<%
[
<:
}
%>
]
:>
#
%:
##
%:%:
Až na způsob zápisu mají digrafy naprosto stejný význam jako znaky nebo znakové kombinace, které nahrazují. Z hlediska překladače představují stejné symboly (token). Alternativní možnost náhrady představují tzv. trigrafy, které uvádí tabulka 2.4. Tabulka 2.4: Trigrafy znak
náhrada
znak
náhrada
znak
náhrada
#
??=
[
??(
{
??<
??'
^
??!
|
??-
~
\
??/
]
??)
}
??>
Trigrafy zpracovává preprocesor (viz kapitola 16), takže překladač s nimi nepřijde do styku, zatímco s digrafy zachází překladač. ❚ N ěkteré implementace neumějí zpracovat trigrafy přímo; v tom případě poskytují zvláštní samostatný program, který je nutno zavolat před překladem. Tento program trigrafy odstraní a nahradí je znaky, které představují.
2.3 Identifikátor Identifikátor je jméno nějaké součásti programu. V C++ můžeme jako identifikátor použít libovolnou posloupnost písmen a číslic a začínající písmenem. Identifikátor může také obsahovat znak podtržení a může jím i začínat. Může obsahovat i univerzální jména znaků (nebo odpovídající znaky) a může jimi začínat. Nesmí obsahovat speciální znaky (např. +, – apod.) a nesmí obsahovat bílé znaky (mezery, tabulátory atd.). Implementace mohou množinu znaků, považovaných za písmena, ještě rozšířit. Mezi častá rozšíření patří např. přidání znaku $. ❚ V elká a malá písmena se v identifikátorech považují za různé znaky. Např. bouda a Bouda jsou různé identifikátory. 34 Jazyky C a C++
❚ D élka identifikátoru v C++ není omezena a všechny znaky v něm jsou významné (signifikantní), tj. překladač bere při rozlišování identifikátorů v úvahu všechny znaky. V identifikátorech se rozlišují malá a velká písmena. ❚ I dentifikátor se nesmí shodovat se žádným z klíčových slov (viz kapitola 2.4). To znamená, že identifikátor nesmí být tvořen stejnou posloupností písmen se stejnou velikostí jako některé z klíčových slov. ❚ I dentifikátory začínající dvěma podtržítky nebo jedním podtržítkem a velkým písmenem jsou vyhrazeny pro vnitřní potřeby překladače, takže bychom se jim měli vyhýbat. ❚ S tarší verze jazyka C omezovaly počet signifikantních znaků v identifikátorech. Např. standard ANSI C požadoval alespoň 31 signifikantních znaků. ❚ S tarší verze jazyků C a C++ nedovolovaly v identifikátorech používání znaků národních abeced.
PŘÍKLAD Slovo template není možné v C++ použít jako identifikátor, neboť se shoduje s jedním z klíčových slov tohoto jazyka. V jazyce C je to však správný identifikátor. Slova Template, TEMPLATE, tEmplatE jsou v obou jazycích správně utvořené a navzájem různé identifikátory, neboť se od klíčového slova template liší velikostí písmen.
2.3.1 Oblast platnosti, oblast viditelnosti Ke každému identifikátoru se váže oblast platnosti (část programu, v níž existuje objekt, který tento identifikátor označuje) a oblast viditelnosti (část programu, v níž je objekt označený identifikátorem dostupný). Tím se budeme podrobně zabývat později (viz kapitola 6.5).
2.4 Klíčová slova Klíčová slova jsou vlastně vyhrazené identifikátory, které označují datové typy, příkazy a jiné syntaktické konstrukce jazyků C a C++. Tabulka 2.5 obsahuje přehled klíčových slov jazyka C; symbol * označuje klíčová slova, která najdeme jen v C99. Tabulka 2.5: Klíčová slova jazyka C. Hvězdička označuje klíčová slova zavedená v C99. auto
double
inline *
sizeof
volatile
case
enum
long
struct
_Bool *
break char
const
continue default do
else
extern float for
goto if
int
register
restrict * return short
signed
static switch
typedef union
while
_Complex *
Imaginary *
unsigned void
Tabulka 2.6 obsahuje přehled klíčových slov jazyka C++; symbol * označuje klíčová slova, která najdeme jen v C++0x.
Základní pojmy 35
Tabulka 2.6: Klíčová slova jazyka C++. Hvězdička označuje klíčová slova zavedená v C++0x. alignas *
continue
friend
register
true
asm
default
if
return
typedef
alignof * auto
decltype *
goto
delete
bool
break case
catch char
char16_t * char32_t * class const
constexpr * const_cast
reinterpret_cast
inline
double
short
int
do
dynamic_cast
namespace
enum
new
explicit
noexcept *
export
nullptr *
extern
operator
false
private
float
protected
for
typename
sizeof
mutable
else
typeid
signed
long
public
try
union
static
static_assert * static_cast struct switch
unsigned using
virtual void
volatile
template
wchar_t
this
thread_local *
while
throw
Všechna klíčová slova jazyků C90 a C++ obsahují pouze malá písmena anglické abecedy. ❚ S výjimkou klíčových slov restrict, _Bool¸ _Complex a _Imaginary jsou všechna klíčová slova jazyka C součástí C++. ❚ Klíčová slova _Bool¸ _Complex a _Imaginary, s nimiž se setkáme pouze v C99, začínají jedním podtržítkem následovaným velkým písmenem. Implementace může doplnit další klíčová slova. Měla by začínat dvěma podrtžítky. Tabulka 2.7 ukazuje nejčastější rozšíření, s nimiž se setkáme v překladačích na PC. Tabulka 2.7: Nejčastější nestandardní klíčová slova v překladačích na PC __cdecl __except __finally __huge __syscall
__declspec __far __leave __pascal __try
__export __fastcall __near __stdcall __int64
Za vyhrazená slova se také pokládají následující náhrady některých operátorů, uvedené v tabulce 2.8. Tabulka 2.8: Standardní náhrady některých operátorů symbol
náhrada
&&
and
||
or
^ !
36 Jazyky C a C++
symbol |
náhrada
symbol
náhrada
bitor
!=
not_eq
xor_eq
&
bitand
xor
&=
and_eq
not
|=
or_eq
^=
~
compl
❚ V jazyce C jsou tyto náhrady k dispozici také, jsou však řešeny prostřednictvím maker #definovaných v hlavičkovém souboru . ❚ V C++ by tyto náhrady měly být automatické; mnoho překladačů je však zpřístupňuje – podobně jako jazyk C – pomocí hlavičkového souboru . ❚ V C++0x jsou také vyhrazena slova final a override (standard o nich hovoří jako o identifikátorech se zvláštním významem).
2.5 Lexikální konvence a zápis programu Překladač při analýze textu zdrojového programu v jazyce C rozlišuje několik druhů symbolů (token): identifikátory, klíčová slova, literály (přímo zapsané konstanty), operátory, oddělovače atd. V C++ jich rozlišuje více, to však pro nás není podstatné. Prvním úkolem překladače je rozdělit zdrojový text na tyto symboly. Přitom postupuje tak, že vyjde od konce posledního nalezeného symbolu a vždy vezme nejdelší řetězec následujících znaků, který může utvořit další symbol.
PŘÍKLAD Vezměme příkaz a---b;
Ač by se mohlo zdát, že jej lze interpretovat nejméně třemi způsoby, (a--) - b;
a- (--b);
a – (-(- b));
postup překladače bude jednoznačný: Za identifikátorem a najde tři znaky minus za sebou, a nejdelší symbol, který z nich lze sestavit, je operátor --. Proto rozloží uvedený výraz jako (a--) - b;
❚ J ednotlivé symboly se v programu oddělují zpravidla pomocí bílých znaků a komentářů. Jestliže za sebou následují znaky, které nemohou spolu tvořit jeden symbol, nemusí být odděleny bílým znakem (například ve výrazu 1+2 nemusí být mezi 1 a + žádný bílý znak). ❚ V šude, kde může být jedna mezera, můžeme vložit libovolný počet bílých znaků nebo komentářů. Vložením nadbytečných bílých znaků (mezer, přechodů na novou řádku apod.) a komentářů můžeme výrazně zpřehlednit zápis programu, aniž změníme jeho význam.
2.6 Průběh překladu Většinou stačí vědět, že zpracování zdrojového programu v C++ probíhá ve třech základních fázích. 1. Nejprve preprocesor odstraní komentáře, vloží do překládaného souboru soubory připojované direktivou #include a odstraní tyto direktivy ze zdrojového textu, provede ostatní direktivy preprocesoru a odstraní je ze zdrojového textu a rozvine makra. Trigrafy nahradí znaky, které zastupují. Po dokončení této fáze tedy zdrojový text překládaného souboru neobsahuje žádné trigrafy, direktivy ani komentáře. 2. Pak překladač rozloží zdrojový text na symboly (token), získanou posloupnost symbolů analyzuje, a pokud v něm nenarazí na žádné chyby, vytvoří relativní soubor. To je soubor Základní pojmy 37
obsahující strojový kód, v němž ovšem nejsou ještě vyřešeny všechny odkazy. (Můžeme si představovat, že každý relativní soubor obsahuje tabulku jmen exportovaných symbolů, tedy např. proměnných a funkcí, které mohou jiné relativní soubory používat, a tabulku nevyřešených odkazů, tj. např. jmen proměnných a funkcí, které se v tomto souboru používají, nejsou v něm ale definovány.) 3. V poslední fázi linker sestaví z jednotlivých relativních souborů spustitelný soubor. Přitom se pokusí odkazy, které nelze vyřešit pomocí jmen nalezených v relativních souborech našeho programu, vyřešit pomocí standardní knihovny. Jako vstupní bod programu určí funkci main(). Jestliže se některý z odkazů nepodaří vyřešit, skončí tato fáze chybovým hlášením a linker nemusí vůbec vytvořit spustitelný soubor. První dvě fáze mohou probíhat zároveň, třetí fáze musí být oddělena (i když ji může provádět stejný program). Musí zůstat zachována možnost odděleného překladu jednotlivých součástí programu.
2.6.1 Průběh překladu podrobně Ve skutečnosti standard rozlišuje devět fází překladu (osm v jazyce C, jedna z nich je specifická pro C++). Následující text je v podstatě převzat se standardu [1]. 1. Je-li to potřebné, převedou se znaky ze zdrojového souboru na znaky základní množiny znaků. (Znaky, vyjadřující přechod na nový řádek, se nahradí jistou standardní reprezentací konce řádku.) Způsob převodu závisí na implementaci. Trigrafy se nahradí odpovídajícími jednoznakovými reprezentacemi. Všechny znaky, které nejsou součástí základní znakové množiny, se nahradí odpovídajícími univerzálními jmény znaků. (Implementace může použít jakékoli vnitřní kódování, pokud je zaručeno, že se znakem z rozšířené znakové množiny a s jeho reprezentací pomocí univerzálního jména znaku se bude zacházet stejně.) 2. Ze zdrojového textu se odstraní všechny výskyty přechodu na nový řádek, před nimiž bezprostředně předchází znak \. Tím se některé řádky zdrojového textu spojí, takže vzniknou „logické řádky zdrojového textu“. Jestliže takto vznikne posloupnost znaků představující univerzální jméno znaku, není chování programu definováno. Jestliže neprázdný zdrojový soubor nekončí přechodem na nový řádek, nebo jestliže končí přechodem na nový řádek, kterému bezprostředně předchází znak \, není chování programu definováno. 3. Zdrojový soubor je rozložen na symboly preprocesoru a posloupnosti bílých znaků (včetně komentářů). Zdrojový soubor nesmí končit uvnitř symbolu preprocesoru nebo uvnitř komentáře. Přechody na nový řádek se v této fázi zachovají. Neprázdné posloupnosti bílých znaků jiných než přechodů na nový řádek mohou být nahrazeny jednou mezerou (zda k tomu dojde, závisí na implementaci). Rozklad zdrojového souboru na symboly preprocesoru závisí na kontextu; například znak < může podle okolností znamenat lomenou závorku, do níž se uzavírá jméno hlavičkového souboru v direktivě #include, nebo relační operátor „menší než“. 4. Provedou se direktivy preprocesoru a rozvinou se makra. Vznikne-li spojením symbolů (pomocí operátoru ##) posloupnost znaků představující univerzální jméno znaku, není chování programu definováno. Direktiva #include způsobí, že na hlavičkový nebo zdrojový soubor v ní uvedený jsou použity fáze 1–4. Takto vložený soubor může opět obsahovat direktivu #include; v tom případě se uvedený postup aplikuje rekurzivně. 38 Jazyky C a C++
5. Všechny znaky z množiny znaků zdrojového programu, použité ve znakových nebo řetězcových literálech, se převedou na znaky z množiny používané při běhu. 6. Symboly, představující řetězcové literály z „obyčejných“ znaků a oddělené nejvýše bílými znaky, se spojí v jeden řetězcový literál z „obyčejných“ znaků. Symboly, představující řetězcové literály z „širokých“ znaků a oddělené nejvýše bílými znaky, se spojí v jeden řetězcový literál z „širokých“ znaků. 7. Bílé znaky, oddělující symboly, ztratily provedením šesté fáze svůj význam. Symboly preprocesoru se převedou na symboly překladače. Výsledná posloupnost symbolů se syntakticky a sémanticky analyzuje a přeloží. Poznamenejme, že zdrojové soubory jednotky překladu a přeložené jednotky překladu nemusí být uloženy v souborech. Dokonce ani nemusí být vzájemně jednoznačná korespondence mezi těmito entitami a jakoukoli vnější reprezentací. Uvedený popis je pouze konceptuální a nespecifikuje žádnou určitou implementaci. 8. Jednotky překladu a instanční jednotky (ty vzniknou překladem šablon) se zkombinují následujícím způsobem: Z každé přeložené jednotky překladu se získá seznam požadovaných instancí šablon. Najdou se definice požadovaných šablon. Závisí na implementaci, zda se požaduje, aby byl k dispozici zdrojový soubor obsahující jednotky překladu s těmito definicemi. Vytvoří se všechny požadované instance a z nich vzniknou instanční jednotky. (Instanční jednotka je podobná jednotce překladu, neobsahuje ale žádné definice šablon a žádné odkazy na nevytvořené instance šablon.) Pokud se některou instanci nepodaří vytvořit, je program chybný. 9. Vyřeší se všechny odkazy na externí objekty a funkce. K vyřešení odkazů na funkce a objekty, které nejsou v překládaném programu definovány, se použijí knihovny. Všechen takto získaný výstup překladače se spojí do spustitelného souboru (program image), který obsahuje všechny informace potřebné k provedení programu v cílovém prostředí.
• Z popisu deváté fáze plyne, že knihovny slouží k vyřešení odkazů na objekty, které nejsou v programu definovány. To ale jinými slovy znamená, že si smíme napsat vlastní verzi kterékoli z knihovních funkcí nebo tříd. Protože bude součástí programu, bude mít přednost před stejnojmennou knihovní verzí. • V jazyce C odpadá 8. fáze. Ve skutečnosti ji nepoužívají ani mnohé dnešní překladače C++.
2.6.2 Pozorovatelné chování programu Standardy obou jazyků kladou řadu požadavků, které musí implementace C a C++ splňovat. Implementace (tedy překladače a odpovídající knihovny) mohou kterýkoli z těchto požadavků opominout, ovšem za předpokladu, že se přeložený program bude chovat tak, jako kdyby tyto požadavky byly splněny. To jinými slovy znamená, že všechny požadavky, které standard [1] (a podobně i standardy [2] a [3] a návrh [4]) klade, se týkají pouze pozorovatelného chování přeloženého programu, nikoli způsobu jeho implementace.
Základní pojmy 39
2.7 Definice a deklarace Z jistého hlediska dělají všechny programy totéž – pracují s daty. Tato data si ukládají na různých místech v operační paměti. Tato paměťová místa označujeme v programu zpravidla symbolickými jmény (identifikátory) a označujeme je jako proměnné. Proměnné jsou tedy paměťová místa, do kterých se ukládají data. Aby mohl program s těmito daty správným způsobem zacházet, musíme v deklaraci určit, jakého datového typu (nebo krátce jen typu) tato data jsou. Datové typy určují vnitřní reprezentaci proměnných v počítači, obor hodnot, kterých mohou nabývat, a operace, které mohou být s uloženými hodnotami prováděny. Všechny proměnné, funkce, definice typů atd., které programátor chce používat, musí být deklarovány a zpravidla též definovány. Oba pojmy – deklarace a definice – se často poněkud nedbale používají jako synonyma, přestože jejich význam je odlišný.
2.7.1 Deklarace ❚ D eklarace (nebo též informativní deklarace) seznamuje překladač s názvem (identifikátorem) proměnné, funkce, třídy, šablony nebo datového typu. ❚ D eklarací proměnné stanovíme její typ, paměťovou třídu (viz kapitola 6.2) a viditelnost (viz kapitola 6.5) jejího identifikátoru. Deklarací funkce stanovíme typ vracené hodnoty a počet, pořadí a typy parametrů a případně jejich implicitní hodnoty. ❚ D eklarace není spojena s vyhrazením paměti – deklarace nepřikazuje proměnnou nebo funkci vytvořit, pouze překladači oznamuje, že tato proměnná nebo funkce v programu existuje a je definována (vytvořena) někde jinde. ❚ I dentifikátor může být v rámci svého rozsahu platnosti (viz kapitola 6.5) deklarován vícekrát.
Deklarace v C++: ❚ D eklarace proměnné začíná klíčovým slovem extern (viz kapitola 6.2.5) a neobsahuje inicializaci. ❚ D eklarace funkce je její prototyp („hlavička bez těla ukončená středníkem“). ❚ P opis statické datové složky v deklaraci třídy je deklarace, nikoli definice (viz kapitola 11.2.2). ❚ D eklarace jsou také typedef (viz kapitola 5.1.5) a using (viz kapitoly 7. 3.2 a 11.6.2).
2.7.2 Definice ❚ D efinice (nebo též definiční deklarace) nejen popisuje objekt (proměnnou, funkci atd.), ale také přikazuje překladači, aby jej vytvořil. Po zadání definice překladač nejprve vyhradí potřebný prostor v paměti. ❚ P roměnné, funkce a jiné součásti programu dostupné v celém programu mohou mít několik deklarací, ale jen jednu definici. Statické proměnné a funkce mohou mít v každém z modulů (samostatně překládaných souborů) jinou definici. ❚ D efinice proměnné v C++ buď neobsahuje klíčové slovo extern nebo obsahuje inicializaci. ❚ D efinice funkce obsahuje její tělo. 40 Jazyky C a C++
❚ S tatické datové složky tříd musíme definovat někde za deklarací třídy (na úrovni souboru, tj. mimo tělo funkce). ❚ D eklarace globální proměnné v jazyce C, která neobsahuje ani klíčové slovo extern, ani inicializaci, bude chápána jako informativní deklarace, pokud za ní bude následovat definice. Jinak bude chápána jako definice. (Tato tzv. pracovní deklarace není v C++ dovolena.)
2.7.3 Pravidlo jediné definice Každá entita, kterou chceme v programu používat, musí být někde definována. Překladač však smí na definici narazit v každé jednotce překladu (samostatně překládané části programu) jen jednou. Sestavovací program musí pro každou z entit používaných v programu najít právě jednu definici. To je vyjádřeno pravidlem jediné definice (anglicky One Definition Rule, ODR). ❚ Ž ádná jednotka překladu nesmí obsahovat více než jednu definici proměnné, funkce, výčtového nebo objektového typu nebo šablony. ❚ Každý program musí obsahovat právě jednu definici funkce, která je v programu používána, pokud není deklarována s modifikátorem inline (viz kapitola 10.1.6). ❚ Každý program musí obsahovat právě jednu definici objektu (nikoli ve smyslu OOP), který je v programu používán. ❚ N ěkteré součásti programu mohou být za jistých okolností definovány implicitně; jde např. o konstruktor bez parametrů (viz kapitola 11.9.1), destruktor (viz kapitola 11.9.6) nebo přiřazovací operátor (viz kapitola 12.3.2). ❚ D efinice funkce nebo objektu musí být buď v programu, nebo v knihovně, kterou tento program využívá. Může jít o standardní nebo jinou knihovnu. ❚ F unkce deklarovaná s modifikátorem inline musí být definována v každé jednotce překladu, ve které se používá. ❚ D efinice třídy se vyžaduje v těch překladových jednotkách, ve kterých se uvedená třída používá způsobem, při němž překladač vyžaduje úplný typ. Objektový typ T musí být úplný, jestliže ■ definujeme objekt typu T; ■ má proběhnout konverze l-hodnoty typu T na r-hodnotu; ■ nějaký výraz má být implicitně nebo explicitně konvertován na typ T; ■ výraz typu ukazatel, který nepředstavuje konstantu s hodnotou 0 a je typu jiného než void*, je konvertován na ukazatel nebo referenci na T pomocí implicitní konverze, operátoru dynamic_cast nebo static_cast (viz kapitola 8.4.9); ■ na výraz typu T aplikujeme operátor pro přístup ke složkám; ■ na výraz typu T aplikujeme operátor sizeof nebo typeid; ■ definujeme nebo voláme funkci, která má parametr typu T nebo která vrací hodnotu typu T; ■ přiřazujeme l-hodnotu typu T; ■ aplikujeme na něj operátor alignof (jen v C++0x). ❚ V programu může být více než jedna definice výčtového nebo objektového typu, funkce s modifikátorem inline s externím sestavováním, šablony třídy, šablony nestatické funkce, statické datové složky šablony třídy, šablony metody nebo specializace šablony,
Základní pojmy 41
v níž jsou některé parametry šablony nespecifikovány, pokud je každá definice v jiné jednotce překladu a pokud tyto definice vyhovují následujícím požadavkům. ❚ J e-li nějaká entita D definována ve více než jedné jednotce překladu, pak tyto definice musí být ekvivalentní, tj. ■ všechny definice se musí skládat ze stejné posloupnosti symbolů; ■ jména, použitá v těchto definicích, musí odkazovat na stejné objekty (nikoli ve smyslu OOP); ■ všechny konstruktory, destruktory, přetížené operátory, implicitní volání konverzních funkcí, atd. musí odkazovat na stejné funkce; ■ implicitní hodnoty parametrů, použité v implicitním nebo explicitním volání funkce, se považují za součást definice, a proto podléhají výše zmíněným požadavkům; ■ jestliže D představuje třídu s implicitně deklarovaným konstruktorem, pak jde o konstruktor, který je deklarován v každé z jednotek překladu. Tento implicitně definovaný konstruktor musí ve všech jednotkách překladu volat stejný konstruktor bázové třídy.
PŘÍKLAD Následující příklad jsem převzal ze standardu [1]. // samostatně překládaný soubor č. 1 struct X { X(int); X(int, int); }; X::X(int = 0) { } class D: public X { }; D d2; // D() volá X(int) // samostatně překládaný soubor č. 2 struct X { X(int); X(int, int); }; X::X(int = 0, int = 0) { } class D: public X {}; // D() volá X(int, int) Implicitně definovaný konstruktor třídy D porušuje pravidlo jedné definice, neboť v každé jednotce překladu volá jiný konstruktor bázové třídy.
2.8 L-hodnota a r-hodnota Ve výkladu o jazycích C a C++ se často setkáváme s pojmy l-hodnota a r-hodnota (l-value, resp. r-value). Ve výkladu a jazyce C a v C++ až po verzi C++03 vystačíme s následující specifikací: ❚ L -hodnota je výraz, který určuje místo v paměti (může to být např. identifikátor proměnné nebo dereferencovaný ukazatel, může to ale být i funkce). Obsah tohoto místa nemusí
42 Jazyky C a C++
být možné měnit.5 Pokud potřebujeme místo, jehož obsah lze měnit, hovoříme o modifikovatelné l-hodnotě. ❚ R -hodnota je jakýkoli výraz, který není l-hodnota.
2.8.1 L-hodnota, r-hodnota a další (C++0X) Připravovaný standard C++0x rozlišuje výrazy podrobněji, jak ukazuje obrázek 2.1 tyto změny byly vynuceny zavedením referencí odkazujících na r-hodnoty.
Obrázek 2.1: Kategorie výrazů podle [4]
❚ L -hodnota je výraz určující objekt (nikoli ve smyslu OOP) nebo funkci. ❚ X -hodnota (x-value z anglického eXpiring value) také odkazuje na objekt, obvykle na konci životního cyklu; x-hodnota vzniká jako výsledek určitého druhu operací s referencemi na r-hodnoty (především jako výsledek volání funkce, jež vrací referenci na r-hodnotu nebo přetypování reference na r-hodnotu na objekt). ❚ G l-hodnota (gl-value, z anglického generalized l-value) je buď l-hodnota nebo x-hodnota. ❚ R -hodnota je x-hodnota, dočasný objekt nebo podobjekt x-hodnoty nebo dočasného objektu nebo hodnota, která není sdružena s nějakým objektem (například literál, kterému není přiděleno místo v paměti). ❚ P r-hodnota (z anglického „pure“ r-value, tedy nejspíše pravá r-hodnota) je r-hodnota, která není x-hodnotou. Vedle literálů to může být výsledek funkce, která nevrací referenci.
2.9 Zarovnání Některé datové typy mohou vyžadovat určité zarovnání (alignment). To znamená, že objekty tohoto typu musí ležet v paměti na adresách splňujících jisté požadavky. Tyto požadavky mohou být mimořádně důležité při spolupráci s kódem napsaným v jiných programovacích jazycích. Ani jazyk C, ani C++03 tuto problematiku nijak neřeší. C++0x umožňuje zjistit zarovnání pomocí operátoru alignof(). Umožňuje také v deklaraci proměnné nebo typu předepsat zarovnání pomocí atributu alignas(). Původně znamenala l-hodnota něco, co je možné zapsat na levou stranu přiřazovacího výrazu (l tedy byla zkratka anglického slova left, levý). Podobně r-hodnota je výraz, který může stát na pravé straně přiřazení – r je zkratkou anglického right, pravý.
5
Základní pojmy 43
Hodnoty zarovnání, které lze předepsat, závisejí na implementaci; musí však být k dispozici alespoň hodnoty vyžadované vestavěnými datovými typy.
2.10 Běh programu Základní představa, že běh programu začíná prvním příkazem funkce main() a končí návratem z této funkce, sice obvykle postačuje, ale v mnoha případech je třeba vědět více. ❚ K programu je typicky z knihovny připojen tzv. startovací a ukončovací kód. ❚ S tartovací kód „připraví půdu“, tj. mimo jiné definuje standardní vstupní a výstupní proudy a pak zavolá funkci main() a předá jí parametry. ❚ U končovací kód „uklidí po programu“, tj. uzavře neuzavřené soubory, postará se o volání destruktorů globálních instancí objektových typů (v C++), zavolá funkce registrované pomocí funkce atexit() definované ve standardním hlavičkovém souboru <stdlib. h>, resp. apod. Poznamenejme, že v programech pro některá speciální prostředí (např. ve vnořených systémech, tj. v programech, které řídí různá zařízení) nemusí být vůbec funkce main() definována.
2.10.1 Inicializace globálních proměnných V celém tomto odstavci znamená termín „objekt“ manipulovatelnou oblast paměti, nikoli nutně instanci objektového typu. Následující dva kroky se označují jako statická inicializace. ❚ P aměť objektů se statickou dobou života (tj. globálních proměnných, lokálních statických proměnných a statických datových složek tříd) je nejprve vynulována. Tato inicializace nulou proběhne před jakoukoli další inicializací. Některé objekty mohou být poté inicializovány znovu, nenulovými hodnotami. ❚ P ak proběhnou inicializace nenulovými konstantními hodnotami, pokud jsou známy v době překladu. Veškeré ostatní inicializace se označují jako dynamické. ❚ D ynamické inicializace objektů deklarovaných na úrovni jmenného prostoru se statickou dobou života mohou, ale nemusí proběhnout spolu se statickými inicializacemi. ❚ S tandard zaručuje, že dynamické inicializace objektů definovaných na úrovni jmenného prostoru proběhnou před prvním použitím jakékoli funkce nebo objektu z jednotky překladu, v níž je daný objekt definován. ❚ D ynamické inicializace objektů definovaných na úrovni jmenného prostoru mohou, ale nemusí proběhnout před provedením prvního příkazu funkce main(). Nelze tedy spoléhat na to, že všechny proměnné definované na úrovni prostoru jmen (a globální proměnné) budou již při startu funkce main() inicializovány. ❚ J estliže v inicializaci jednoho objektu (o1) definovaného na úrovni jmenného prostoru odkazujeme na hodnotu jiného objektu (o2) definovaného na úrovni jmenného prostoru v téže jednotce překladu, který je inicializován dynamicky, není určeno, zda bude použita hodnota o2 po plné inicializaci nebo po inicializaci nulou. 44 Jazyky C a C++
PŘÍKLAD: Podívejme se na následující fragment kódu, převzatý z [1]: inline extern double double
double fd() { return 1.0; } double d1; d2 = d1; // ! d1 = fd();
Hodnota proměnné d1 může být 0 nebo 1.
2.10.2 Ukončení programu Program může skončit následujícími způsoby: ❚ U končením funkce main() provedením příkazu return n; V takovém případě se zruší lokální proměnné definované ve funkci main(), v případě instancí objektových typů se zavolají jejich destruktory, a pak se zavolá funkce exit(n), definovaná ve standardní knihovně a deklarovaná v <stdlib.h>, resp. . ❚ P řechodem programu přes složenou závorku } ukončující tělo funkce main(). To je ekvivalentní provedení příkazu return 0; ❚ V oláním funkce exit() kdekoli v programu. V takovém případě se nezruší lokální proměnné v právě aktivních funkcích, proběhne však obvyklá ukončovací posloupnost (uzavřou se soubory, zavolají se destruktory instancí objektových typů deklarovaných na úrovni prostorů jmen, zavolají se funkce registrované pomocí atexit()). ❚ V oláním funkce abort(), deklarované v <stdlib.h>, resp. . Tato funkce okamžitě ukončí program, aniž proběhne ukončovací posloupnost operací. To znamená, že se neuzavřou soubory, nezavolají se destruktory instancí objektových typů deklarovaných na úrovni prostorů jmen, nezavolají se funkce registrované pomocí atexit(). ❚ V oláním funkce terminate() deklarované v <exception>; tato funkce implicitně zavolá funkci abort(). (Tuto možnost máme jen v C++.) ❚ V C99 a v C++0x je k dispozici funkce _Exit(). Tato funkce ukončí program podobně jako exit(), nebudou však volány funkce registrované pomocí atexit(). ❚ V C++ 0x a v nejnovějších implementacích jazyka C je k dispozici také funkce quick_ exit(). Tato funkce ukončí program bez úklidu prostředků. Přitom se zavolají funkce registrované pomocí funkce at_quick_exit().
Základní pojmy 45
3.
Základní datové typy Základní (vestavěné) typy jsou datové typy, jejichž definice je součástí standardu jazyka. Jména typů jsou vyjádřena klíčovými slovy nebo jejich kombinacemi. Jde o typy vyjadřující celá a reálná čísla, znaky a logické hodnoty; v C99 k nim přibyly i typy vyjadřující komplexní čísla.
❚ D atové typy vyjadřující celá čísla, znaky a logické hodnoty shrnujeme pod označení celočíselné typy. ❚ D atové typy pro práci s reálnými čísly (čísly s pohyblivou řádovou čárkou) shrnujeme pod označení reálné typy.
3.1 Celá čísla Tabulka 3.1 shrnuje přehled celočíselných typů používaných v jazycích C a C++ a jejich rozsahy v překladačích pro 32bitové prostředí na PC. Vedle typů zde uvedených lze k celočíselným výpočtům použít i znakové typy, o nichž budeme hovořit v následujícím oddílu. Jazyky C a C++ obsahují dvě skupiny celočíselných typů – se znaménkem a bez něj. První čtyři typy v tab. 3.1 jsou typy se znaménkem, zbývající 4 jsou bez znaménka. Tabulka 3.1: Datové typy pro práci s celými čísly typ
rozsah hodnot na PC (32 b)
short
–32768 .. 32767
int
long
long long
unsigned short unsigned
unsigned long
unsigned long long
–2147483648 .. 2147483647 – 2147483648 .. 2147483647 –9223372036854775808 .. 9223372036854775807 0 .. 65 535 0 .. 4294967295 0 .. 4294967295 0 .. 18446744073709551615
❚ V implementacích pro 16bitové prostředí má zpravidla typ int stejný rozsah jako typ short a typ unsigned má stejný rozsah jako typ unsigned short.
Základní datové typy 47
❚ T yp short zabírá v paměti vždy stejný počet bajtů jako typ unsigned short. Podobně typ int zabírá v paměti vždy stejný počet bajtů jako typ unsigned, typ long zabírá v paměti vždy stejný počet bajtů jako typ unsigned long a typ long long zabírá v paměti vždy stejný počet bajtů jako typ unsigned long long. ❚ T ypy long long a unsigned long long zavedl až standard C99 a C++0x. V C90 ani v C++03 nejsou. Mnohé starší implementace C++ a C90 je však obsahují jako rozšíření (někdy pod označením __int64, resp. unsigned __int64). ❚ S tandardy jazyků C a C++ nepředepisují rozsahy celočíselných typů, takže ty mohou být v různých implementacích různé. Standard jazyka C99 však předepisuje minimální dovolený rozsah těchto typů. ❚ R ozsah typu int nesmí být menší než rozsah typu short, rozsah typu long nesmí být menší než rozsah typu int a rozsah typu long long nesmí být menší než rozsah typu long. Podobně rozsah typu unsigned nesmí být menší než rozsah typu unsigned short, rozsah typu unsigned long nesmí být menší než rozsah typu unsigned a rozsah typu unsigned long long nesmí být menší než rozsah typu unsigned long. ❚ T ypy se znaménkem se od typů bez znaménka liší chováním při přetečení, tedy když se výsledek aritmetické operace nevejde do rozsahu daného typu. V případě typů bez znaménka probíhají výpočty vždy modulo 2n, kde n je počet bitů používaných k reprezentaci daného typu; v případě typů se znaménkem nemusí být výsledek definován. To se týká i přiřazení. Uvedená pojmenování celočíselných typů jsou nejkratší možná. Tabulka 3.2 uvádí synonyma, která lze pro označení těchto typů použít. Pravidla pro jejich pojmenování lze vyjádřit takto: ❚ S oučástí názvu každého z těchto typů může být klíčové slovo int. ❚ S oučástí názvů typů se znaménkem může být klíčové slovo signed. Kromě – snad – zvýšení přehlednosti však zde modifikátor signed žádný význam nemá. Tabulka 3.2: Synonyma pro pojmenování celočíselných typů typ
Synonyma
short
signed short, short int, signed short int
int
signed int
unsigned short unsigned long
unsigned long long long
unsigned long long
unsigned short int unsigned int
signed long, long int, signed long int unsigned long int
signed long long, long long int, signed long long int unsigned long long int
❚ M inimální a maximální hodnoty, kterých mohou jednotlivé celočíselné typy nabývat, najdeme v jazyce C v hlavičkovém souboru ; jde o makra INT_MAX, INT_ MIN, UINT_MAX atd. 48 Jazyky C a C++
❚ V C++ můžeme makra ze souboru použít také, můžeme ale použít i specializaci šablonové třídy std::numeric_limits<> pro daný typ. Práce s ní je jednodušší, neboť prostřednictvím statických metod max(), min() apod. poskytuje jednotný přístup ke všem typům.
PŘÍKLAD Výsledkem následujících příkazů #include unsigned hodnota; hodnota = UINT_MAX+2; cout << hodnota;
// Na PC v 32bitovém prostředí // Nejvyšší hodnota + 2 // Vypíše 1
bude výstup čísla 1. Konkrétně ve 32bitovém prostředí na PC proběhne výpočet modulo 232, tj. modulo 4 294 967 296.
3.1.1 Celočíselné literály Celočíselné konstanty zapisujeme v programu podobně jako v matematice, nesmíme však oddělovat mezerami (ani jiným způsobem) trojciferné skupiny. To znamená, že musíme psát 12345, nikoli 12 345. Vedle desítkové soustavy můžeme k zápisu celočíselné konstanty použít také osmičkovou nebo šestnáctkovou soustavu. Typ konstanty můžeme ovlivnit příponami U, L a LL a jejich kombinacemi (lze použít i malá písmena). ❚ Konstanta v osmičkové soustavě musí začínat 0 (znakem nula) a smí obsahovat jen číslice 0–7. ❚ Konstanta v šestnáctkové soustavě musí začínat 0x nebo 0X (nula a malé nebo velké X) a smí obsahovat číslice 0 – 9, a, b, c, d, e, f. (Můžeme použít i velká písmena A, B, C, D, E, F, na velikosti písmen nezáleží.) Při rozlišování přetížených funkcí je důležité znát typ celočíselných konstant. Tento typ závisí na číselné soustavě, v níž konstantu zapíšeme, na její velikosti a na příponě nebo příponách. V následujícím výčtu vždy platí, že konstanta je prvního typu v uvedeném seznamu, do jehož rozsahu se vejde. ❚ D esítková konstanta bez přípon je typu int, long nebo long long – prvního, do jehož rozsahu se její hodnota vejde. (Jestliže daná implementace neobsahuje typ long long, berou se v úvahu jen první dva.) ❚ O smičková nebo šestnáctková konstanta bez přípony je typu int, unsigned, long, unsigned long, long long nebo unsigned long long – opět prvního, do jehož rozsahu se její hodnota vejde. (Opět pokud daná implementace neobsahuje typ long long, berou se v úvahu jen první čtyři typy.) ❚ Konstanta (zapsaná ve kterékoli přípustné číselné soustavě) s příponou U nebo u bude typu unsigned, unsigned long nebo unsigned long long. ❚ D esítková konstanta s příponou L (resp. l) typu long nebo long long, šestnáctková nebo osmičková konstanta s touto příponou bude typu long, unsigned, long long nebo unsigned long long.
Základní datové typy 49
❚ Konstanta s příponou UL, LU, ul nebo lu (zapsaná ve kterékoli přípustné číselné soustavě) bude typu unsigned long nebo unsigned long long. ❚ D esítková konstanta s příponou LL nebo ll bude typu long long, šestnáctková nebo osmičková konstanta s touto příponou bude typu long long nebo unsigned long long. ❚ Konstanta s příponou ULL, LLU, ull nebo llu (zapsaná ve kterékoli přípustné číselné soustavě) bude typu unsigned long long.
PŘÍKLAD Desítkovou konstantu 27 zapíšeme v osmičkové soustavě 033 a v šestnáctkové soustavě 0x1b. Konstanta 0 je typu int, 0L je typu long a 0U je typu unsigned. Konstanta 5000000000U je (ve 32bitovém prostředí na PC) typu unsigned long, neboť obsahuje příponu U a nevejde se do rozsahu typu unsigned.
3.1.2 Další celočíselné typy v C99 a v C++0x Jazyky C++ a C90 předpokládají, že implementace poskytují pouze typy uvedené v předchozí tabulce. Standardy C99 a C++0x však počítají s tím, že implementace mohou nabídnout i další celočíselné typy. Vedle toho prostřednictvím hlavičkového souboru <stdint. h> poskytují alternativní pohled na existující typy. Standard C99 stanoví i způsob reprezentace celých čísel v počítači.
Rozšířené typy Implementace mohou poskytnout i další celočíselné typy, neuvedené v předchozí tabulce. Standard o nich hovoří jako o rozšířených typech (extended types) a upravuje jejich postavení z hlediska konverzí apod. (viz kapitola 8.3.5).
Alternativní pohled na celočíselné typy Název datového typu nezaručuje jeho vlastnosti (rozsah, rychlost při výpočtu, zda může obsahovat ukazatel apod.) Proto nabízí C99 a C++0x alternativní přístup k celočíselným typům prostřednictvím deklarací typedef v hlavičkovém souboru <stdint.h>. V následujících popisech znamená N desítkové celé číslo bez znaménka. Některé z těchto typů jsou povinné, jiné volitelné – implementace je nemusí obsahovat, a přesto bude vyhovovat standardu C99 nebo C++0x. ❚ T ypy s přesně určenou šířkou. Jména tvaru intN_t označují celočíselné typy se znaménkem, reprezentované ve dvojkovém doplňku, o velikosti přesně N bitů. Podobně uintN_t představuje celočíselný bezznaménkový typ o velikosti přesně N bitů. Například int8_t představuje osmibitové celé číslo se znaménkem, unt16_t představuje šestnáctibitové celé číslo bez znaménka. Tyto typy jsou nepovinné. Pokud ovšem implementace poskytuje celočíselné datové typy o velikosti 8, 16, 32 nebo 64 bitů, je povinna poskytnout odpovídající deklarace typedef. ❚ T ypy, které mají alespoň udanou šířku. Jména tvaru int_leastN_t, resp. uint_ leastN_t označují celočíselný typ se znaménkem, resp. bez znaménka takový, že má
50 Jazyky C a C++
alespoň N bitů a žádný typ s menším rozsahem již tuto podmínku nesplňuje. Například int_least32_t představuje celočíselný typ o šířce alespoň 32 bitů. ❚ I mplementace je povinna poskytnout typy int_least8_t, int_least16_t, int_least32_t, int_least64_t, uint_least8_t, uint_least16_t, uint_least32_t a uint_least64_t. Může ovšem poskytnout i další. ❚ N ejrychlejší typy s minimální šířkou. Typy int_fastN_t, resp. uint_fastN_t označují nejrychlejší celočíselný typ se znaménkem, resp. bez znaménka se šířkou alespoň N bitů. Implementace je povinna poskytnout typy int_fast8_t, int_fast16_t, int_fast32_t, int_fast64_t, uint_fast8_t, uint_fast16_t, uint_ fast32_t a uint_fast64_t. Může ovšem poskytnout i další. ❚ C eločíselné typy, do nichž lze uložit ukazatel. Do celočíselné proměnné typu intptr_t, resp. uintptr_t lze uložit ukazatel typu void* a pak ho konvertovat zpět na ukazatel, aniž se změní jeho hodnota. Tyto typy jsou nepovinné. ❚ N ejvětší celočíselné typy. Jména intmax_t, resp. uintmax_t označují celočíselný typ se znaménkem, resp. bez něj, s největším rozsahem, který je v dané implementaci k dispozici. Tyto typu jsou povinné. Při vstupu, resp. výstupu hodnot těchto typů pomocí funkcí z rodiny printf() a scanf() se používají nástroje definované v hlavičkovém souboru .
Zobrazení celých čísel v paměti Norma C99 stanoví, že k zobrazení celých čísel v paměti počítače lze použít rozšířený přímý kód (sign and magnitude), jednotkový doplněk (one’s complement) nebo dvojkový doplněk (two’s complement). Poznamenejme, že PC a velmi mnoho dalších počítačů používá dvojkový doplněk.
3.2 Znakové typy Znakové typy jsou celočíselné typy a lze je používat i k výpočtům, podobně jako datové typy z předchozího oddílu. Především však slouží k reprezentaci znaků při komunikaci počítače s uživatelem. Jazyk C++03 zná 4 znakové typy, jejichž přehled najdete v následující tabulce 3.3. Tytéž typy jsou k dispozici i v jazyce C, i když jejich význam je poněkud odlišný. Jazyk C++0x přidal další dva znakové typy pro práci se znaky v kódování Unicode; v tabulce 3.3 jsou uvedeny jako poslední. Tabulka 3.3: Znakové typy Typ
rozsah
char
–128 .. 127 nebo 0 – 255
signed char
–128 .. 127
wchar_t
Typicky jako unsigned nebo unsigned long
unsigned char char16_t char32_t
0 .. 255 Stejný jako uint_least16_t
Stejný jako uint_least32_t
Základní datové typy 51
❚ N orma nepředepisuje, zda je char typ se znaménkem nebo bez znaménka – to závisí na implementaci. ❚ J azyk C++, stejně jako C99, pokládá char za typ různý od unsigned char a signed char, i když je implementován jako jeden ze zbývajících dvou. (To je důležité při rozlišování přetížených funkcí.) V C90 je char synonymem pro unsigned char nebo signed char (záleží a implementaci, případně na volbách překladače). ❚ Klíčové slovo signed v označení typu signed char nelze – na rozdíl od ostatních celočíselných typů – vynechat. ❚ T ypy char, unsigned char a signed char zabírají v paměti 1 bajt a používají se především k vyjadřování evropských znaků. ❚ T ypy char, unsigned char nebo signed char lze používat i k výpočtům s malými celými čísly. Znakové hodnoty lze bez omezení přiřazovat číselným proměnným a naopak. ❚ D atový typ wchar_t je v jazyce C definován pomocí typedef v hlavičkovém souboru <stddef.h> jako synonymum pro některý z celočíselných typů, v C++ je to základní typ. Používá se pro „široké znaky“ (wide character), tj. znaky, které nelze zakódovat v 8 bitech. Zpravidla jde o znaky v kódování Unicode. Zabírá 2 nebo 4 bajty. ❚ N epleťte si široké znaky s řetězci. Široký znak představuje vždy jen jeden znak, i když je uložen v několika bajtech, zatímco řetězec je posloupnost znaků. ❚ J azyky C a C++ umožňují pracovat i s tzv. vícebajtovými znaky (multibyte character). To jsou znaky, které jsou podle okolností kódovány jedním nebo několika bajty. Pro ně není žádný zvláštní datový typ, pracuje s nimi skupina knihovních funkcí, které najdeme v hlavičkových souborech <stdlib.h> a <wchar.h> (v C++ můžeme použít i názvy a ). Poznamenejme, že vícebajtové znaky oficiálně zavedl až dodatek ke standardu C90 z r. 1995. ❚ T ypy char16_t a char32_t, které najdeme jen v C++0x, slouží k práci se znaky v kódování Unicode. První z nich má stejnou velikost jako jeho podkladový celočíselný typ uint_least16_t, tedy nejméně 16 bitů, a vejdou se do něj všechny znaky „základní roviny“ kódování Unicode. Druhý s nich má stejnou velikost jako jeho podkladový celočíselný typ uint_least32_t, tedy nejméně 32 bitů, a vejdou se do něj všechny znaky kódování Unicode.
3.2.1 Znakové literály Konstanty, které představují velkou většinu běžných znaků, zapisujeme jako znak mezi dvěma apostrofy, např. 'a', 'Ř' atd. Před takto zapsanou konstantou může stát prefix L, u nebo U (poslední dva jen v C++0x). Tak můžeme zapisovat i znaky národních abeced. Řídicí znaky, uvozovky, apostrof a některé další znaky zapisujeme pomocí tzv. řídicích posloupností, jejichž přehled uvádí tabulka 3.4. Tabulka 3.4: Řídicí posloupnosti vyjadřující znaky v C a v C++ název
označení v ASCII
zápis v C/C++
nový řádek
NL (LF)
\n
horizontální tabulátor
HT
52 Jazyky C a C++
\t
název
označení v ASCII
zápis v C/C++
vertikální tabulátor
VT
\v
krok zpět
BS
počátek řádky
CR
konec stránky
FF
upozornění
BEL
obrácené lomítko
\
otazník
?
apostrof
'
uvozovka
"
znak s osmičkovým ASCII kódem ooo
ooo
znak s šestnáctkovým ASCII kódem xxx
xxx
\b \r \f \a \\ \? \' \"
\ooo
\xhhh...
❚ P ro široké znaky lze také použít tzv. univerzální jména znaků. To jsou zápisy tvaru \ uxxxx nebo \ Uxxxxxxxx, kde malým u následují přesně 4 šestnáctkové číslice, za velkým U následuje přesně osm šestnáctkových číslic, jež vyjadřují kód znaku v kódování Unicode. ❚ Z ápis \uxxxx znamená totéž co \U0000xxxx. ❚ Z nakové konstanty bez prefixu jsou v C++ typu char. V jazyce C jsou typu int. ❚ Z nakové konstanty typu wchar_t zapisujeme podobně jako jednobajtové znakové konstanty, navíc připojíme prefix L – např. L'a'. ❚ Z nakové konstanty s prefixem u, resp. U jsou typu char16_t, resp. char32_t.
3.3 Logické hodnoty Jazyk C90 neobsahuje zvláštní typ pro práci s logickými hodnotami. Jako logické hodnoty v něm lze použít hodnotu libovolného číselného typu nebo typu ukazatel. Přitom platí, že nenulové hodnoty čísel nebo ukazatelů znamenají logickou hodnotu „pravda“, nulové hodnoty znamenají „nepravda“. Stejným způsobem lze s logickými hodnotami pracovat i v C99 a v C++. Oba tyto jazyky však obsahují i zvláštní typ pro vyjadřování logických hodnot (bool, resp. _Bool).
3.3.1 Typ bool (C++) Typ bool je k disposici jen v C++ a je to celočíselný typ bez znaménka. Proměnným tohoto typu mohou být přiřazeny hodnoty vyjádřené klíčovými slovy false (nepravda) a true (pravda). Proměnným typu bool můžeme také přiřadit číselné hodnoty nebo hodnoty ukazatelů. Pak je nula konvertována na false a jakákoli hodnota jiná než nula na true. Přiřadíme-li hodnotu typu bool číselné proměnné, bude true konvertováno na 1 a false na 0.
Základní datové typy 53
3.3.2 Typ _Bool (C99) ❚ T yp _Bool je k dispozici v C99 a je to opět celočíselný typ bez znaménka. Jeho možné hodnoty jsou 0 a 1, přičemž 0 znamená logickou hodnotu „nepravda“ a 1 znamená logickou hodnotu „pravda“. Pro konverze platí podobná pravidla jako pro typ bool v jazyce C++. ❚ H lavičkový soubor <stdbool.h> obsahuje makro bool, které se rozvine v klíčové slovo _Bool. Dále obsahuje makra true, resp. false, jež představují hodnoty 1, resp. 0. Vložení tohoto hlavičkového souboru lze testovat pomocí makra __bool_true_ false_are_defined.
3.4 Operace s celými čísly Vlastnostmi jednotlivých operátorů se budeme podrobně zabývat později (viz kapitola 8.4). Zde pouze shrneme, jaké operace jsou s celými čísly dovoleny a jaké jsou jejich výsledky. V tomto oddílu se nezabýváme konverzemi celočíselných typů, neboť o nich budeme hovořit v kapitole 8.3.
3.4.1 Přiřazování Modifikovatelné l-hodnotě jakéhokoli celočíselného typu lze přiřadit r-hodnotu kteréhokoli číselného nebo výčtového typu. (O výčtových typech budeme hovořit v kapitole 4.1.) Přitom se r-hodnota nejprve konvertuje na typ l-hodnoty a výsledek této konverze se přenese na určené místo.
3.4.2 Aritmetické operace Na hodnotu kteréhokoli z číselných typů lze aplikovat unární aritmetické operátory + a – a binární aritmetické operátory +, –, *, / a %. Na l-hodnotu kteréhokoli z těchto typů lze použít též operátory inkrementace a dekrementace ++ a – –.
Unární aritmetické operace Před použitím unární aritmetické operace proběhnou s operandem celočíselná rozšíření (viz kapitola 8.3.1). Výsledek je stejného typu jako operand po konverzi. ❚ V ýsledkem použití unárního operátoru + je původní hodnota po implicitních celočíselných konverzích. ❚ V ýsledkem použití unárního operátoru minus na typ se znaménkem je původní hodnota (opět po implicitních celočíselných konverzích) s opačným znaménkem – tedy číslo opačné. ❚ V ýsledkem použití unárního operátoru minus na hodnotu u typu bez znaménka je hodnota 2n – u.
PŘÍKLAD Platí-li ve 32bitovém prostředí na PC deklarace unsigned u = 1;
54 Jazyky C a C++
způsobí příkaz cout << -u << endl; výstup
4294967295
S hodnotou, uloženou v u, proběhly tytéž operace jako při výpočtu opačného čísla pro typ int, výsledek se však interpretuje jako hodnota typu unsigned. Výsledkem příkazů int x = INT_MIN; // -2147483648 cout << -x << endl; bude výstup
-2147483648
Použitím unárního minus na hodnotu typu int vznikne opět hodnota typu int. Na PC (a obecně na počítačích používajících dvojkový doplněk) se hodnota opačná k INT_MIN nevejde do rozsahu typu int, takže dojde k celočíselnému přetečení; jeho výsledkem je uvedená hodnota. Podobně platí LONG_MIN == -LONG_MIN
a v překladačích, které podporují typ long long, také LLONG_MIN == -LLONG_MIN
Binární aritmetické operace Před provedením binární aritmetické operace proběhnou s operandy celočíselná rozšíření (viz kapitola 8.3.1) a obvyklé aritmetické konverze (viz kapitola 8.3.2), které zaručí, že oba operandy budou stejného typu. Výsledek je stejného typu jako oba operandy po konverzích. ❚ O perátory +, resp. – znamenají sčítání, resp. odečítání. ❚ O perátory *, resp. / znamenají násobení, resp. celočíselné dělení. Operátor % vrací zbytek po celočíselném dělení (operace modulo). ❚ J e-li druhý operand operátorů / nebo % nulový, není chování programu definováno (výsledkem může být cokoli, včetně přerušení výpočtu pro výjimku procesoru). Jinak jsou-li a, b dvě celá čísla a b není rovno 0, platí (a/b)*b + a%b == a. ❚ J sou-li oba operandy kladné, je i výsledek operace % kladný. Jinak výsledek závisí na implementaci, tj. zpravidla na vlastnostech procesoru.
PŘÍKLADY Platí následující vztahy: 13 / 7 == 1 13 % 7 == 6
Inkrementace a dekrementace Na modifikovatelnou l-hodnotu kteréhokoli z celočíselných typů můžeme aplikovat prefixové i postfixové operátory ++ a – –.
Základní datové typy 55
❚ O perátor ++ způsobí, že operand bude obsahovat následníka, tedy hodnotu o 1 vyšší, než byla jeho původní hodnota. ❚ O perátor – – způsobí, že operand bude obsahovat předchůdce, tedy hodnotu o 1 nižší, než byla jeho původní hodnota. ❚ P refixový operátor způsobí, že nejprve se změní hodnota operandu a teprve pak se tato hodnota použije. V případě postfixového operátoru se nejprve použije hodnota operandu a teprve pak se tato hodnota změní. ❚ O perace s typy bez znaménka probíhají modulo 2n, kde n je počet bitů v reprezentaci typu operandu. ❚ P ro typ bool platí, že následníkem jakékoli hodnoty je true. Použití operátoru ++ na hodnoty typu bool se však považuje za nevhodné. ❚ O perátor dekrementace (prefixový ani postfixový) se nesmí aplikovat na hodnoty typu bool.
3.4.3 Relace Pro porovnávání celých čísel můžeme použít relační operátory <, <=, >=, >, == a !=. S operandy proběhnou celočíselná rozšíření (viz kapitola 8.3.1) a obvyklé aritmetické konverze (viz kapitola 8.3.2), které zaručí, že oba operandy budou stejného typu. ❚ O perátory <, <=, >=, > znamenají po řadě operace menší než, menší nebo rovno, větší nebo rovno, větší než. ❚ O perátory ==, resp. != představují relace rovná se, resp. nerovná se. ❚ V C90 vracejí tyto operace hodnoty typu int, a to 0, pokud relace není splněna, a 1, pokud splněna je. V C99 vracejí tyto operace stejné hodnoty, jsou však typu _Bool. V C++ vracejí tyto operace hodnoty typu bool, a to a to false, pokud relace není splněna, a true, pokud splněna je.
3.4.4 Logické operace Celá čísla lze v C i v C++ použít všude, kde se očekávají logické hodnoty (typ _Bool v C99, resp. typ bool v C++). To znamená, že na celočíselné operandy můžeme použít binární logické operátory &&, || a unární logický operátor !. Výsledek je v C90 typu int, v C99 typu _Bool a v C++ je typu bool. ❚ O perátor && znamená logickou konjunkci (logický součin, operaci A): Výsledek je pravdivý pouze v případě, že jsou pravdivé oba operandy. Jinak je výsledek nepravdivý. Operátor || znamená logickou disjunkci (logický součet, operaci NEBO): Výsledek je pravdivý, je-li pravdivý alespoň jeden operand. Jsou-li oba operandy nepravdivé, je i výsledek nepravdivý. ❚ O perátor ! znamená logickou negaci. Výsledek je pravdivý, je-li operand nepravdivý, a naopak, výsledek je nepravdivý, je-li operand pravdivý. ❚ T abulku pravdivostních hodnot těchto operátorů najdete v kapitole 8.4.6.
3.4.5 Bitové operace Jazyky C a C++ umožňují pracovat i s jednotlivými bity celých čísel. K tomu slouží binární operátory &, | a ^ a unární bitový operátor ~. Vedle toho lze k bitovým operacím použít také operátory << a >>. 56 Jazyky C a C++
Bitový doplněk Unární operátor ~ vytvoří bitový (jednotkový) doplněk: Je-li daný bit operandu roven 1, bude odpovídající bit výsledku roven 0 a naopak, je-li daný bit operandu roven 0, bude odpovídající bit výsledku roven 1. ❚ S operandem proběhnou celočíselná rozšíření. Výsledek je stejného typu jako operand po konverzi.
Binární bitové operace Před provedením binární bitové operace proběhnou s operandy celočíselná rozšíření (viz kapitola 8.3.1) a obvyklé aritmetické konverze (viz kapitola 8.3.2), které zaručí, že oba operandy budou stejného typu. Výsledek je stejného typu jako oba operandy po konverzích. ❚ O perátor & znamená bitovou konjunkci. Daný bit výsledku je roven 1, jsou-li odpovídající bity obou operandů rovny 1, jinak je roven 0. ❚ O perátor | znamená bitovou disjunkci. Daný bit výsledku je roven 1, je-li odpovídající bit alespoň jednoho z operandů roven 1. Jsou-li odpovídající bity obou operandů rovny 0, je i odpovídající bit výsledku roven 0. ❚ O perátor ^ znamená bitovou nonekvivalenci (výlučné NEBO). Daný bit výsledku je roven 1, jsou-li odpovídající bity obou operandů různé. Jsou-li odpovídající bity obou operandů stejné, je odpovídající bit výsledku roven 0. ❚ T abulky hodnot těchto operátorů najdete v kapitole 8.4.5.
Bitové posuny Operátory << a >> slouží k bitovým posunům. Jejich levým operandem je celé číslo, jehož bity chceme posunout, pravým operandem je výraz udávající, o kolik pozic chceme bity v daném čísle posunout. S levým operandem proběhnou celočíselná rozšíření; výsledek je stejného typu jako levý operand po konverzi. ❚ O perátor << znamená bitový posun vlevo. Výsledkem výrazu E1 << E2 je hodnota, která vznikne z E1 posunutím o E2 bitů doleva. Pravé bity se doplní nulami, levé bity, které se posunou „mimo rozsah“ E1, se ztratí. ❚ J e-li E1 výraz celočíselného typu bez znaménka, znamená E1 << E2 násobení číslem 2E2 modulo 2n, kde n je počet bitů, použitých ke zobrazení daného celočíselného typu. ❚ O perátor >> znamená bitový posun vpravo. Výsledkem výrazu E1 >> E2 je hodnota, která vznikne z E1 posunutím o E2 bitů doprava. Pravé bity, které se posunou „mimo rozsah“ E1, se ztratí. ❚ J e-li E1 výraz celočíselného typu bez znaménka, doplní se levé bity nulami. Je-li E1 výraz celočíselného typu se znaménkem, doplní se levé bity hodnotou znaménkového bitu. ❚ J e-li E1 typ bez znaménka nebo kladná hodnota typu se znaménkem, je E1 >> E2 ekvivalentní celočíselnému dělení 2E2. Je-li E1 záporná hodnota typu se znaménkem, závisí výsledek na implementaci.
PŘÍKLADY Platí-li ve 32bitovém prostředí používajícím zobrazení ve dvojkovém doplňku, tedy např. v některém z běžných překladačů na PC, deklarace int n = 0, m;
Základní datové typy 57
bude po provedení operací m = ~n;
v proměnné m uložena hodnota -1. Po provedení operací n = 1; m = n << 2;
bude v m uložena hodnota 4; po provedení operací n = -1; m = n >> 2;
bude v m uložena hodnota -1, neboť zleva se doplňuje znaménkový bit.
3.4.6 Další operace Funkce pro výpočet absolutní hodnoty celých čísel se znaménkem (abs(), labs(), v C99 také llabs()) najdeme v hlavičkovém souboru <stdlib.h>, resp. v v C++. Funkce pro konverzi znakového řetězce (atoi(), atol(), v C99 také atoll()) na celé číslo najdeme také v <stdlib.h>m stejně jako funkci rand() pro generování rovnoměrně rozdělených celých čísel v intervalu 0 .. RAND_MAX.
3.5 Reálná čísla Reálná čísla („čísla s pohyblivou řádovou čárkou“) se v paměti počítače ukládají jako dvojice hodnot, z nichž jedna se nazývá mantisa a druhá exponent. V tomto takzvaném semilogaritmickém tvaru by např. číslo 54 123 mohlo být zapsáno 0,54123 × 104. Číslo 0,54123 je zde mantisa, číslo 4 je exponent a číslo 10 je základ číselné soustavy (ten není třeba ukládat). V počítači se pro ukládání reálných čísel ovšem používá základ 2, takže jak mantisa, tak exponent jsou binární čísla. Jazyky C a C++ znají 3 typy reálných čísel. Jejich přehled najdete v následující tabulce. Tabulka 3.5: Typy vyjadřující reálná čísla typ
přibližný rozsah hodnot na PC
float
3,4E-38 – 3,4E+38
long double
3,4E-4932 – 3,4E+4932
double
1,7E-308 – 1,7E+308
❚ M ísto double můžeme ve starších verzích jazyka C také psát long float. Ani standard C99, ani standard C++ tuto náhradu nezná, mnohé současné překladače ji ale umožňují. ❚ T yp double poskytuje nejméně stejnou přesnost výpočtu jako typ float a typ long double poskytuje nejméně stejnou přesnost výpočtu jako typ double. Množina hodnot typu float je podmnožinou množiny hodnot typu double a množina hodnot typu double je podmnožinou množiny hodnot typu long double. V některých implementacích na PC má typ long double stejný rozsah jako typ double.
58 Jazyky C a C++
❚ V běžných implementacích zabírá typ float 4 B, typ double 8 B a typ long double 8 B nebo 10 B. ❚ M inimální a maximální hodnoty reálných jednotlivých typů a další důležité údaje o nich najdeme v hlavičkovém souboru , popřípadě ve specializaci šablonové třídy std::numeric_limits<>.
3.5.1 Reálné typy v C99 V C99 jsou v hlavičkovém souboru <math.h> definovány typy float_t a double_t, což jsou reálné typy s alespoň takovým rozsahem jako float, resp. double. ❚ V ýznam těchto typů se řídí hodnotou makra FLT_EVAL_METHOD: Má-li toto makro hodnotu 0, představují typy float_t, resp. double_t ve skutečnosti typy float, resp. double. Má-li hodnotu 1, představují oba double, a má-li hodnotu 2, představují oba long double. Implementace může definovat ještě další možnosti.
3.5.2 Reálné literály Reálné literály zapisujeme v programu buď ve tvaru s pevnou řádovou tečkou, nebo v semilogaritmickém tvaru. Zápis ve tvaru s pevnou řádovou tečkou se podobá běžnému zápisu desetinného čísla, obsahuje však místo desetinné čárky tečku – např. 3.14159 – a nesmí obsahovat žádné oddělovače skupin (nelze tedy psát např. 3.141 592 6). Semilogaritmický tvar obsahuje mantisu, znak E nebo e a za ním exponent. Např. číslo 123.456 zapíšeme v semilogaritmickém tvaru jako 0.123456E3 (to znamená 0,123456 × 103). ❚ A ni před znakem E, resp. e ani za ním nesmíme udělat mezeru. ❚ R eálné literály jsou typu double. Chceme-li literál typu float, připojíme k němu příponu F nebo f. Přípona L nebo l označuje literál typu long double.
Reálné literály v C99 ❚ V C99 můžeme reálné literály zapisovat i v šestnáctkové soustavě. Takovýto literál začíná prefixem 0x nebo 0X, za nímž následuje šestnáctkové číslo s řádovou tečkou a „exponentová část“. V ní ale místo e nebo E použijeme p nebo P. ❚ T ento exponent se vztahuje k základu 2, nikoli k základu 10 nebo 16.
PŘÍKLAD Proměnná double x = 0x40.2P2; bude mít hodnotu 256,5.
// pouze v C99
❚ M akro INFINITY, definované v <math.h>, se rozvine v kladné nekonečno nebo v nekonečno bez znaménka, pokud je to v dané implementaci možné, nebo v kladnou konstantu typu float, která způsobí přetečení v době překladu. ❚ V implementacích, které podporují tichý NaN pro typ float, by mělo být k dispozici makro NAN, které se rozvine v odpovídající hodnotu.
Základní datové typy 59
3.5.3 Operace s reálnými čísly Vlastnostmi jednotlivých operátorů se budeme podrobně zabývat později (viz kapitola 8.4). Zde pouze shrneme, jaké operace jsou s reálnými čísly dovoleny a jaké jsou jejich výsledky. V tomto oddílu se nezabýváme konverzemi reálných typů, neboť o nich budeme hovořit v kapitole 8.3.1.
Přiřazování Modifikovatelné l-hodnotě jakéhokoli reálného typu lze přiřadit r-hodnotu kteréhokoli číselného nebo výčtového typu. (O výčtových typech budeme hovořit v kapitole 4.1.) Přitom se r-hodnota nejprve konvertuje na typ l-hodnoty a výsledek této konverze se přenese na určené místo.
Aritmetické operace Na hodnotu kteréhokoli z reálných typů lze aplikovat unární aritmetické operátory + a – a binární aritmetické operátory +, –, * a /. Jedním z operandů může být i hodnota celočíselného typu. Před použitím proběhnou s operandy obvyklé aritmetické konverze, které způsobí, že oba operandy budou stejného typu. Výsledek je téhož typu jako operandy po konverzi. Na l-hodnoty reálných typů lze aplikovat operátory inkrementace a dekrementace ++ a – –. ❚ U nární operátory +, resp. – představují identitu, resp. výpočet opačného čísla. ❚ B inární operátory +, –, * a / vyjadřují po řadě sčítání, odečítání, násobení a dělení reálných čísel. ❚ P řed použitím binárních aritmetických operátorů proběhnou s operandy obvyklé aritmetické konverze, které zajistí, že oba operandy budou stejného typu. Výsledek je stejného typu jako operandy po konverzi. ❚ J estliže výsledek operace neleží v rozsahu tohoto typu, není chování programu definováno. Podobně má-li pravý operand operátoru / hodnotu 0, není chování programu definováno. ❚ O perátory ++, resp. – – způsobí přičtení, resp. odečtení 1 od operandu. Prefixový operátor způsobí, že nejprve se změní hodnota operandu a teprve pak se tato hodnota použije. V případě postfixového operátoru se nejprve použije hodnota operandu a teprve pak se tato hodnota změní.
Relace Pro porovnávání reálných čísel mezi sebou (nebo s celými čísly) lze použít relační operátory <, <=, >=, >, == a !=. Jejich význam a vlastnosti jsou stejné jako v případě celých čísel (viz kapitola 3.4.3). ❚ N ěkterá reálná čísla nelze v počítači reprezentovat přesně. Proto je třeba při porovnávání velmi blízkých reálných hodnot brát v úvahu zaokrouhlovací chyby. Zejména použití operátoru == může způsobit problémy.
Logické operace Reálná čísla lze v C i v C++ použít všude, kde se očekávají logické hodnoty (typ _Bool v C99, resp. typ bool v C++). To znamená, že na reálné operandy můžeme použít binární
60 Jazyky C a C++
logické operátory &&, || a unární logický operátor !. Jejich vlastnosti jsou stejné jako v případě celých čísel (viz kapitola 3.4.4).
Další operace s reálnými čísly V hlavičkovém souboru <math.h>, resp. najdeme běžné matematické funkce, jako je sin(), cos(), atan(), exp() a další; tyto funkce očekávají parametr typu double a vracejí hodnotu typu double. ❚ F unkce sinf(), cosf(), atanf(), expf() a další očekávají parametr typu float a vracejí hodnotu typu float. ❚ F unkce sinl(), cosl(), atanl(), expl() a další očekávají parametr typu long double a vracejí hodnotu typu long double. ❚ V C++ jsou k dispozici přetížené verze funkcí sin(), cos(), atan(), exp() a dalších pro typy float a long double.
3.6 Komplexní čísla (jen C99) Jazyky C90 a C++ nemají vestavěný typ pro práci s komplexními čísly. V C90 se setkáme s různými nestandardními rozšířeními založenými na vhodné struktuře, C++ nabízí ve standardní knihovně v hlavičkovém souboru šablonu complex<>, jež umožňuje výpočty s celými čísly. Jazyk C99 nabízí tři typy pro práci s komplexními čísly, a to float _Complex, double _Complex a long double _Complex. Jsou implementovány jako dvojice reálných čísel typů float, double, resp. long double. ❚ O typu float _Complex říkáme, že jeho odpovídající reálný typ je float; podobně definujeme odpovídající reálný typ pro zbývající komplexní typy. ❚ I mplementace může obsahovat také tři datové typy pro reprezentaci imaginárních čísel, a to float _Imaginary, double _Imaginary a long double _Imaginary. I zde hovoříme o odpovídajícím reálném typu. ❚ R eálná čísla lze automaticky konvertovat na komplexní odpovídajícího typu. Dané reálné číslo se stane reálnou částí vytvořeného komplexního čísla a doplní se nulová imaginární část. ❚ Komplexní čísla lze automaticky konvertovat na reálná čísla. Přitom se „zahodí“ imaginární část. ❚ I maginární čísla (typu float _Imaginary atd.) lze automaticky konvertovat na komplexní odpovídajícího reálného typu. Dané imaginární číslo se stane imaginární částí vytvořeného komplexního čísla a doplní se nulová reálná část. . ❚ Komplexní čísla lze automaticky konvertovat na imaginární čísla odpovídajícího reálného typu. Přitom se „zahodí“ reálná část.
Makra pro komplexní čísla ❚ V hlavičkovém souboru najdeme makra complex a imaginary, která se rozvinou v klíčová slova _Complex, resp. _Imaginary. ❚ M akro _Complex_I, definované opět v hlavičkovém souboru , se rozvine v hodnotu typu const float _Complex představující číslo i (komplexní jednotku, někdy také označovanou j). Základní datové typy 61
❚ M akro _Imaginary_I se rozvine v hodnotu typu const float _Imaginary představující číslo i. ❚ M akro I se rozvine v _Imaginary_I nebo v _Complex_I. ❚ M akra imaginary a _Imaginary_I jsou k dispozici pouze v implementacích, které podporují typy _Imaginary.
3.6.1 Operace s komplexními čísly Vlastnostmi jednotlivých operátorů se budeme podrobně zabývat později (viz kapitola 8.4). Zde pouze shrneme, jaké operace jsou s komplexními čísly dovoleny a jaké jsou jejich výsledky. V tomto oddílu se nezabýváme konverzemi reálných a komplexních typů, neboť o nich budeme hovořit v kapitole 8.3.
3.6.2 Přiřazování Modifikovatelné l-hodnotě jakéhokoli reálného typu lze přiřadit r-hodnotu kteréhokoli číselného nebo výčtového typu. (O výčtových typech budeme hovořit v kapitole 4.1.) Přitom se r-hodnota nejprve konvertuje na typ l-hodnoty a výsledek této konverze se přenese na určené místo.
3.6.3 Aritmetické operace Na hodnotu kteréhokoli z komplexních typů lze aplikovat unární aritmetické operátory + a – a binární aritmetické operátory +, –, * a /. Výsledek je téhož typu jako operandy po konverzi. Na l-hodnoty komplexních typů lze také aplikovat operátory inkrementace a dekrementace ++ a – –. ❚ U nární operátory +, resp. – představují identitu, resp. výpočet opačného čísla. ❚ B inární operátory +, –, * a / vyjadřují po řadě sčítání, odečítání, násobení a dělení komplexních čísel. ❚ Komplexní čísla můžeme porovnávat pomocí operátorů == a !=. Operace <, <=, > a >= nemají pro komplexní čísla smysl. ❚ P řed použitím binárních aritmetických operátorů proběhnou s operandy obvyklé aritmetické konverze, které zajistí, že oba operandy budou stejného typu. Výsledek je stejného typu jako operandy po konverzi. ❚ J estliže výsledek operace neleží v rozsahu tohoto typu, není chování programu definováno. ❚ M á-li pravý operand operátoru / hodnotu 0, není chování programu definováno. ❚ O perátory ++, resp. – – nelze na komplexní čísla aplikovat.
3.6.4 Logické operace Komplexní čísla lze v C i v C++ použít všude, kde se očekávají logické hodnoty (typ _Bool v C99, resp. typ bool v C++). To znamená, že na komplexní operandy můžeme použít binární logické operátory &&, || a unární logický operátor !. Jejich vlastnosti jsou stejné jako v případě celých čísel (viz kapitola 6.4.4). 62 Jazyky C a C++
3.6.5 Další operace s komplexními čísly V hlavičkovém souboru najdeme běžné matematické funkce definované pro komplexní typy. Jejich jména začínají písmenem c: csin(), ccos(), catan(), cexp(). V hlavičkovém souboru jsou také definována „typově generická“ makra sin(), cos(), atan(), exp(), která podle typu skutečného parametru zavolají odpovídající reálnou nebo komplexní funkci.
3.7 Typ void Mezi základní typy se řadí také typ void. Nejde o plnohodnotný datový typ, neboť v jazycích C a C++ nelze deklarovat proměnnou typu void. (Standard říká, že jde o neúplný typ, jehož deklarace nemůže být dokončena.) Typ void lze použít následujícími způsoby: ❚ J ako návratový typ funkce. V tom případě vyjadřuje, že jde o funkci, která nic nevrací, tedy o proceduru. ❚ J ako specifikaci formálních parametrů funkce. V tom případě říká, že daná funkce nemá parametry. ❚ J ako cílový typ při přetypování. Takovéto přetypování znamená „zahození“ hodnoty konvertovaného výrazu. ❚ M ísto doménového typu ukazatele. Ukazatel typu void* nemá doménový typ – může ukazovat na objekt6 jakéhokoli typu. Typ void nelze použít mj. v následujících situacích: ❚ N elze deklarovat proměnnou typu void. ❚ N elze deklarovat pole typu void. ❚ N elze deklarovat referenci na void. ❚ U kazatel typu void* nelze dereferencovat (je třeba ho před použitím přetypovat na ukazatel s doménovým typem).
Nikoli ve smyslu OOP.
6
Základní datové typy 63
4.
Výčtové typy, struktury a unie Výčtové typy, struktury a unie již patří mezi typy, které si může definovat programátor. V C++ jsou struktury a unie objektové typy. V této kapitole však o jejich objektových vlastnostech nebudeme hovořit; půjde nám o shrnutí jejich neobjektových vlastností, které jsou v obou jazycích téměř stejné. Standard C++ pro takto chápané struktury a unie používá označení POD, což je zkratka slov plain old data – obyčejná stará data. O objektových vlastnostech struktur a unií budeme hovořit v kapitole 11.
4.1 Výčtové typy Výčtové typy umožňují v programu vyjádřit skutečnosti, které stačí popsat celými čísly, ale u nichž potřebujeme omezit možnost přiřazování hodnot. Například dny v týdnu bychom mohli popsat např. čísly 1–7, nebo lépe pojmenovanými konstantami s uvedenými hodnotami. Podobně ovšem můžeme popsat i např. stavy datového proudu, a pak se může snadno stát, že proměnné, která má obsahovat stav proudu, přiřadíme hodnotu dne. Výčtové typy v C i v C++ jsou vlastně celočíselné typy; v jazyce C++ jsou pro ně omezeny možnosti automatických konverzí. Používají se také často pro práci s bitovými příznaky. Vlastnosti výčtových typů v jazycích C a C++ se poněkud liší. Navíc jazyk C++0x přinesl dvě novinky, které se týkají výčtových typů: možnost specifikace podkladového typu a možnost deklarace kvalifikovaného výčtového typu.
4.1.1 Deklarace výčtového typu Pro přehlednost popíšeme zvlášť deklaraci výčtového typu v jazyce C a v C++03, a zvlášť v C++0x.
Deklarace výčtového typu v C a v C++03 Deklarace výčtového typu: // V jazyce C a v C++03 • e num jmenovka ; • e num jmenovkanep { seznam_položek , nep } seznam_deklarátorůnep ; Výčtové typy, struktury a unie 65
Seznam položek: // Ve všech verzích C i C++ • p oložka • p oložka , seznam_položek Položka: // Ve všech verzích C i C++ • identifikátor • identifikátor = konstantní_výraz ❚ P rvní varianta deklarace výčtového typu (předběžná deklarace) pouze oznamuje překladači, že existuje výčtový typ s danou jmenovkou, nepopisuje ho. Zná-li překladač pouze tuto deklaraci, není výčtový typ plně definován. Zbylé dvě možnosti daný typ také popisují (definují). ❚ J menovka: Jméno (identifikátor) deklarovaného výčtového typu. Lze ji vynechat, pak vznikne nepojmenovaný (anonymní) výčtový typ – tedy vlastně skupina pojmenovaných konstant, nic více. Nepojmenovaný výčtový typ nemůže mít předběžnou deklaraci. ❚ P oložka: Deklarace pojmenované hodnoty výčtového typu (výčtové konstanty). V deklaraci jí můžeme přidělit hodnotu; ta musí být vyjádřena celočíselným výrazem, který lze vyhodnotit v době překladu. ❚ Z a seznamem položek před ukončovací složenou závorkou může být čárka. To usnadňuje automatické generování zdrojového kódu. ❚ V deklaraci výčtové konstanty můžeme použít i hodnoty výčtových konstant deklarovaných dříve. ❚ P okud specifikaci hodnoty u některé položky vynecháme, přidělí jí překladač hodnotu o 1 větší, než má předchozí položka. Neuvedeme-li hodnotu u první položky, přidělí jí překladač hodnotu 0. ■ Různé položky mohou mít stejné hodnoty. ■ Hodnoty nemusí být uvedeny ve vzestupném pořadí. ❚ S eznam_deklarátorů: Přímo v deklaraci výčtového typu můžeme definovat proměnné tohoto typu, ukazatele na ně, pole atd. Jestliže je nedefinujeme zde, můžeme je definovat později, pokud nejde o nepojmenovaný výčtový typ. ❚ J méno výčtového typu v jazyce C je enum jmenovka, v C++ stačí užít pouze jmenovku (lze ale použít i konstrukci enum jmenovka). ❚ V ýčtový typ je plně definován v okamžiku, kdy překladač zpracuje ukončovací složenou závorku jeho deklarace. Výčtové konstanty jsou deklarovány v okamžiku, kdy překladač zpracuje jejich výskyt v deklaraci výčtového typu.
PŘÍKLAD enum chyba {CHYBA_CTENI = 1, CHYBA_ZAPISU = 2} a, *b; enum den {po, ut, st, ct, pa, so, ne,}; typedef enum {cervene = 1, zelene, zaludy, kule} barva;
V deklaraci výčtového typu chyba zavádíme dvě výčtové konstanty, a to CHYBA_CTENI, jež má hodnotu 1, a CHYBA_ZAPISU s hodnotou 2. Přímo v deklaraci výčtového typu jsme deklarovali proměnnou a typu chyba a proměnnou b typu ukazatel na typ chyba. V deklaraci typu den představuje konstanta po hodnotu 0, ut hodnotu 1 atd. Čárka za konstantou ne není chyba – dovoluje ji jak standard C++, tak i C99. 66 Jazyky C a C++
Třetí deklarace ukazuje typický idiom jazyka C: Abychom nemuseli v programu psát zdlouhavou kombinaci enum jmenovka, jmenovku vynecháme a celou deklaraci vnoříme do deklarace typedef. Na takto deklarovaný typ se odvoláváme identifikátorem barva.
PŘÍKLAD Chceme-li v jazyce C deklarovat funkci f() s jedním parametrem typu chyba z předchozího příkladu, napíšeme void f(enum chyba x) { /* tělo funkce */ } V C++ stačí psát
void f(chyba x) { /* tělo funkce */ }
Můžeme ale samozřejmě použít i stejný zápis jako v jazyce C.
Deklarace výčtového typu v C++0x Deklarace výčtového typu: // V C++0x • e num klíčnep jmenovka podkladový_typnep; • e num klíčnep jmenovkanep podkladový_typnep { seznam_položek , nep } seznam_deklarátorůnep; Podkladový typ: // Jen C++0x • : jméno_celočíselného_typu Klíč: // Jen C++0x • c lass • s truct Z tohoto popisu plyne, že deklarace výčtového typu může v C++0x vypadat stejně jako ve starších verzích jazyka – a pak pro ni platí i stejná pravidla. Navíc zde ale můžeme specifikovat klíč a podkladový typ. ❚ P odkladový typ jméno celočíselného typu zapsané za dvojtečkou. Hodnoty daného výčtového typu budou ukládány v proměnné, jejíž velikost, způsob uložení atd. bude stejný jako u proměnné podkladového typu. Tím ve skutečnosti definujeme rozsah daného výčtového typu. ❚ N euvedeme-li v deklaraci výčtového typu T podkladový typ, použije překladač celočíselný typ, do něhož lze uložit hodnoty všech výčtových konstant typu T. ❚ K líč je klíčové slovo struct nebo class. V deklaraci výčtového typu znamenají obě totéž. ❚ P oužijeme-li klíč, deklarujeme kvalifikovaný výčtový typ (scoped enum type). Konstanty tohoto typu je třeba při použití kvalifikovat jménem typu, v němž byly deklarovány.
Výčtové typy, struktury a unie 67
PŘÍKLAD Deklarací enum konstanta: long {min = INT_MIN, stred = 0, max = 50};
zavádíme nekvalifikovaný výčtový typ konstanta, jehož podkladový typ bude long. Deklarací enum class velikost: short {maly, stred, velky};
zavádíme kvalifikovaný výčtový typ velikost. Je-li x proměnná tohoto typu, přiřadíme jí hodnotu zápisem x = velikost::stred;
Obě deklarace se mohou objevit v témže programu a nedojde ke konfliktu jmen, neboť hodnoty typu velikost musíme vždy kvalifikovat.
4.1.2 Použití výčtového typu Nekvalifikovaný výčtový typ Identifikátory výčtových konstant nekvalifikovaného výčtového typu představují hodnoty určené v deklaraci. Používají se samostatně, není třeba (a nelze) je kvalifikovat jménem typu. Příkazem Barvy b = cervena; uložíme do proměnné b hodnotu konstanty cervena. Hodnotu libovolného nekvalifikovaného výčtového typu lze automaticky konvertovat na hodnotu libovolného číselného typu. ❚ N elze deklarovat proměnné výčtového typu, který není plně definován. Lze ovšem deklarovat proměnné typu ukazatel nebo reference na neúplně definovaný typ. ❚ N eúplně definovaný (neúplný) typ nelze použít jako typ parametru předávaného hodnotou; lze ho ovšem použít jako typ parametru předávaného odkazem (v C++) nebo ukazatelem.
Kvalifikovaný výčtový typ (C++0x) Také identifikátory kvalifikovaného výčtového typu představují hodnoty určené v deklaraci a platí pro ně podobná pravidla jako pro nekvalifikovaný výčtový typ, až na to, že při použití je musíme vždy kvalifikovat jménem typu, které připojíme před jméno konstanty operátorem ::. To umožňuje použít v různých výčtových typech stejně pojmenované konstanty, aniž by vznikl konflikt jmen.
4.1.3 Rozsah výčtového typu Do proměnné výčtového typu lze uložit i jiné hodnoty než odpovídající výčtové konstanty. ❚ V jazyce C se proměnné výčtového typu chovají jako proměnné typu char, int nebo unsigned. Konkrétní volba podkladového typu závisí na implementaci a na rozsahu hodnot výčtových konstant. V každém případě musí obsáhnout všechny hodnoty těchto konstant.
68 Jazyky C a C++
❚ V C++03 je rozsah hodnot výčtového typu určen takto: Označíme-li amin, resp. amax hodnoty nejmenší, resp. největší z výčtových konstant, je rozsah typu určen dvojicí bmin, resp. bmax, kde bmin, resp. bmax představují nejmenší, resp. největší hodnotu nejmenšího bitového pole, do něhož lze uložit amin i amax. ❚ J sou-li celá čísla reprezentována pomocí dvojkového doplňku (tedy např. na PC), platí: ■ Jsou-li všechny výčtové konstanty nezáporné, je dolní mez rozsahu výčtového typu bmin rovna 0. ■ Jinak je bmax nejmenší hodnota větší nebo rovná většímu z čísel abs(amin ) - 1 a abs(amax ), která má tvar 2M - 1, a bmin je rovno - (bmax + 1 ). Jinými slovy, je to nejmenší rozsah tvaru -2M, 2M - 1, do něhož se vejdou jak hodnoty výčtových konstant, tak i čísla k nim opačná (s opačným znaménkem). ❚ P ro výčtový typ v C++0x, v jehož deklaraci není určen podkladový typ, platí stejná pravidla jako v C++03. ❚ J e-li v deklaraci uveden podkladový typ, je rozsah výčtového typu roven rozsahu podkladového typu.
4.1.4 Operace s výčtovými typy Nekvalifikované výčtové typy lze používat všude, kde lze používat celá čísla. Před aritmetickými operacemi se hodnoty výčtových typů převedou na hodnotu typu int, nebo na nejmenší typ se znaménkem nebo bez, který může pojmout všechny jejich hodnoty.
Konverze ❚ V jazyce C lze hodnotu libovolného výčtového typu automaticky konvertovat na hodnotu libovolného číselného typu a naopak, hodnotu libovolného číselného typu lze automaticky konvertovat na hodnotu libovolného výčtového typu. ❚ V C++ lze hodnotu libovolného nekvalifikovaného výčtového typu automaticky konvertovat na hodnotu libovolného číselného typu. Opačnou konverzi, nebo konverzi mezi různými výčtovými typy, musíme explicitně předepsat. ❚ P ro kvalifikované výčtové typy není automatická konverze na celá čísla definována. Lze je ale na celá čísla konvertovat explicitně.
4.1.4 Aritmetické a jiné operace S výčtovými typy lze provádět všechny operace, které lze dělat s celočíselnými typy. Platí tedy vše, co jsme si řekli v kapitole 3.1.
PŘÍKLAD Platí-li deklarace enum den {po = 1, ut = 2, st = 4, ct = 8, pa = 16, so = 32, ne = 64}; lze proměnné
den volno;
přiřadit hodnotu volno = den(so | ne); Výčtové typy, struktury a unie 69
a bude obsahovat bitovou disjunkci uvedených dvou hodnot. Přetypování výsledku operace so | ne na typ dny je nezbytné, neboť výsledek této operace je typu int.
4.1.5 Přetěžování operátorů Pro výčtové typy lze v C++ přetěžovat všechny operátory, které lze přetěžovat jako volné funkce (viz kapitola 12.2).
4.2 Struktury Struktura je datový typ představující skupinu pojmenovaných proměnných různých typů, s nimiž zacházíme jako s celkem. Jednotlivé proměnné, jež tvoří strukturu, označujeme jako složky (sloty).7
4.2.1 Deklarace struktury deklarace struktury: • s truct jmenovka ; • s truct jmenovkanep { deklarace_složek } seznam_deklarátorůnep; ❚ P rvní možnost pouze informuje překladač o existenci datového typu, nepopisuje ho (předběžná deklarace). Druhá možnost ho i popisuje (definuje). Zná-li překladač pouze předběžnou deklaraci, říkáme, že typ je neúplně definován, že je neúplný. ❚ J menovka: Identifikátor představující jméno deklarovaného typu. Neuvedeme-li ji, bude deklarace struktury představovat definici nepojmenovaného datového typu. V tom případě musíme deklarovat alespoň jednu proměnnou tohoto typu. ❚ D eklarace složek: Deklarace jednotlivých proměnných, které budou tvořit proměnnou typu struktura. ❚ D eklarace složek (kromě bitových polí) se řídí stejnými syntaktickými pravidly jako deklarace „obyčejných“ proměnných, pouze nesmějí obsahovat inicializaci. Výjimku tvoří bitová pole (viz kapitola 4.2.4) a tzv. otevřená pole (viz kapitola 4.2.5), která mají vlastní syntaktická pravidla. ❚ S eznam_deklarátorů: Přímo v deklaraci typu struktury můžeme definovat proměnné tohoto typu, ukazatele na ně, pole tohoto typu atd. Pokud je nedefinujeme zde, můžeme je definovat později, pokud nejde o nepojmenovaný typ. ■ Proměnné nepojmenovaných strukturových typů nelze předávat jako parametr funkce. ❚ J méno takto deklarovaného typu v jazyce C je struct jmenovka, v C++ stačí užít pouze jmenovku. ❚ I dentifikátory složek musí být jednoznačné v rámci deklarovaného typu. Různé strukturové typy mohou obsahovat složky se stejnými identifikátory. Identifikátor složky také může být stejný jako identifikátor globální nebo lokální proměnné, funkce nebo typu. V angličtině se pro datové složky struktur a tříd v C++ používá také označení field. Doslovnému překladu „pole“ se ale vyhýbáme, protože český termín pole je v programování vyhrazen pro něco jiného.
7
70 Jazyky C a C++
❚ S truktura (jako datový typ) je plně definována v okamžiku, kdy překladač zpracuje ukončovací složenou závorku její deklarace. Jméno typu (neúplnou deklaraci) zná v okamžiku, kdy zpracuje jmenovku.
PŘÍKLAD Deklarace struct osoba { char jmeno[20]; char ulice[20]; int psc; char mesto[20]; };
zavádí datový typ struct osoba. Proměnnou tohoto typu deklarujeme příkazem struct osoba sef;
V jazyce C++ můžeme napsat jen osoba sef;
V jazyce C se deklarace struktury často uzavírá do deklarace typedef, podobně jako deklarace výčtového typu, aby bylo možno při použití vynechat klíčové slovo struct: typedef struct _datum { int den, mesic, rok; } datum;
Úplně a neúplně deklarované typy ❚ N elze deklarovat proměnné strukturového typu, který není plně definován. Lze ovšem deklarovat proměnné typu ukazatel nebo reference na neúplně deklarovaný typ. ❚ N eúplně deklarovaný typ nelze použít jako typ parametru předávaného hodnotou; lze ho ovšem použít jako typ parametru předávaného odkazem (v C++) nebo ukazatelem.
PŘÍKLAD Následující deklarace jsou správné: typedef struct prvek PRVEK, *UPRVEK; struct prvek { int data; UPRVEK dalsi; };
První deklarace oznamuje existenci typu struct prvek, zavádí pro něj jméno PRVEK a zároveň zavádí jméno UPRVEK pro ukazatel na ni. Druhá deklarace pak definuje typ struct prvek (velmi jednoduchý prvek jednosměrně zřetězeného seznamu).
Výčtové typy, struktury a unie 71
Agregáty a POD-struktury (C++) V C++ jsou struktury zvláštním případem objektových typů. Standard však vymezuje dvě kategorie, které se mohou v jistých situacích chovat poněkud jinak – agregáty a POD-struktury. Podobné označení se používá i pro některé zvláštní případy unií (viz kapitola 4.4). ❚ A gregát je struktura, která nemá žádný uživatelem definovaný konstruktor, žádné chráněné (protected) nebo soukromé (private) nestatické datové složky, v její deklaraci není uveden předek (viz kapitola 11.6) a neobsahuje virtuální metody (viz kapitola 11.7.2). ❚ P OD-struktura je agregát, který navíc neobsahuje ■ nestatické datové složky typu třídní ukazatel (viz kapitola 11.15) ; ■ nestatické datové složky typu reference; ■ nestatické datové složky typu struktura nebo unie, která není POD; ■ pole některého z výše uvedených typů (kromě referencí); ■ uživatelem definovaný přiřazovací operátor; ■ uživatelem definovaný destruktor.
4.2.2 Složky struktur Každá proměnná strukturového typu je tvořena složkami uvedenými v deklaraci typu. Tyto složky (s výjimkou bitových polí, viz kapitola 4.3.4) můžeme použít všude, kde se očekává l-hodnota odpovídajícího typu. Přistupujeme k nim prostřednictvím jejich identifikátorů. ❚ S ložky struktury leží v paměti za sebou. Velikost struktury však může být větší než součet velikostí jednotlivých složek, neboť složky mohou být zarovnávány na sudé adresy, adresy dělitelné 4, 8 nebo 16. ❚ S ložkou struktury nemůže být proměnná právě deklarovaného typu (struktura nemůže obsahovat sama sebe). Může jí ale být ukazatel na strukturu právě deklarovaného typu. Potřebujeme-li pracovat s jednotlivými složkami proměnné typu struktura, použijeme operátor . (tečka) nebo -> (šipka, je složen ze znaků „minus“ a „menší než“). ❚ M áme-li k dispozici proměnnou typu struktura, použijeme operátor tečka. ❚ M áme-li k dispozici ukazatel na proměnnou typu struktura, použijeme operátor šipka. přístup ke složce struktury: • p roměnná . jméno_složky • u kazatel -> jméno_složky
PŘÍKLAD Vezměme strukturu osoba deklarovanou v předchozím příkladu a deklarujme proměnné osoba sef, *u = &sef;
Proměnná u obsahuje ukazatel na proměnnou sef. Složce psc proměnné sef přiřadíme hodnotu příkazem sef.psc = 10100; nebo
u -> psc = 10100;
72 Jazyky C a C++
4.2.3 Inicializace Proměnné typu struktura můžeme v deklaraci inicializovat. inicializátor struktury: • { seznam_inicializátorů_složek } • { seznam_inicializátorů_složek , } ❚ S eznam inicializátorů složek, to jsou čárkou oddělené výrazy, které se použijí k inicializaci jednotlivých složek. ❚ P rvní inicializátor odpovídá první složce (tj. složce zapsané v deklaraci jako první), druhý inicializátor odpovídá druhé složce atd. ❚ J e-li inicializátorů méně než složek, doplní se nulami (nebo hodnotami jiných typů odpovídajícími nule). Nesmí jich být více. ❚ J e-li složkou struktury opět struktura, použije se uvedené pravidlo rekurzivně. ❚ Z ávorky, jež uzavírají vnitřní inicializátory, lze vynechat. Překladač pak přiřadí inicializátory složkám podle pořadí. ❚ V C++ lze takto inicializovat pouze struktury, které jsou agregáty (viz kapitola 4.2.1). Strukturové typy, které nejsou agregáty, se považují za plnohodnotné objektové typy a k jejich inicializaci je třeba využít konstruktoru.
Inicializace struktur v C99 Jazyky C90 a C++ umožňují předepsat inicializaci buď pro všechny složky struktury, nebo jen pro prvních několik. Standard C99 přinesl možnost předepsat inicializaci jen vybraných složek, ne nutně počátečních. Slouží k tomu tzv. pojmenované inicializátory (designator): pojmenovaný inicializátor: • . jméno složky = výraz; Pojmenované inicializátory můžeme v jedné deklaraci kombinovat s nepojmenovanými.
PŘÍKLAD Předpokládáme, že platí deklarace struktury datum uvedená výše a deklarace struct zamestnanec { char jmeno[20]; datum nastup; int plat; };
Pak můžeme deklarovat proměnnou struct zamestnanec z = {"Jan Vopička", {1, 3, 2005}, 15000}; nebo
struct zamestnanec z = {"Jan Vopička", 1, 3, 2005, 15000}; V C99 můžeme také napsat
struct zamestnanec z = {.plat = 16000};
Ostatní složky se doplní nulami, resp. ukazatelem s hodnotou NULL.
Výčtové typy, struktury a unie 73
Toto je pouze náhled elektronické knihy. Zakoupení její plné verze je možné v elektronickém obchodě společnosti eReading.