![](img/tr.png)
- nový typ bool
- kvalifikátor const
- úvod do polí – inicializace a volání prvků
- zajímavosti s řetězci
- řádkově orientovaný vstup: getline() a get()
- míchání numerického a řetězového vstupu
- úkol č. 4
Tak nějak jsme se dostali ke 4. lekci. Co dnes probereme? Samé zajímavé věci! Naučíme se, jak zjistit velikost proměnné, počet znaků, vytvářet pole apod.
Nový typ bool
Naučíme se používat jeden další typ, nový v C++. Bool má hodnotu buď pravda (true), nebo nepravda (false), tedy 1 a 0. True je tedy jakákoliv nenulová hodnota, false nulová hodnota. Literály true a false se mohou konvertovat do typu int. True se interpretuje jako 1, false jako 0.
bool odpoved = true;
int odpov = false;
bool start = -100; // start se přiřadí true
bool stop= 0; // stop se přiřadí false
Kvalifikátor const
Slouží na vytváření symbolických konstant – použitím klíčového slova const. Předpokládejme, že chcete vytvořit konstantu na počet měsíců v roce:
const int MESICE = 12; // mesice je konstanta pro 12
Nyní můžete v programu místo 12 použít MESICE. Po inicializaci konstanty se nastaví hodnota a kompilátor vám následně nedovolí změnit hodnotu proměnné MESICE, protože proměnná je konstantní. Pokud byste chtěli do takovéto proměnné přiřadit poté hodnotu, zobrazí se chybové hlášení, že je překročena Lvalue. Je to to samé, jako byste chtěli přiřadit 5 do 6. (Lvalue je hodnota, jako například proměnná, která se vyskytuje na levé straně přiřazovacího operátoru.)
Jedna taková maličkost: názvy proměnných, které inicializujete jako konstantu, pište velkými písmeny, abyste si připomněli, že se jedná o konstantu. Obecný tvar pro vytvoření konstanty je:
const typ jméno = hodnota;
Při vytváření konstanty ji musíte i inicializovat!
Poznámka od xHire: Přecházíte-li na C++ z C a chystáte se použít #define
na definování konstanty, použijte místo toho const
.
Deklarace znamená vytvoření proměnné (např. int i;), definice znamená deklarace a následná inicializace proměnné v jednom příkazu (např. int i = 1;), samotná inicializace znamená přiřazení hodnoty proměnné – bez deklarace v témže příkazu (např. i = 1;).
Úvod do polí
Brzy dojdete k závěru, že potřebujete něco víc, než jsou jen jednoduché základní typy. C++ vám nabízí víc – odvozené typy. Nejdále dosažitelným typem je třída, lahůdka C++, ke které se dopracujeme. Ale C++ podporuje i několik skromnějších typů převzatých z C. Například pole – může obsahovat několik hodnot stejného typu. Určitý druh pole může obsahovat řetězec. Struktury mohou obsahovat několik proměnných různých typů a nakonec ukazatele, které říkají počítači, na jaké adrese (kde) jsou umístněna data. Postupně se ke všemu propracujeme.
Pole je datová forma, která může obsahovat několik hodnot stejného typu, například 60 hodnot typu int. Každá hodnota se ukládá do samostatného prvku pole a počítač je ukládá za sebou do paměti. Deklarace pole obsahuje tři věci:
- typ hodnoty, která má být uložena do každého prvku
- jméno pole
- počet prvků v poli
Pole se vytvářejí jako klasické proměnné, jen za ně přidáme hranaté závorky, které obsahují počet prvků (už jste se setkali s polem char):
typ jméno_pole [velikost_pole];
Např.:
short mesice[12]; //vytváří pole 12ti short
…vytváří pole, které se jmenuje mesice a má 12 prvků, z nichž každý může obsahovat hodnotu typu short. Každý prvek je vlastně samostatná proměnná. Velikost pole musíme hned nastavit. K prvkům pole můžeme přistupovat jednotlivě, číslování začíná od 0. Takže mesice[0] je prvním prvkem pole mesice a mesice[11] je posledním prvkem pole mesice.
Všimněte si, že index posledního prvku je o 1 menší než velikost pole. (Je to dáno právě tím, že číslování začíná od 0.)
Inicializace prvků
Jsou 2 možné způsoby:
Vytvoříme si pole hromada a inicializujeme jednotlivé prvky:
int hromada[3]; hromada[0] = 5; hromada[1] = 2; hromada[2] = 8;
- Vytvoříme si pole hromada a rovnou inicializujeme:
int hromada[3] = {5, 2, 8};
Volání prvků
Prvky voláme klasicky:
cout << hromada[0]; // vypíše obsah prvního prvku, tedy 5
cout << hromada; // vypíše adresu pole - tedy tak ne
Při vytváření polí můžeme nechat kompilátor počítat:
int mesice[] = {5, 4, 3, 2}; // nechá kompilátor vypočítat velikost pole
Můžete také inicializovat pole částečně:
int mesice[5] = {5, 4}; // inicializujete pouze první 2 prvky
Zbylé prvky jsou inicializovány automaticky na nulu, není tedy těžké je všechny inicializovt na nulu:
int mesice[5] = {0};
Zajímavosti s řetězci
Abychom zpřístupnili standardní knihovní f-ci strlen() na zjištění délky řetězce, musíme includovat cstring (string.h pro starší implementace), tedy:
#include // zpřístupní f-ci strlen()
Mezi závorky dejte proměnnou, u které chcete zjistit délku řetězce:
char jmeno[] = "Curo";
cout << "vase jmeno ma delku " << strlen(jmeno) << " znaky.";
Výpis:
vase jmeno ma delku 4 znaky
Dále se naučíme, jak zjistit velikost proměnné, použijeme k tomu f-ci sizeof. Například máme proměnnou vek:
int vek = 15;
cout << sizeof vek; // vypíše velikost proměnné vek
Nějaké další vychytávky
Podobně jako \n používáme v textu na výstup, můžeme použít další sekvence:
akce | sekvence |
nový řádek | \n |
horizontální tabulátor | \t |
vertikální tabulátor | \v |
návrat na předchozí znak | \b |
návrat vozu | \r |
zvuková výstraha | \a |
zpětné lomítko | \\ |
otazník | ? |
jednoduchá uvozovka | \' |
dvojitá uvozovka | \" |
Příklad:
cout << "Napis tvuj kod : _____\b\b\b\b\b";
Řádkově orientovaný vstup: getline() a get()
Abyste byly schopni zavádět celé věty místo jednotlivých slov, potřebujete pro vstup řetězce jiný přístup. Například f-ce getline() čte celý řádek a přitom používá znak nového řádku (Enter) na označení konce vstupu.
Tuto metodu využíváme použitím cin.getline() jako funkční volání. Má dva parametry. Jeden je jméno pole a druhý je počet znaků, které se mají přečíst. Je-li nastavena velikost na 20 znaků, f-ce přečte 19 znaků a dvacátý použije na dodání znaku null na konec (\0). F-ce ukončuje vstup, jakmile hranice překročí nastavený počet znaků nebo se stiskne Enter, záleží na tom, co přijde první.
Například, chcete-li uložit jméno do pole o velikosti 20 znaků s pomocí f-ce getline():
char jmeno[20];
cin.getline(jmeno, 20); // přečte 19 znaků a uloží
Přečte celý řádek do pole jmeno za předpokladu, že řádek obsahuje 19 nebo méně znaků.
#include
using namespace std;
int main()
{
char jmeno[20];
cout << "Zadejte vase jmeno:\n";
cin.getline(jmeno, 20); // čte celý řádek
cout << "vase jmeno je " << jmeno << ".\n";
cin.get();
cin.get();
return 0;
}
Teď zabrousíme trochu do praktičnosti. Když chcete změnit např. velikost pole jmeno či maximální počet přečtených znaků, vytvořte si konstantu, které přidělíte tento počet, a vždy, zvláště ve složitějších příkladech, stačí změnit hodnotu konstanty a tím pádem se změní vše, pomocí jednoho zásahu:
#include
using namespace std;
int main()
{
const int VEL = 20;
char jmeno[VEL];
cout << "Zadejte vase jmeno:\n";
cin.getline(jmeno, VEL); // čte celý řádek
cout << "vase jmeno je " << jmeno << ".\n";
return 0;
}
Co vlastně dělá onen cin.getline();? Čte znak nového řádku, tedy čeká na stisknutí Enteru.
Další variantou je cin.get(); s parametrem. Funguje úplně stejně jako cin.getline();.
Pokud používáte příkaz cin.get(); vícekrát, pište raději za něho příkaz cin.get(); bez parametru:
cin.get(jmeno, 20); // čte první řádek
cin.get(); // čte znak nového řádku
cin.get(jmeno2, 20); // čte druhý řádek
nebo
cin.get(jmeno, 20).get(); // zřetězení členských f-cí
Nepočítám s tím, že to hned pochopíte. Vysvětlení:
cin.get(jmeno, 20); // jmeno a 20 jsou paametry předávané f-ci cin.get();
Jediný rozdíl mezi cin.getline(); a cin.get(); je, že cin.get(); zanechává znak nového řádku ve vstupní frontě.
cin.get(jmeno, 20);
cin.get(jmeno2, 20); // problém
Protože první volání zanechává znak nového řádku ve vstupní frontě, tento znak je prvním znakem, který uvidí druhé volání. Tedy get(); dospívá k závěru, že dosáhla konce řádku aniž by nalezla něco k přečtení. Volání cin.get() (žádné parametry) čte samotný následující znak, i když je to znak nového řádku. Chce to vyzkoušet, abyste pochopili, jak to pracuje.
Proč tedy používáme cin.get(), když máme cin.getline()?
Starší implementace možná cin.getline(); nemají a také vás to nutí být pečlivější. Někdy v budoucnu vám ukážu, v čem je někdy cin.get(); lepší (k ošetření chyb), ale cin.getline(); – nejlepší volba pro čtení celého řádku.
Teď si představte, že jste vytvořili pole o velikosti 20 znaků a uživatel zadá 28 znaků, tedy více, než bylo alokováno. Tyto znaky přijdou do vstupní fronty, což by se zapsalo do následující proměnné, a to přece vůbec nechceme! Proto to můžeme ošetřit tím, že za cin.get(); s parametrem (ne za cin.getline();) napíšeme cin.clear();
cin.get(jmeno, 20);
cin.clear(); // vymaže znaky ve vstupní frontě
Míchání numerického a řetězového vstupu
Toto může zapříčinit problémy. Jak se jich vyvarovat?
#include
using namespace std;
int main()
{
cout << "V kterem roce byl vas dum postaven?\n";
int year;
cin >> year;
cout << "Jaka je jeho adresa?\n";
char address[80];
cin.getline(address, 80);
cout << "Rok vystavby: " << year << "\n";
cout << "Adresa: " << address << "\n";
cout << "Konec!\n";
cin.get();
return 0;
}
Výpis:
V kterem roce byl vas dum postaven?
1966
Jaka je jeho adresa?
Rok vystavby: 1966
Adresa:
Konec!
Nikdy nedostanete příležitost zadat adresu. Problém spočívá v tom, že když cin přečte rok, tak zanechá znak nového řádku generovaný klávesou Enter ve vstupní frontě. Potom cin.getline() přečte nový řádek jako prázdný a přiřadí prázdný řetězec do pole address. Oprava spočívá v přečtení a odhození znaku nového řádku před čtením adresy, např. použitím cin.get(); bez parametru.
Trošku lidštěji
cin zanechává vždy ve vstupní frontě Enter, který automaticky jde do dalšího vstupu. Pokud tedy je cin >> vek;, uloží hodnotu do proměnné vek a nechá Enter do dalšího vstupu. Hned následující cin >> vyska; nejprve dostane Enter z předchozího vstupu, ale (pokud jste si všimli, cin vás jen tak nepustí, pokud jen zmáčknete Enter, aniž byste zadali hodnotu) ten přeskočí a čeká na hodnotu. Zadáme 8 a stiskneme Enter, ten jde zas do vstupní fronty. cin.getline() si s tím už ovšem neporadí, protože řetězec může být prázdný a přijme tedy Enter a tím i ukončí vstup, což právě přeskočí vstup do proměnné address. Jak to tedy opravit?To je úkol pro vás! Není to vůbec těžké, proto ještě přidáme:
Úkol č. 4
- Opravit kód, aby fungoval v plné míře.
- Sestavte program, který se v tomto pořadí zeptá na následující položky:
- Na celé jméno (řetězec se uloží do jednoho pole), otázka bude vypadat přesně takhle (včetně češtiny!):
„Vaše jméno prosím: “. - Na váš věk.
- Na pět čísel, která se postupně uloží do dalšího pole; tato čísla sečte a ukáže výsledek.
- Na celé jméno (řetězec se uloží do jednoho pole), otázka bude vypadat přesně takhle (včetně češtiny!):
- Program vypíše jméno pozpátku (help – cykly).
- Vše bude hezky provedeno, tedy odřádkováno, odmezerováno a s použitím znaků z tabulky, co jste dělali v testu, tedy nějaké ohraničení či čáry apod.
Věřím, že to budete mít hned :o).