- Dodatek k cyklu FOR
- Operátor „,“ (čárka)
- Kopírování a spojování řetězců, jejich velikost a délka
- Alias jména
- Časová prodleva
- Dvourozměrné pole
- Operátor „?“
- Příkaz SWITCH
- Příkazy break a continue
- Příkaz goto
- Knihovna cctype
- Symbolické konstanty climits
- Chyby, které možná děláte
- Vícesouborové projekty
- Úkol
Lekce 8.
Dnes si dáme odpočinek od f-cí a podobných témat. Tato lekce bude víc odpočinková, ale o to víc zábavnější! Poučíme se o věcech, na které jsem zapomněl upozornit, nebo je odložil. Rozhodně jsou to důležité příkazy, se kterými obohatíte každý program.
8.1 – Dodatek k cyklu FOR
Klasicky vypadá cyklus for takto:
for (i=0 ; i<5 ; i++)
příkazy;
…ale pokud chceme vynechat některou z podmínek (například proto, že již před začátkem cyklu je hodnota někde stanovená), není nic snazšího, než danou podmínku vynechat (ale středník tam přesto musí být!):
for ( ; i<5 ; i++)
příkazy;
8.11 – Operátor „,“ (čárka)
Operátor čárka nám dovoluje provést více podmínek, změn, deklarací atp.:
for (j=5 , i=4 ; j<i ; i-- , j++)
příkazy;
Myslím, že je to dostatečně pochopitelné. Operátor čárka:
- má nejnižší prioritu
- čte zleva doprava
- když má na výběr, použije druhý výraz
int kocka = 17, 145; // nastaví proměnnou kočka na 17 (čísla 145 si nevšímá)
int kocka = (17, 145); // nastaví proměnnou kočka na 145
8.12 – Kopírování a spojování řetězců, jejich velikost a délka
V celém bloku 8.12 budeme muset na začátek programu vložit tuto direktivu:
#include <cstring>
Kopírování jednoho řetězce do druhého:
strcpy(retezec1, retezec2); //retezec1 je příjemcem, z retezec2 se kopíruje
Z toho plyne, že obsah řetězce retezec2 nakopíruje svůj obsah do řetězce retezec1:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char dotaz[20];
cin >> dotaz;
char dotaz1[20];
strcpy(dotaz1, dotaz);
cout << dotaz << endl;
cout << dotaz1;
cin.get();
cin.get();
return 0;
}
Pozor! U všech těchto funkcí (strxxx) musíme zabezpečit místo pro kopírovaný či spojovaný řetězec! Kompilátor nekontroluje, zda se obsah řetězce „vleze“ do cílového řetězce – zda je tedy místo pro něj.
Ovšem může nastat situace, že chceme zkopírovat pouze několik znaků. V tom případě použijeme podobnou f-ci:
strncpy(dotaz1, dotaz, 20);
…způsobí, že se do řetězce dotaz1 zkopíruje obsah řetězce dotaz, ale nejvýše 20 znaků!
Příklad:
#include
<iostream>
#include <cstring>
using namespace std;
int main()
{
cout << "zadejte text ke zkopirovani (min. 5 znaku)";
char dotaz[20];
cin >> dotaz;
char dotaz1[20];
strncpy(dotaz1, dotaz, 3);
cout << dotaz << endl;
cout << dotaz1;
cin.get();
cin.get();
return 0;
}
Vezme řetězec dotaz a nakopíruje jeho první 3 znaky do proměnné dotaz1.
Spojování řetězců
Spojování probíhá podobně, jako f-ce na kopírování:
strcat(retezec_cilovy, retezec_zdrojovy);
Řetězec retezec_cilovy obsahuje nyní jak obsah sama sebe, tak i obsah řetězce retezec_zdrojovy:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char dotaz[50];
cin >> dotaz;
char dotaz1[20];
cin >> dotaz1;
strcat(dotaz, dotaz1);
cout << dotaz << endl;
cin.get();
cin.get();
return 0;
}
Zjištění délky řetězce
Na zjištění délky řetězce nám poslouží příkaz:
strlen(jmeno_promenne);
Ten nám zjistí počet znaků, které obsahuje daná proměnná.
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char dotaz[50];
cout << "zadej slovo :";
cin >> dotaz;
cout << "\nPromenna obsahuje " <<
strlen(dotaz) << " znaku.";
cin.get();
cin.get();
return 0;
}
Zjištění velikosti řetězce
Slouží nám ke zjištění velikosti proměnné v bajtech.
sizeof nazev_promenne;
Příklad:
#include <iostream>
using namespace std;
int main()
{
char dotaz[50];
cout << "zadej slovo :";
cin >> dotaz;
cout << "\nPromenna ma velikost " << sizeof dotaz << " bajtu.";
cin.get();
cin.get();
return 0;
}
8.13 Alias jména
Vytváří stejný název proměnné, ale jiného typu. Pokud tedy chceme použít 2× stejný název, ale pokaždé jiný typ, použijeme:
typedef jméno_typu alias_jmeno;
…takže například pokud máme proměnnou typu int a chceme tutéž proměnnou typu long:
int slovo;
typedef long slovo;
8.14 – Časová prodleva
Pokud chceme pracovat s časem, slouží k tomu několik dalších příkazů. Ale k jejich zpřístupnění potřebujeme další direktivu:
#include <ctime>
Nejprve výpis:
clock( ); // tento příkaz označuje aktuální čas, když je volán, nastaví se na nynější čas
clock_t // je to typ (stejně jako int, long...) proměnné orientované na uchování času
CLOCKS_PER_SEC // musí být psáno velkými písmeny; díky tomuto systém definuje konstantu = počet systémových jednotek za sekundu (neboli synchronizuje rychlost procesoru a přepočítává z ní sekundy)
Starší hry právě byly někdy nehratelné na nynějších strojích, poněvadž neobsahovaly tento přepočet, a byly tak závislé na času procesoru (počtu operací). Čím silnější PC, tím rychlejší hra (uspěchanější).
clock_t doba = 5 * CLOCKS_PER_SEC;
//clock_t - typ
//doba - proměnná
//5 - počet sekund
//CLOCKS_PER_SEC - převádí rychlost procesoru na sekundy
clock_t start = clock();
//clock_t - typ
//start - proměnná
//clock() - nynější čas
Poté si již stačí určit podmínku:
while(clock() - start < doba)
; // středník místo těla cyklu
clock() - start je vlastně čas teď - čas při vytvoření proměnné start
Jak plyne právě z této řádky, start se musí inicializovat těsně před cyklem, jinak se krátí čas a může se stát, že na básněnou časovou prodlevu ani nedojde.
Příklad:
#include <iostream>
#include <ctime>
using namespace std;
int main()
{
clock_t doba = 2* CLOCKS_PER_SEC;
clock_t start = clock();
for (int i=0; i< 20000; i++)
cout << "ahoj";
while (clock() - start < doba)
;
return 0;
}
Na přesně dvousekundovou časovou prodlevu nikdy nedojde, protože provedení cyklu zabere určitý čas. Pokud bychom tedy chtěli onu dvousekundovou prodlevu, náprava je jednoduchá:
#include <iostream>
#include <ctime>
using namespace std;
int main()
{
clock_t doba = 2* CLOCKS_PER_SEC;
for (int i=0; i< 20000; i++)
cout << "ahoj";
clock_t start = clock();
while (clock() - start < doba)
;
return 0;
}
8.2 – Dvourozměrné pole
…neboli 2D pole. Je to to samé, jako u klasických polí, jen delší.
int pole[4] = { 1, 2, 3, 4 }; //jednorozměrné pole (klasické)
int pole[4][5]; //dvou rozměrné pole, má 4 pole, každé 5 proměnných
Vytváření 2D pole
int pole[4][5];
Vytvoří 2D pole, které obsahuje 4 klasické pole a každé klasické pole má 4 prvky.
Inicializace 2D pole
int pole[0] = { 1, 2, 6, 4, 5 };
int pole[1] = { 8, 2, 6, 5, 9 };
...
…nebo…
int pole[4][5] =
{
{ 1, 2, 6, 4, 5 }, // hodnoty pro pole[0]
{ 8, 2, 6, 5, 9 }, // hodnoty pro pole[1]
...
};
Volání prvku
pole[1][3];
- pole - proměnná
- [1] - číslo pole
- [3] - číslo prvku
8.3 – Operátor „?“
Obecně: výraz1 ? výraz2 : výraz3
int c = a > b ? a : b;
Je-li a>b, pak přiřaď proměnné c hodnotu a, pokud ne (tedy a<b), pak přiřaď hodnotu b. Pokud je tedy podmínka pravdivá, proměnná c dostane hodnotu proměnné a, pokud podmínka pravdivá není, tak dostane hodnotu proměnné b.
Příklad :
#include <iostream>
using namespace std;
int main()
{
int a, b;
cout << "Zadejte dve cela cisla: ";
cin >> a >> b;
cout << "Vetsi z cisel " << a << " a " << b;
int c = a > b ? a : b; // c = a jestliže a > b, jinak c = b
cout << " je " << c << "\n";
return 0;
}
8.4 – Příkaz SWITCH
Pokud chcete vytvořit například menu, můžete ho udělat pomocí sekvencí if else if else, ale příkaz switch pracuje mnohem snadněji.
Obecně:
switch (celočíselný_výraz)
{
case 1 : příkaz(y)
case 2 : příkaz(y)
...
default : příkaz(y)
}
Příkaz switch v C++ funguje jako směrovací zařízení, které říká počítači, jaký řádek programového kódu má dále provést. Při čtení switch skáče program na řádek, který je označen hodnotou odpovídající hodnotě celočíselný_výraz. Například má-li tento celočíselný_výraz hodnotu 4, pak jde na řádek, který má číslo 4. Hodnota celočíselný_výraz musí být výrazem, který produkuje číselnou hodnotu. Také označení (1, 2…) musí být konstantním výrazem. Nejčastěji se používají jednoduché konstanty typu int nebo char (1,2,3…, e,f,g,h…) nebo emulátory. Jestliže celočíselný_výraz neodpovídá žádnému označení (1,2,3…), přejde na řádek default, které ale není povinné. Vynecháte-li ho a nenajde-li žádnou shodu, jednoduše opustí příkaz switch a pokračuje dále. Pokud ale default použijete, automaticky za vás opraví možnosti, jako když uživatel zadá číslo, které není nabízeno. V tom případě se switch opakuje.
Switch se velmi liší od podobných příkazů v jiných jazycích (např. Pascal). Jakmile přeskočí na určitý řádek ve switch, potom provádí všechny příkazy, které za ním následují, dokud ho nějak nepřesměrujete. Vykonávání se automaticky NEZASTAVÍ na dalším case. Abyste zastavili na konci určité skupiny příkazů, musíte použít příkaz break. To způsobí opuštění příkazu switch.
Ukážeme si příklad jednoduché implementace break do switch:
#include <iostream>
using namespace std;
void showmenu(); // prototypy funkcí
void report();
void comfort();
int main()
{
showmenu();
int choice;
cin >> choice;
while (choice != 5)
{
switch(choice)
{
case 1 : cout << "\a\n";
break;
case 2 : report();
break;
case 3 : cout << "Vedouci byl pritomen cely den.\n";
break;
case 4 : comfort();
break;
default : cout << "To neni volba.\n";
}
showmenu();
cin >> choice;
}
cout << "Sbohem!\n";
return 0;
}
void showmenu()
{
cout << "Prosim, zadejte 1, 2, 3, 4 nebo 5:\n"
"1) poplach 2) zprava\n"
"3) vymluva 4) uklidneni\n"
"5) ukonceni\n";
}
void report()
{
cout << "To byl vytecny tyden pro obchod.\n"
"Trzby vzrostly o 120%. Vydaje poklesly o 35%.\n";
}
void comfort()
{
cout << "Vasi zamestnanci si mysli, ze jste nejlepsi vykonny reditel\n"
"v prumyslu. Spravni rada si mysli,\n"
"ze jste nejlepsi vykonny reditel v prumyslu.\n";
}
Výpis:
Prosim, zadejte 1, 2, 3, 4 nebo 5:
1) poplach 2) zprava
3) vymluva 4) uklidneni
5) ukonceni
4
Vasi zamestnanci si mysli, ze jste nejlepsi vykonny reditel
v prumyslu. Spravni rada si mysli,
ze jste nejlepsi vykonny reditel v prumyslu.
Prosim, zadejte 1, 2, 3, 4 nebo 5:
1) poplach 2) zprava
3) vymluva 4) uklidneni
5) ukonceni
2
To byl vytecny tyden pro obchod.
Trzby vzrostly o 120%. Vydaje poklesly o 35%.
Prosim, zadejte 1, 2, 3, 4 nebo 5:
1) poplach 2) zprava
3) vymluva 4) uklidneni
5) ukonceni
6
To neni volba.
Prosim, zadejte 1, 2, 3, 4 nebo 5:
1) poplach 2) zprava
3) vymluva 4) uklidneni
5) ukonceni
5
Sbohem!
Cyklus while se ukončí, jakmile uživatel zavede 5. Zavedení čísel 1-4 aktivuje odpovídající volbu ze seznamu switch a zavedení čísla 6 spustí standardní příkazy (následující za příkazem switch). Jak bylo poznamenáno výše, program potřebuje příkazy break, aby omezil vykonávání programu na určitou část příkazu switch. Abyste viděli, že to tak opravdu funguje, odstraňte z předchozího programu příkazy break a sledujte. Zjistíte, že při zadání například čísla 2 program provede všechny operace začleněné k 2, 3, 4 a standardnímu case default. Ptáte se, proč to tak dělá? Takový způsob vykonávání může být totiž užitečný. Ukážeme si příklad, kde:
char vyber;
cin >> vyber;
while (vyber != 'Q' && vyber != 'q' )
{
switch(vyber)
{
case 'a' :
case 'A': cout << "\a\n";
break;
case 'r' :
case 'R': cout << "ok";
...
}
}
Protože bezprostředně za case 'a' neexistuje žádný break, vykonávání programu přechází na následující řádek → příkaz case 'A'. Jak vidíte, můžete takto ošetřit používání velkých a malých písmen a další věci. Nebo také pro více voleb udělat jeden příkaz.
Switch tedy požíváme na větší větvení (3 a více položek), kdežto u menších použijeme if else. Dále také není switch přizpůsoben na zacházení s rozsahy. Každé označení case příkazu switch musí být jediná hodnota. Pokud tedy chceme rozsah, použijeme if else:
if (vek > 17 && age < 35)
index = 0;
else if (age >= 35 && age < 50)
index = 1;
else if (age >= 50 && age < 65)
index = 2;
else
index = 3;
8.41 – Příkazy break a continue
Příkazy break a continue umožňují programu přeskočit část programového kódu. Break můžeme použít v příkazu switch nebo v kterémkoliv cyklu. Break způsobí, že se okamžitě opouští příkaz switch nebo cyklus a pokračuje se na příkazech následujících za cyklem či switch. Continue se používá v cyklech a způsobuje, že se přeskočí zbytek těla cyklu a znova ho celý opakuje.
#include <iostream>
using namespace std;
const int ArSize = 80;
int main()
{
char line[ArSize];
int spaces = 0;
cout << "Zadejte textovy radek:\n";
cin.get(line, ArSize);
for (int i = 0; line[i] != '\0'; i++)
{
cout << line[i]; // zobrazi znak
if (line[i] == '.') // ukončení, když je tečka
break;
if (line[i] != ' ') // přeskočení zbytku příkazů v cyklu
continue;
spaces++;
}
cout << "\n" << spaces << " mezer\n";
return 0;
}
8.42 – Příkaz goto
C++, podobně jako C, basic a mnoho dalších jazyků má příkaz goto.
goto paris
…znamená přeskočení na místo, které nese paris:
char(ch);
cin >> ch;
if (ch == 'P')
goto paris;
cout << ...
...
paris: cout << "Prave jsi udelal skok.";
Neznamená to tedy nic jiného, než skákání na určitá místa v programu. Ve většině případů je ale použití příkazu goto nevhodné. Měli byste raději použít strukturované řídící příkazy jako if else, switch, continue…
8.5 – Knihovna cctype
C++ zdědil z C příhodný balík f-cí, které souvisí se znaky, jejichž proto prototypy jsou v hlavičkovém souboru cctype, které zjednodušují takové úkoly, jako je určení, zda je znak velkým písmenem, číslicí či interpunkčním znaménkem. Použití těchto f-cí je vhodnější než použití operátorů A a NEBO. Například tady vidíte, jak byste mohli použít A a NEBO na otestování, zda je znak ch alfabetickým znakem:
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
Porovnejte to s použitím isalpha():
if (isalpha(ch))
Nejenže je to kratší, ale taky obecnější.
Jméno funkce | Návratová hodnota |
isalnum() | True, je-li parametr alfanumerický, písmeno, nebo číslice |
isalpha() | True, je-li parametr alfabetický |
iscntrl() | True, je-li parametr řídící znak |
isdigit() | True, je-li parametr desítková číslice (0-9) |
isgraph() | True, je-li parametr libovolný tisknutelný znak, než mezera |
islower() | True, je-li parametr malé písmeno |
isprint() | True, je-li parametr libovolný tisknutelný znak včetně mezery |
ispunct() | True, je-li parametr interpunkční znak |
isspace() | True, je-li parametr standardním oddělovacím znakem, to jest mezera, posun formuláře, nový řádek, návrat vozu, horizontální a vertikální tabulátor |
isupper() | True, je-li parametr velké písmeno |
isxdigit() | True, je-li parametr hexadecimální číselný znak (0-9, a-f, A-F) |
tolower() | Je-li parametrem velké písmeno, navrací malé písmeno onoho znaku jinak navrací nezměněný parametr |
toupper() | Je-li parametrem malé písmeno, navrací velké písmeno onoho znaku jinak navrací nezměněný parametr |
Příklad:
#include <iostream>
#include <cctype> // prototypy znakových funkcí
using namespace std;
int main()
{
cout << "Zadejte text pro analyzu a znakem @"
" ukoncete vstup.\n";
char ch;
int whitespace = 0;
int digits = 0;
int chars = 0;
int punct = 0;
int others = 0;
cin.get(ch); // získá první znak
while(ch != '@') // test ukončení vstupu
{
if(isalpha(ch)) // je to alfabetický znak?
chars++;
else if(isspace(ch)) // je to bílý znak?
whitespace++;
else if(isdigit(ch)) // je to číslice?
digits++;
else if(ispunct(ch)) // je to znak interpunkce?
punct++;
else
others++;
cin.get(ch); // získá další znak
}
cout << chars << " pismen, "
<< whitespace << " bilych znaku, "
<< digits << " cislic, "
<< punct << " znaku interpunkce, "
<< others << " ostatnich.\n";
return 0;
}
8.6 – Symbolické konstanty climits
Hlavičkový soubor climits definuje symbolické konstanty na zachycení typů mezí. Např. INT_MAX představuje největší hodnotu, kterou může typ int obsahovat.
CHAR_BIT | počet bitů v char |
CHAR_MAX | maximální hodnota char |
CHAR_MIN | minimální hodnota char |
SCHAR_MAX | maximální hodnota signed char |
SCHAR_MIN | minimální hodnota signed char |
UCHAR_MAX | maximální hodnota unsigned char |
SHRT_MAX | maximální hodnota short |
SHRT_MIN | minimální hodnota short |
USHRT_MAX | maximální hodnota unsigned short |
INT_MAX | maximální hodnota int |
INT_MIN | minimální hodnota int |
UINT_MAX | maximální hodnota unsigned int |
LONG_MAX | maximální hodnota long |
LONG_MIN | minimální hodnota long |
ULONG_MAX | maximální hodnota unsigned long |
Inicializace spojuje přiřazení s deklarací. Např.:
#inlude <climits>
...
int n_int = INT_MAX;
…deklaruje proměnnou n_int a nastaví jí největší možnou hodnotu typu int. Soubor climits obsahuje řádky, které jsou podobné následujícímu:
#define INT_MAX 32767
Zde je #define (podobně jako #include) direktiva preprocesoru. Doslova říká: „Prohlédni si program, zda tam nejsou instance INT_MAX a nahraď každý výskyt hodnotou 32767.“ Takže direktiva #define pracuje podobně jako příkaz globálního vyhledávání a nahrazování v editoru nebo v textovém editoru. Až poté, co se tyto změny uskuteční, se program zkompiluje. Takto si můžete vytvářet i vlastní symbolické konstanty. Ovšem direktiva #define je přežitek z C. C++ má lepší řešení: const. Pokud ovšem chcete použít #define, není to nic těžkého.
Příklad:
#include <iostream>
using namespace std;
#define ZERO 0 // vytváří symbol ZERO pro hodnotu 0
#include <climits> // definuje INT_MAX jako největší celočíselnou hodnotu
int main()
{
short sam = SHRT_MAX; // inicializuje proměnnou na maximální hodnotu
unsigned short sue = sam; // v pořádku, je-li proměnná sam již definována
cout << "Sam ma ulozeno " << sam << " dolaru a Sue " << sue;
cout << " dolaru.\nNa kazdy ucet pridame 1 dolar.\nNyni ";
sam = sam + 1;
sue = sue + 1;
cout << "ma Sam ulozeno " << sam << " dolaru a Sue " << sue;
cout << " dolaru.\nChudak Sam!\n";
sam = ZERO;
sue = ZERO;
cout << "Sam ma ulozeno " << sam << " dolaru a Sue " << sue;
cout << " dolaru.\n";
cout << "Z kazdeho uctu vezmeme 1 dolar.\nNyni ";
sam = sam - 1;
sue = sue - 1;
cout << "ma Sam ulozeno " << sam << " dolaru a Sue " << sue;
cout << " dolaru.\nStastna Sue!\n";
return 0;
}
8.7 – Chyby, které možná děláte
Nepleťte si testovací operátor je rovno (==) s přiřazovacím operátorem (=).
musik == 4 // porovnání
…se zeptá: „Je musik rovno 4?“ Výraz má hodnotu true, nebo false.
musik = 4 // přiřazení
…přiřadí hodnotu 4 do musik.
Příklad:
#include <iostream>
using namespace std;
int main()
{
int quizscores[10] = { 20, 20, 20, 20, 20, 19, 20, 18, 20, 20};
cout << "Provedeno spravne:\n";
int i;
for (i = 0; quizscores[i] == 20; i++) //porovnani
cout << "kviz " << i << " je 20\n";
cout << "Provedeno nebezpecne spatne:\n";
for (i = 0; quizscores[i] = 20; i++) // prirazeni
cout << "kviz " << i << " je 20\n";
return 0;
}
8.71 – Vícesouborové projekty
Pokud chceme vložit knihovnu do našeho programu, napíšeme:
#include <iostream>
Pokud chceme importovat náš vlastní soubor, postupujeme takto:
a) Vytvoříme např. soubor pro import češtiny (resp. kódy znaků) cestina.h. Náš soubor musí mít vždy příponu .h. Do něho napíšeme text (výpis znaků jako třeba char cc=97 atd.) a uložíme.
b) Do našeho hlavního programu vepíšeme pod #include <iostream> řádku:
#include "cestina.h"
//soubor cestina.h se musí nacházet přímo v rootu programu, kde ho máme uložený, pokud tam není, kompilátor začne hledat ve standardním umístnění knihoven.
…nebo…
#include "C:/cpp/cestina.h"
//přímá adresa na náš soubor
…nebo…
#include <cestina.h>
//cestina.h se musí nacházet přímo v seznamu, kde se nacházejí zbylé (iostream, cctype, cmath…)
8.8 – Úkol
Pro dnešek bude jiný úkol. Poněvadž tato lekce byla spíše doplňující, nicméně myslím, že neméně důležitá a hlavně zajímavá, bude úkol následující:
Vytvořte co nejlepší program || hru, ale POUZE s využitím toho, co jsme se naučili pomocí těchto kurzů! Hodnotí se originalita, grafika, čeština, nápad a samozřejmě komentáře a správnost programování, dále bude velký důraz kladen na syntaxi. Žádné dlouhé nepřehledné kódy, ale přesné, přehledné a napsané co nejúspornějším a nejefektivnějším způsobem (tedy pokud mám rozsáhlé větvení, žádné if else, ale switch). Nejlepší práce vyhodnotím(e) a vystavím(e).