Upravená učebnice C a C++

Transkript

Upravená učebnice C a C++
Učebnice Programování V C/C++
PROGRAMOVÁNÍ V C/C++ - 1. DÍL................................................................................................................. 3
PŘEDPOKLADY .............................................................................................................................................. 3
CO JE TO PROGRAMOVÁNÍ ............................................................................................................................... 3
"KDE SEŽENU TEN PROGRAM?" ........................................................................................................................ 3
INSTALACE A BLIŽŠÍ SEZNÁMENÍ ........................................................................................................................ 3
CO " TEN PROGRAM" DĚLÁ ............................................................................................................................... 3
NA ZÁVĚR PRVNÍHO DÍLU ................................................................................................................................. 3
PROGRAMOVÁNÍ V C/C++ - 2. DÍL................................................................................................................. 4
NÁŠ PRVNÍ PROGRAM ..................................................................................................................................... 4
KOMENTÁŘE ................................................................................................................................................. 4
ŘÁDKY CO ZAČÍNAJÍ # ..................................................................................................................................... 4
VLASTNÍ PROGRAM ........................................................................................................................................ 4
J EŠTĚ TROCHU HYGIENY ................................................................................................................................. 5
CO SI ZAPAMATOVAT ...................................................................................................................................... 5
PROGRAMOVÁNÍ V C/C++ 3. DÍL................................................................................................................... 6
PROMĚNNÉ................................................................................................................................................... 6
ZÁKLADNÍ TYPY PROMĚNNÝCH ......................................................................................................................... 6
JAK SI " UDĚLÁME" TAKOVOU PROMĚNNOU .......................................................................................................... 7
KDE DEKLAROVAT PROMĚNNOU?...................................................................................................................... 7
CO MŮŽEME S ČÍSELNOU PROMĚNNOU DĚLAT? ................................................................................................... 7
D NEŠNÍ JEDNODUCHÝ PROGRAM ...................................................................................................................... 8
PLATNOST PROMĚNNÝCH ................................................................................................................................ 8
DO PŘÍŠTĚ.................................................................................................................................................... 8
PROGRAMOVÁNÍ V C/C++ 4. DÍL................................................................................................................... 9
BLOK KÓDU .................................................................................................................................................. 9
VĚTVENÍ PROGRAMU ...................................................................................................................................... 9
CO KDYŽ POTŘEBUJI VÍCE PODMÍNEK: .............................................................................................................. 11
DALŠÍ KOUZELNÉ SLOVO: ELSE ....................................................................................................................... 11
ZKUŠEBNÍ PROGRAM .................................................................................................................................... 11
DODATEK O PLATNOSTI PROMĚNNÝCH............................................................................................................. 12
PROGRAMOVÁNÍ V C/C++ 1. BONUS .......................................................................................................... 13
PRAVIDLA ................................................................................................................................................... 13
JAK NA TO?................................................................................................................................................. 13
PLÁN ......................................................................................................................................................... 13
PŘÍPRAVA NA SMYČKU .................................................................................................................................. 13
SMYČKA ..................................................................................................................................................... 13
KÓD .......................................................................................................................................................... 13
PROGRAMOVÁNÍ V C/C++ 5. DÍL................................................................................................................. 15
WHILE ...................................................................................................................................................... 15
VARIACE NA WHILE....................................................................................................................................... 15
SMYČKA FOR .............................................................................................................................................. 15
U ČÍME SE ČÍST ............................................................................................................................................ 16
SHRNUTÍ .................................................................................................................................................... 16
PROGRAMOVÁNÍ V C/C++ 6. DÍL................................................................................................................. 17
VYHODNOCOVÁNÍ VÝRAZŮ ............................................................................................................................. 17
L ÍNÉ VYHODNOCOVÁNÍ.................................................................................................................................. 17
FUNKCE ..................................................................................................................................................... 18
V PRAXI...................................................................................................................................................... 18
PROGRAMOVÁNÍ V C/C++ 7. DÍL................................................................................................................. 21
D NEŠNÍ TÉMA .............................................................................................................................................. 21
PREPROCESOR A JEHO DIREKTIVY .................................................................................................................. 21
# INCLUDE< SOUBOR.H> ................................................................................................................................. 21
# INCLUDE"SOUBOR.H" .................................................................................................................................. 21
# DEFINE JMENO HODNOTA ....................................................................................................................... 21
DALŠÍ DIREKTIVY ......................................................................................................................................... 22
N ĚKTERÉ DALŠÍ OPERÁTORY ......................................................................................................................... 22
O PERÁTORY -- A ++ ..................................................................................................................................... 22
PROGRAM .................................................................................................................................................. 23
PŘÍŠTĚ ...................................................................................................................................................... 23
PROGRAMOVÁNÍ V C/C++ 8. DÍL................................................................................................................. 24
POINTERY .................................................................................................................................................. 24
JAK VYPADÁ PAMĚŤ ...................................................................................................................................... 24
PROMĚNNÉ A PAMĚŤ .................................................................................................................................... 24
STACK A HEAP............................................................................................................................................. 24
POINTERY .................................................................................................................................................. 24
JAK SE DEKLARUJE POINTER .......................................................................................................................... 25
KAM MOHOU POINTERY UKAZOVAT .................................................................................................................. 25
SHRNUTÍ .................................................................................................................................................... 25
PROGRAMOVÁNÍ V C/C++ 9. DÍL................................................................................................................. 26
CO JE TO ALOKACE ...................................................................................................................................... 26
JAK SE ALOKUJE .......................................................................................................................................... 26
D EALOKACE................................................................................................................................................ 26
PRÁCE S POINTERY ...................................................................................................................................... 26
NULL A CO JE TAM, KDYŽ TAM NIC NENÍ?? ....................................................................................................... 27
KDYŽ POINTER UKAZUJE NA STACK ................................................................................................................. 27
SHRNUTÍ .................................................................................................................................................... 27
SLUŠNÉ CHOVÁNÍ ........................................................................................................................................ 27
CVIČENÍ ..................................................................................................................................................... 28
Programování v C/C++ - 1. díl
Konečně tu máme i něco pro opravdové programátory. Novou rubriku programování otevírá série článků o
jazyce C (a později C++). Nepředpokládá žádné dřívější zkušenosti s programováním, přesto je případná znalost
php díky velké syntaktické podobnosti výhodou (přestože historicky je původcem této syntaxe právě jazyk C).
Vítejte na začátku prvního dílu. Pokud čtete tento článek, chcete se nejspíš naučit programovat. My se v tomto
seriálu budeme učit programovat v jazyce C, což je docela vhodný začátek i pokud se rozhodnete
s programováním pokračovat později, třeba už profesionálně. Rád bych upozornil, že řada informací je
nekompletních, jde mi hlavně o to, aby se začátečníci orientovali.
Předpoklady
Předpokladem pro tento seriál je základní znalost počítačů (co je to soubor, spustitelný soubor atp.) a
matematiky (výraz, neznámá).
Co je to programování
Pokud si nejste jisti tím, co je to programování (vzpomínám si na jednoho známého jak říká "ja stejně vůbec
nechápu jako jak se to dělá, že ten počítač hraje hokej"), není se za co stydět. Program je vlastně takový seznam
činností (program ;)), které má počítač provést. Taková kuchařka. Vezmi hrnec, dej do něj mléko, začni vařit, přidej
puding, dokud není husté tak míchej, přestaň vařit, rozlévej do mističek. Jednoduché!
"Kde seženu ten program?"
Tradiční otázka začátečníků je "kde seženu ten program co se to v něm dělá". Ten program co se to v něm dělá
je vlastně něco jako textový editor, kam píšete svůj program. Co se týče freewarových programů, doporučuji DevC++ ( stáhnout). Pokud se Vám nějakým způsobem dostalo do ruky Visual C++ nebo Borland C/C++ pro DOS,
mohu také doporučit. Já se budu soustředit na Dev-C++, protože je dostupné Vám všem zdarma a bude pro
většinu také první volbou.
Instalace a bližší seznámení
Instalace by měla být bezproblémová. Vybrat adresář zvládnete určitě sami, volby můžete nechat zaškrtnuté
všechny.
Po instalaci si založíme nový projekt. V hlavním menu zvolíme Nový->Projekt, za typ zvolíme konzolovou
aplikaci/console application, pojmenujeme si ho a uložíme. Některé verze vygenerují už připravený základní
zdrojový kód, některé ne (potom bude dobré přidat do projektu nějaký soubor přes Projekt->Nový soubor). Soubory
v projektu se zpravidla ukládají s koncovkou .c nebo .cpp.
Pro ty z vás, kteří se ještě nezorientovali, do budoucna se můžeme dostat do situace, že pro projekt budeme
mít i více zdrojových kódů (souborů), proto tedy existují projekty - aby nám umožnili pohodlně spravovat zdrojové
soubory.
Co "ten program" dělá
Ten program je vlastně takový balíček více různých programů. První z nich Vám slouží jako editor a nazývá se
vývojové prostředí. Dalším důležitým programem je tzv. compiler, což je program který přeloží program z naší
lidské formy do toho, čemu rozumí počítač. Aby toho nebylo málo, je často potřeba více těchto překladů compileru
dát dohromady, aby nám z jednotlivých kapitol vznikla celá kniha (např. tedy výsledný spustitelný soubor) - o to se
stará linker.
Nemusíte se ale všech těchto pojmů bát. Vše lze velmi jednoduše ovládat z vývojového prostředí stiskem
několika kláves.
Na závěr prvního dílu
Prokousali jsme se tím nezáživným a příště už můžeme začít s vlastním programováním. Napíšeme si
jednoduchý program, který nás dovede pozdravit a vysvětlíme si pár základních principů.
Programování v C/C++ - 2. díl
A je tu další díl seriálu o programovaní v jazyku C. Minule jsme měli takovou malou seznamovací party, teď už
nás ale čeká první rande, vlastně první program.
Náš první program
S chutí do toho, hned je hotovo. Založte si nový projekt (typu konzolová aplikace), nějak si ho pojmenujte,
pokud vám Dev-C++ už něco vygenerovalo, smažte to. Pokud ne, zkopírujte si následující část kódu.
/* tohle je náš první program */
#include<stdio.h>
//tady je to důležité
int main()
{
//pozdravíme
printf("Ahoj!");
//počkáme na klávesu
getchar();
//této řádky si nevšímejte
return 0;
}
Je to takový kraťoučký prográmek, který nás pozdraví, počká až "to odklepneme" a skončí.
Komentáře
První řádek začíná dvojicí znaků /*. Tak začíná tzv. komentář, tedy nějaká poznámka, kterou si programátor ke
svému programu napsal (tady "tohle je náš první program"). Komentář ale musí i nějak končit. Od toho máme
dvojici znaků */. Vše co je mezi /*a*/ je pro kompilátor (compiler) neviditelné, nevšímá si toho. Komentář může mít
i více řádek, pokud vám ale stačí řádek jen jeden, můžete psát komentář i za dvé lomítka // (kompilátor si nebude
všímat toho co je mezi těmito lomítky a koncem řádku. Není potřeba ho tedy nijak ukončovat, jeho konec je vždy
tam, kde končí řádek), jako jsem to udělal o několik řádek níže.
Řádky co začínají #
Řádky co začínají # si nebudeme zatím vůbec všímat. Někdy později jim budeme věnovat celou kapitolu.
Vlastní program
Vlastní program, tedy náš seznam úkolů pro počítač, je mezi znaménky { a } (složené závorky). To co zbylo
před našimi závorkami - main() - si také vysvětlíme v některém pozdějším díle (pro dnešek se budete muset smířit
s tím, že pracujme jen a jen s částí mezi složenými závorkami a sem tam nějakým tím komentářem).
Nad každým důležitým řádkem jsem napsal, co znamená. Pamatujte si, že jednotlivé instrukce a příkazy pro
počítač se od sebe oddělují vždy středníkem. Můžete jich mít na jedné řádce klidně deset (no brrr), ale je důležité
je mít oddělené středníkem (jazyk C rozlišuje mezi malými a velkými písmeny, to znamená že printf a PrintF NENÍ
to samé!!). Podle toho kompilátor pozná, kde končí jeden příkaz a začíná druhý. Pokud je potřeba k příkazu připojit
ještě nějaký parametr, přesněji specifikovat, co se bude dít, je potřeba to uvést v závorkách (příkazy - také se jim
říká funkce - po pravdě parametry buď mají, a mají jich přesný počet s přesným významem, nebo nemají (výjímky
jsou, ale zabývat se jimi nebudeme). Máme-li tedy, jako ve výše uvedeném příkladu
printf("ahoj!");
Znamená to, že máme funkci (příkaz, instrukci), která se jmenuje printf (což znamená "print formated", česky
"vytiskni formátované"… funkce mívají často nějaká intuitivní jména) a která vytiskne na obrazovku to, co dostane
jako parametr (nějaký text mezi dvojicí uvozovek), v tomto případe bude tedy na obrazovce:
Ahoj!
Hned jak se náš text vytiskne, provede se funkce getchar (která nemá žádný parametr, takže závorky jsou sice
prázdné, ale musejí tam být!), která "přečte" z klávesnice první stisknuté tlačítko (česky "zjisti znak"), tím pádem
ale i musí počkat než se zmáčkne :).
To je tedy náš první program. Přeložíme ho Ctrl-F10, spustíme Ctrl-F9 (doufám že jste od minule ještě Dev-C++
nesmazali…), chvíli počkáme, než se program přeloží, a mělo by se vám všem objevit okno a v něm Ahoj.
Stiskněte klávesu, a program se ukončí.
POZN: je potřeba soubor uložit s koncovkou .C, pokud už jste ho ale uložili s koncovkou .CPP a nechce se Vám
ho ukládat znovu, stačí přidat před main slovo "int" a před } přidat řádek "return 0;" (vše samozřejmě bez závorek a
uvozovek)
Ještě trochu hygieny
I Váš kód potřebuje hygienu. Když ho nebudete pořádně udržovat, bude za chvíli zarostlý a ošklivý. To, o čem
tu budu mluvit, je důležité. Ač se Vám to teď nezdá, je velmi dobré mít kód přehledný. Snažte se dodržovat nějaký
svůj styl. Pokud možno by jeho součástí mělo být, že píšete vždy jeden příkaz na jednu řádku. Pokud to má logicky
nějaký smysl, můžete třeba občas vložit prázdnou řádku (jako když píšete nějaký sloh nebo zápis ze schůze - mělo
by to být přehledné. Na rozdíl od třeba knihy se ale musíte v tomto textu dobře orientovat na první pohled.
Nemůžete ho pokaždé číst od začátku. Dobré a přehledné členění je proto důležité). Dobrým zvykem je také psát
za čárku mezeru a někteří ji dávají i za levou závorku a před pravou závorku, to aby věci v závorce byly trochu
odsazené (časem zjistíte, že věci v závorkách se daleko častěji mění než např. jména funkcí ;)). Také se nebojte
odsazovat (třeba jako v našem příkladu, vše mezi { a } je odsazené (o jeden TAB nebo několik mezer, jak Vám to
vyhovuje)). Časem to správné rozmístění dostanete do oka.
Podobná věc platí pro komentáře. Nešetřete jimi, pokud budete mít třeba poměr komentář: kód 1:1 (a není
naprosto zřejmé, co chcete dělat a proč to chcete dělat), je to výborné. Po několika dnech nebo stovkách řádků
třeba zapomenete, co tenhle řádek dělá a hlavně proč to vlastně dělá… hmmmm
Co si zapamatovat
Pamatujte si, že jednotlivé funkce/příkazy se od sebe oddělují středníkem (v podstatě jsou to výrazy. Každou
funkci nebo příkaz si můžete představit jako funkci v matematickém výrazu. Pokud věcem co jsem do této závorky
napsal moc nerozumíte, nic se neděje). Pamatujte si že se komentář píše mezi /* a */ nebo za // a že platí, že čím
více, tím lépe. Komentář slouží k tomu, aby jste se ve vlastním kódu lépe vyznali! Pokud má funkce nějaké
parametry, píšou se do závorky za jméno funkce/příkazu a oddělují se čárkou. Pokud ne, píšou se tam prázdné
závorky. Ne že by vám to bez nich neprošlo, ale znamenalo by to něco jiného :o a to my nechceme.
Programování v C/C++ 3. díl
Dnes si budeme povídat o proměnných a o jejich druzích. Konečně už budete schopni napsat program, který
bude opravdu něco dělat a v příštím díle si už naprogramujeme malinkatou hru, to jen aby jste měli motivaci do
začátku ;-)
Proměnné
Proměnnou si můžete představit jako nějakou hodnotu. Tu hodnotu můžete různě měnit, což je velmi důležité, a
také z ní můžete číst, což je neméně důležité. Kolem a kolem, v proměnných si uchováváte hodnoty, které si
potřebujete ve vašem programu pamatovat. Vlastně naprostou většinu hodnot si je potřeba pamatovat. Pokud by
jste psali například úplně jednoduchý program, který se zeptá uživatele na jeho jméno a potom ho pozdraví, je
potřeba se uživatele na to jméno zeptat a potom ho tím jménem pozdravit. Pokud bychom si ovšem jméno
uživatele nezapamatovali (mi jsme se ho zeptali, ale nezapamatovali jsme si ho!), nebudeme si na něj (na to
jméno) moci vzpomenout, až jím budeme chtít pozdravit uživatele. Prostě jsme se zeptali, jedním uchem šlo
dovnitř, druhým ven a když má teď váš program pozdravit, připadá si jako po požití návykových látek ;). Proto je
potřeba si věci/hodnoty v programu pamatovat (říká se ukládat do proměnných). Pokud by jste si hodnoty nemuseli
pamatovat, nemají vlastně pro Vás žádný účel a potom jste je (např. od uživatele) nemuseli ani získat.
Základní typy proměnných
Proměnná tedy v sobě uchovává nějakou hodnotu. Takovou hodnotou může být například číslo nebo také text.
Prostě, druhy proměnných jsou různé a také se liší v tom, jakým způsobem si je počítač pamatuje (tím se ovšem
ještě zabývat nebudeme). V tabulce je uvedeno několik druhů číselných proměnných (proměnnými, které si
pamatují text, se budeme zabývat někdy jindy).
Název
rozsah bez znaménka
(unsigned)
rozsah se znaménkem
(signed nebo samotné)
char
0 až 255
-128 až 127
short
short int
0 až 65,535
–32,768 až 32,767
0 až 4,294,967,295
–2,147,483,648 až
2,147,483,647
int
long
long int
Poznámky
rozsah pro typ int se liší podle platformy
(data v tabulce platná pro 32-bitové
operační systémy, např. Windows
95/98/NT/XP, Linux atp.)
float
6 číslic pro desetinnou a celočíselnou část dohromady
vždy se znaménkem, ne vždy přesné
double
14 číslic pro desetinnou a celočíselnou část dohromady
vždy se znaménkem, ne vždy přesné
Jak vidíte, liší se tím, jak velká čísla si mohou pamatovat (pokud by jste se do proměnné snažili uložit větší
číslo, než je schopna si pamatovat, dojde k tzv. přetečení (anglicky overflow), kdy se proměnná nastaví na
hodnotu, která je rovna tomu, o kolik se tam proměnná nevešla (varování: vždy by jste měli vědět jakých hodnot
bude proměnný nabývat už když program píšete!). Pokud by jste potřebovali pracovat s ještě větším rozsahem
čísel, než jaká jsou tu uvedena, museli by jste použít nějakou pomocnou knihovnu (nic, čím by jste se zrovna teď
měli zabývat).
Většina jmen proměnných má svůj význam (jak jsme si řekli minule, je důležité rozlišovat mezi malými a velkými
písmeny!). Název char je od anglického character - znak (proč? řekněme že z "historických důvodů"), int je od
anglického integer - celé číslo, long je long - "dlouhé" číslo, float je float - vznášet se, plout - zde kvůli plovoucí
desetiné čárce a konečně double - jako dvojitá přesnost pro desetiná čísla.
Potom je tu přívlastek (dává se před typ proměnné) signed a unsigned, což znamená ze se znaménkem a bez
znaménka. Je-li číslo se znaménkem, pamatuje si počítač jen poloviční rozsah (místo 256 různých čísel při
rozsahu 0-255 je to 256 různých čísel se znaménkem při rozsahu -128 až 127. Slova signed a unsigned jdou
"napasovat" před všechny celočíselné typy, desetinné typy jsou vždy se znaménkem. Můžete používat i názvy
samotné, pak si k tomu počítač (záleží na nastavení kompilátoru, ale zpravidla) přimyslí signed a z char se stane
signed char atp. Příklad použití signed a unsigned
unsigned short
unsigned short int
signed long int
unsigned char
Stručně zopakováno, máme několik typů celočíselných a desetinných. Liší se v tom, jaký rozsah čísel si
pamatují, a u celočíselných v tom, jestli jsou se znaménkem nebo ne.
Jak si "uděláme" takovou proměnnou
Pokud potřebujete uložit nějakou tu hodnotu, potřebujete proměnnou. My už jsme si řekli, jaké jsou jejich typy a
co si umí "pamatovat". Těď si ukážeme, jak si takovou proměnnou "uděláme" nebo vytvoříme.
Řekněme, že chceme uložit číslo v rozsahu 0-10, např. 8. Zvolíme tedy typ char (nebo unsigned char), protože
do jeho rozsahu se pohodlně vejdeme (větší rozsahy zabírají více paměti, proto je lepší používat čísla s nejmenším
dostačujícím rozsahem). A jak to teď uložíme? Je potřeba zvolit si jméno/název, pod kterým si budeme tu hodnotu
ukládat, např. nase_cislo (jména proměnných nesmí začínat číslicí, mohou obsahovat malá a velká písmena,
číslice a podtržítko; správná jména jsou např.
mojecislo
MojeCislo
MojeCislo2
Moje_cislo
_cislo_
Naopak špatně jsou např.
1_mojecislo
moje-cislo
moje&cislo
moje cislo
Dvě proměnné (vůbec dvě jména, tj. jména funkcí, proměnných atp. nemohou mít stejný název. Proto se vám
může někdy stát, že vaše jméno už je použito… potom použijte jiné). Do kódu to pak zapíšeme jako
//tohle je moje prvni promenna
char moje_cislo;
Jak jste si už možná všimli, nejprve napíšete typ proměnné, potom její jméno a středník. Další příklady toho, jak
vypadá deklarace proměnné jsou
signed char my_signed_number;
int my_integer_number;
double HypergalacticSpeed;
float fVelocity;
pokud potřebujete více proměnných stejného druhu a nechce se Vám psát typ proměnné znovu a znovu,
můžete jednotlivé proměnné (jejich jména) stejného druhu oddělit čárkou a za poslední udělat středník.
char prvniCislo, DruheCislo, treti_cislo, CTVRTECISLO;
Tomuto (ať už chcete jen jednu nebo více proměnných) se říká deklarace proměnných, tj. Vy deklarujete, že
budete používat proměnnou určitého jména a typu a kompilátor se díky tomu může na Váš program "lépe
připravit". Než budete proměnnou používat, musíte ji deklarovat.
Kde deklarovat proměnnou?
POZNÁMKA: minule byla velká diskuse o tom, jestli se učíme C nebo C++ a co je lepší. Já jsem seriál
pojmenoval Programováni v C, protože jsem chtěl dát jasn ě najevo, že se tu nebudeme zabývat objektově
orientovaným programováním, ale že se budeme učit základy programování. Pro úplnost tedy
- budu se snažit
odlišit alternativy jak pro C tak pro C++. Pro Vás, co začínáte, nelamte si hlavu, vyberte si C++, proto
že je
pohodlnější na práci a bude se vám žít blaze. Žádné změny není třeba podnikat, soubory ukládejte s
koncovkou
.cpp a máte vystaráno. My si budeme říkat, jak je to pro C++ (pro ty z
vás, kteří to chtějí a musejí vědět nebo musí
z nějakého důvodu progra movat v C se musí deklarovat proměnné na začátku funkce, hned za levou složenou
závorkou) .
Proměnnou můžete deklarovat kdekoliv mezi { a } v příkladu z minulého dílu nebo kamkoliv rozumně
v programu (zkoušejte to, když to bude špatně, počítač vám to nedovolí)
Budu to dělat pořád a pořád: snažte se udržovat si pořádek v kódu, dobře odsazovat (TABem nebo
mezerníkem, např. Visual C++ už to dělá automaticky), dnes ještě zdůrazním, aby jste si dobře pojmenovávali
proměnné, to znamená žádné bbb, abc, fgh atp, ale hezky celým jménem nebo nějakou zkratkou. Pro inspiraci
některé styly, kterými programátoři zapisují proměnné:
int iMojePromenna;
int moje_promenna;
int mojePromenna;
int MojePromenna;
Co můžeme s číselnou proměnnou dělat?
Je to číslo, můžeme s ním tedy dělat jednoduché operace jako sčítání, odčítání, násobení a dělení. Pokud něco
podobného chcete dělat, zapisujete to jako normální rovnici v matematice (středník na konci). Například:
a = b + c;
Jako v matematice, a se bude rovnat (do a se přiřadí hodnota) součtu b a c. Z věcí, které by ještě nemusely být
úplně jasné: znak pro násobení je *, znak pro dělení je /. Přitom můžete mezi sebou míchat celá čísla i čísla
s desetinnou čárkou, převod proběhne automaticky.
Dnešní jednoduchý program
/* druhy program! pocitani pokut */
#include<stdio.h>
int main()
{
//tady zacina hlavni blok kodu
//tady zacina nas program
printf("Vytejte v programu, ktery vypocitava pokuty!\n\r");
printf("Pokuty se budou strhavat ze standardniho platu 15000Kc!\n\r");
//nadeklarujeme 3 promenne typu int
int standardni_plat;
int snizeny_plat;
int pokuta;
//nastavime pocatecni hodnoty
standardni_plat = 15000;
pokuta = 400;
//spocitame kolik je snizeny plat; odecteme tedy od promenne standardni
//plat, ktera ma hodnotu 15000, promennou pokuta, ktera ma hodnotu 400.
//15000-400 je 14600, tzn. ze do promenne snizeny plat se nam ulozi
//hodnota 14600 (prace s ostatnimi druhy promennych probiha stejne)
snizeny_plat = standardni_plat - pokuta;
//vypiseme hodnotu snizeneho platu
//POZZ: funkce printf ma promenny pocet parametru, je trochu nestandardni,
//ale velmi sikovna
printf("Zlobivy zamestnanec si vydela jen %d", snizeny_plat);
//pockame na stisk enteru
getchar();
//to neni dulezite
return 0;
} //tady konci hlavni blok kodu
Platnost proměnných
Používat proměnnou můžete až potom, co jste jí nadeklarovali. Jenomže proměnné taky mohou "umřít". Celá
zápletka je v tom, že proměnná je platná jen v té části kódu, ve které je deklarovaná. Nás zatím tohle umírání
nemusí tížit a když na to nezapomenu, zmíním to v příštím díle.
Do příště
Zkuste si upravit program tak, aby se standardní plat kvůli mimořádným úspěchům firmy zvednul dvakrát (jinak
než tak, že místo 15000 napíšete 30000!). Nápověda: platí stejná pravidla (přednost násobení a dělení před
sčítáním a odčítáním) jako u normálních počtů a můžete používat závorky.
Programování v C/C++ 4. díl
Dnešní téma je větvení a podmínky. Tu slibovanou hru budeme muset ještě o jeden díl odložit, ale přijde.
Minule jsem se tu rozepisoval o proměnných. Doufám že si ještě pamatujete co je to char, short, long, int, double a
float. Ani nepředpokládám, že si je pamatujete všechny, ale aspoň polovinu by jste si pamatovat mohli.
Blok kódu
Takovým předpokladem pro dnešní téma je vědět, co je to blok kódu. Název není úplně přesný, prostě je to
blok, blok kódu, blok programu nebo to mezi složenými závorkami.
Představte si, že máte několik papírů. Aby se Vám s nimi lépe pracovalo, procvaknete je sešívačkou. Co je pro
nás důležité je, že ta sponka ze sešívačky jednotlivé papíry spojila. Už nepracujete s jednotlivými listy, ale se všemi
jako s celkem. Podobně funguje i blok kódu, vezme více řádek a můžete s nimi pracovat jako s jednou. Asi tady
budu muset být přesnější. Jestli si ještě vzpomínáte, jak jsem říkal, že každý jednotlivý příkaz nebo funkce se musí
oddělovat středníkem. Celý takový blok kódu se pak v programu chová jako jeden příkaz. Takový blok pak může
vypadat například nějak takto.
{
//pozdravime
printf("Ahoj, jak se dari");
}
Do bloku je možné vkládat libovolně další bloky (což bude potřeba při větvení). Vkládat ale takový blok
samoúčelně, pokud nepotřebujete seskupit více příkazů/funkcí, není standardní postup a ani bych to
nedoporučoval, ačkoliv to není v zásadě špatně.
Taky si v našem dnešním zkušebním programu nebo v ukázkovém programu z druhého dílu všimněte, že tam
jeden blok kódu máme (v dnešním příkladu jsem ho označil). Je to takový hlavní blok v programu, na jeho začátku
program začíná a na jeho konci program končí.
Větvení programu
Může nastat situace (a ona nastane, a mnohokrát), že Váš program se v jeden moment zastaví na křižovatce a
bude se muset rozhodnout, jestli odbočí nebo bude pokračovat rovně. Přijde na tu křižovatku, stojí tam a rozhodne
se, kterou cestou půjde. To je větvení. Váš program dojde na nějakou svou řádku a tam se rozhodne, co bude
dělat.
Doufám, že už se ptáte, podle čeho se rozhodne. Rozhodne se podle nějaké podmínky. Například pokud
uživatel zadal, že je vyšší než 180 cm, pochválíme ho za jeho výšku. Takovéto jednoduché větvení se dělá pomocí
takového kouzelného slovíčka if (v angličtině if = pokud). Podmínku potom napíšeme do závorek hned za tohle
kouzelné slovo a to, co se má vykonat hned za to (opět si všimněte, jak kód upravuji, aby se dobře četl)
if( vyska > 180)
printf("Ty si ale veky clovek");
Výše uvedený úryvek kódu by vzal hodnotu proměnné vyska (která byla samozřejmě někde dříve v programu
použita a nastavena) a pokud by ta hodnota byla větší než 180, vypsala by se na obrazovku zpráva. Co když ale
chceme provést více věcí než jenom jednu? Jednou možností by bylo napsat jedno if pro každou funkci, kterou
chceme vykonat, pokud je podmínka splněna. Tudy ale cesta rozhodně nevede. Cesta naopak vede přes bloky
kódu. Názorná ukázka
if( vyska > 180 )
{
printf("Ty si ale velky clovek…Jestli chces pokracovat, stiskni klavesu");
getchar();
printf("Jedeme dal");
}
Zde, pokud je hodnota proměnné vyska vyšší než 180 (pro ty matikou nepoznamenané, > znamená větší než),
program opět vypíše gratulaci, počká na stisk klávesy ENTER a informuje nás o tom, že program dál pokračuje.
Pokud se podmínka nesplní, to znamená pokud jsme menší než 180 cm nebo máme přesně 180 cm, program
pokračuje za funkcí nebo blokem, který následuje za slovem if. Připravil jsem vám dva diagramy, program
pokračuje po šipkách.
pokud je hodnota proměnné vyska vyšší než 180
pokud je hodnota proměnné vyska menší než 180
V podmínkách můžete použít několik tzv. operátorů, lidštěji řečeno "značek".
Operátor
Komentář
>
levá strana větší než pravá
<
pravá strana větší než levá
>=
levá strana vetší nebo rovna pravé
<=
pravá strana větší nebo rovna levé
==
!=
levá strana se rovná pravé (POZOR - jsou to skutečně dvě =, zatímco dvě =
porovnávají, jedno = přiřazuje hodnotu!
levá strana se nerovná pravé
Říká se, že každý takový operátor má dva operandy. Jinými slovy každá ta značka potřebuje dvě čísla, které
bude porovnávat. Když počítač uvidí nějakou takovou podmínku, nejprve si spočítá (vyhodnotí) číslo vlevo a
vpravo od operátoru a potom je teprve porovnává.
if( 10 - 2 > 3 )
je tedy ekvivalentní
if( 8 > 3)
Pokud se podmínka, kterou jsem napsali, splní, říká se, že se podmínka vyhodnotila jako pravdivá a nahradí se
jedničkou (nenulová hodnota, nejčastěji 1, pro počítač znamená pravda, 0 pro počítač znamená nepravda). Pokud
se podmínka nesplní, to znamená že hodnota proměnné výška je menší nebo rovna 180, potom se vyhodnotí jako
nepravdivá a nahradí se nulou.
Počítač tedy vlastně jenom zajímá, jestli je v závorce za if nula nebo jiné číslo. Pokud je tam nula, znamená to
pro něj nepravda, pokud je tam jiné číslo, znamená to pravda a vykoná to, co je za if a závorkou. Pokud bychom
měli podmínku, která vypadá zhruba takto
if( 10 > 5 )
počítač to "vidí" jako
if(1)
Přeloženo do češtiny: Pokud je to pravda (…něco udělej…). Určitě vám hodně pomůže, když si budete takhle
podmínky číst. (nahrazení jedničkou nebo nulou probíhá samozřejmě až za běhu programu, protože v době, kdy
program píšete ještě neznáte hodnotu proměnné, která se objeví v podmínce)
Pokud, v zájmu například lepší srozumitelnosti kódu, nebo i z jiných důvodu, potřebujete hodnotu otočit (udělat
z pravdy nepravdu nebo z nepravdy pravdu), potom stačí před hodnotu, kterou chcete otočit, napsat vykřičník.
if(!(18 > 15))
Což by znamenalo "pokud 18 není větší než 15". Vyhodnotilo by se to na:
if(!(1))
Vykřičník nám pravdu (jedničku) otočí na nepravdu (nulu).
if(0)
A máme to.
Co když potřebuji více podmínek:
Pokud je potřeba, aby bylo splněno více podmínek, můžete do závorek za if uvést podmínek i více. Mezi
jednotlivými podmínkami je potřeba určit vztahy. Pokud mají být splněny obě podmínky, napíšete mezi ně &&, což
se čte jako "a zároveň". Pokud vám stačí, aby byla splněna alespoň jedna podmínka, napíšete mezi ně || (dvě
svislítka), což se čte jako "nebo".
if(stari >= 17 && stari <= 25)
printf("Chlap mezi 17 a 25, to je presne to co hledam :) ");
"nebo" mezi podmínkami
if(pocet_nohou > 2 || pocet_rukou > 2)
printf("Ty asi nebudes clovek, ze ne?");
&&
A ZÁROVEŇ
||
NEBO
Pokud je levá i pravá strana pravdivá
pokud je levá nebo pravá strana pravdivá, nebo jsou pravdivé obě
Ještě jednou zdůrazním, že u "nebo" stačí jen jedna ze dvou podmínek pravdivá, ale mohou být pravdivé
i obě!).
Pokud máte více podmínek než dvě, obzvláště pokud ještě k tomu kombinujete && a ||, dávejte velký pozor na
to, v jakém pořadí se vyhodnocují a radši si vždy pomozte dalšími závorkami. Jednotlivé || a && se vyhodnotí opět
jako pravdivé nebo nepravdivé a z
if((1 && 1) || 0)
se stane (vyhodnotí se nejprve závorka, 1 && 1, tedy "pravda a zároveň pravda", což je pravda, nebo-li pro
počítač 1).
if(1 || 0)
z čehož se dále stane (alespoň jedno z dvojice pravda a lež je pravda, což je pravda, tedy 1)
if(1)
Pokud jste tím vším trochu zmateni, nic si z toho nedělejte. Pokud budete schopni lidskou větu správně
přeformulovat na počítačovou podmínku, jste za vodou.
Další kouzelné slovo: else
Pokud chcete, aby se, pokud je podmínka pravdivá, něco vykonalo a pokud ne, vykonalo se něco jiného,
nemusíte použít dvě "opačné" podmínky. Stačí za funkci nebo blok, který máte za vaším if, dát slovíčko else a za
něj hned funkci nebo blok, který nebo která udělá to, co chcete udělat, pokud je podmínka nepravdivá
if(vek >= 18)
//vykona se pokud je podminka spnena
printf("Plnolety(a)");
else
//vykona se pokud je podminka nesplnena
pritnf("Neplnolety");
Zkušební program
pokud si chcete osahat práci s podmínkami více, upravujte si podmínky v následujícím programu.
#include<stdio.h>
int main()
//tady zacina nas hlavni blok!
{
/* tuhle podminku si muzete zkusit upravovat. zkousejte menit hodnoty a operator
a zkuste také slozitejsi podminky s && a || */
if(10 > 5)
printf("podminka splnena");
else
printf("podminka nesplnena");
getchar();
//toho si nevsimejte
return 0;
} //tady konci hlavni blok
Dodatek o platnosti proměnných
Jak jsem slíbil v minulém díle, zmíním se tu ještě krátce o platnosti proměnných. Proměnná platí do konce
bloku, ve kterém je deklarována, ale platí i v blocích vnořených v jejím bloku
Programování v C/C++ 1. bonus
První bonusový díl je tady a s ním naše první hra. I když ještě nejsme schopní napsat celý program úplně sami,
určitě budeme schopni ho napsat s trochou nápovědy.
Pravidla
Naše hra bude hodně jednoduchá: hádání čísla. Doufám, že teď nejste zklamaní, ale pořád jsme jen
začátečníci, že? Pravidla jsou jednoduchá. Počítač si myslí náhodné číslo od 1 do 100 a uživatel hádá, jaké to je.
Pokud uhádne, počítač mu pogratuluje a program se ukončí. Pokud uživatel hádal číslo menší, řekneme mu, že
hádané číslo je větší. Naopak pokud bude hádat větší, upozorníme ho, že hádané číslo je menší.
Jak na to?
Ještě než začneme, položím vám otázku: jak by asi měl program podle vás vypadat, jakou by měl mít strukturu
(jaké konstrukce/smyčky by jste použili, jaké proměnné a co budou vyjadřovat)? Zkuste si na tuto otázku
odpovědět, klidně si dejte i chvilku na rozmyšlenou.
Plán
Já jsem se rozhodl pro 3 proměnné. Jednu, která bude uchovávat naše hádané číslo, jednu, která si bude
pamatovat, co uživatel zrovna tipoval za číslo a jednu, která nám bude pomáhat s hlavní smyčkou v programu.
//do teto promenne ulozime cislo, ktere si pocitac vymysli
int hadane_cislo;
//do teto promenne ulozime cislo, ktere uzivatel zadal
int uzivatel_zadal;
//ma program pokracovat nebo uz uzivatel uhadl?
int pokracuj;
Co se týče smyček, rozhodl jsem se pro jednu hlavní while smyčku. Váš návrh může vypadat samozřejmě jinak,
dovedu si například představit i řešení s do…while smyčkou. Nic Vám nebrání si to zkusit napsat podle svého. To
samé platí i pro vaše proměnné.
V podmínce této mé smyčky se bude vyhodnocovat proměnná pokracuj. Pokud bude mít hodnotu 1, bude se
dál pokračovat, pokud bude mít hodnotu nula, smyčka se sama ukončí.
while(pokracuj == 1)
{
//tady bude vlastni kod
}
Příprava na smyčku
Ještě než ale začneme se smyčkou, je potřeba nastavit naše proměnné na správné hodnoty. Například
pokracuj musíme nastavit na 1, protože jinak by se nám nevykonal ani jeden cyklus naší smyčky. Potom je potřeba
ještě do hadane_cislo uložit nějaké náhodné číslo… o to se postarají dvě následující řádky
//trocha magie, tohle jeste neumime
srand((unsigned)time(NULL));
//do hadane_cislo ulozime pocitacem vygenerovane nahodne cislo
//teto radce take jeste asi nerozumite
hadane_cislo = rand()%100 + 1;
Smyčka
Ve vlastní smyčce potom vždy přečteme číslo od uživatele
//tato radka precte od uzivatele cislo
//zatim budeme predpokladat, ze uzivatel skutecne
//zada cislo a nebude se snazit vnutit nam nejaka pismenka
scanf("%d", &uzivatel_zadal);
a třemi if-y potom zjistíme, jestli je číslo menší, větší, nebo jestli uživatel uhádl.
Zkuste si program opravdu napsat sami, pasivní znalost je poloviční znalost.
Kód
Zde příkládám funkční kód této hry. Pokud chcete hru trochu vylepšít, zkuste ji upravit tak, aby měl uživatel na
uhádnutí čísla jen několik pokusů.
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
//do teto promenne ulozime cislo, ktere si pocitac vymysli
int hadane_cislo;
//do teto promenne ulozime cislo, ktere uzivatel zadal
int uzivatel_zadal;
//ma program pokracovat nebo uz uzivatel uhadl?
int pokracuj;
//nastavime nase promenne
pokracuj = 1;
//trocha magie, tohle jeste neumime
srand((unsigned)time(NULL));
//do hadane_cislo ulozime pocitacem vygenerovane nahodne cislo
//teto radce take jeste asi nerozumite
hadane_cislo = rand()%100 + 1;
//dokud je pokacuje rovno 1
while(pokracuj == 1)
{
//ptame se uzivatele na cislo mezi 1 a 100
printf("hadej cislo od 1 do 100\n\r");
//tato radka precte od uzivatele cislo
//zatim budeme predpokladat, ze uzivatel skutecne
//zada cislo a nebude se snazit vnutit nam nejaka pismenka
scanf("%d", &uzivatel_zadal);
//pokud uzivatelem zadane cislo je vetsi nez to, ktere
//si mysli pocitac
if(uzivatel_zadal > hadane_cislo)
printf("kdepak, hadane cislo je mensi…\n\r");
//pokud uzivatelem zadane cislo je mensi nez to, ktere
//si mysli pocitac
if(uzivatel_zadal < hadane_cislo)
printf("kdepak, hadane cislo je vetsi…\n\r");
//pokud uzivatele zadane cislo je rovno cislu, ktere
//si mysli pocitac
if(uzivatel_zadal == hadane_cislo)
{
//pogratulujeme
printf("Uhadl jste!\n\r");
//nastavimje pokracuj na 0, to ze while-smycka
//se, hned jak se dojde zpet k while, ukonci
pokracuj = 0;
}
}
//rozloucime se
printf("nashledanou");
//pockame na ENTER
getchar();
//tajemna radka
return 0;
}
Programování v C/C++ 5. díl
Začneme už trochu tradičně opakováním. Naposledy jsme se seznámili s větvením programu a podmínkami.
Doufám, že jste vše náležitě pochopili, protože dnes na předešlé téma navážeme. Co je podmínka, a jak se
zapisuje, už jsme se naučili a dneska si probereme ty zbylá kouzelná slova, co nám pomáhají řídit, kudy se
program ubírá.
WHILE
While je dneska první na řadě. Zapisuje se takhle
while(podmínka)
příkaz;
A říká počítači, že dokud je podmínka v závorce pravdivá, má vykonávat příkaz, který je za závorkou uveden.
Pokud jste pozorně četli minulé díly, tušíte správně, že příkaz můžete samozřejmě nahradit i blokem kódu (to, že
se může příkaz nahradit blokem kódu, už budeme brát za samozřejmé a nebudeme to znovu zmiňovat).
V praxi while (anglicky „dokud (je pravda)“) použijete velmi často. Program totiž často dělá něco dokola a
dokola. Program počká, co po něm uživatel chce, provede to a znovu čeká. To se děje právě v tomto while .
Mimochodem, často se také říká smyčka, což značí právě to, že vždy, když se program dostane na konec bloku,
vrátí se zase na jeho začátek (pokud je splněna podmínka; v naprosté většině případů se za while používá blok
kódu).
Praktickým příkladem pro while je následující úryvek kódu:
int cislo;
cislo = 0;
//program o sobe neco napise
printf(“pocitani do 99\n\r”);
//dokud je hodnota promenne cislo mensi nez 100, vykonej blok kodu
while( cislo < 100)
{
//zvetsime cislo o 1
cislo = cislo + 1;
//vypiseme ho
printf(“%d \n\r”, cislo);
}
Program deklaruje proměnnou cislo, nastaví ji na nulu a potom začne smyčka. V ní se nejprve vyhodnotí
podmínka. Pokud je pravdivá, přejde program do bloku kódu a k proměnné cislo přičte 1, a vypíše se. Potom se
program vrátí zpátky na řádku s while a začne se opět porovnáním podmínky. Všimněte si, že pokud je podmínka
nepravdivá, nemusí se vykonat ani jeden cyklus smyčky. Pokud je podmínka splněna, vykoná se smyčka, pokud
není, nevykoná a program pokračuje za ní.
Variace na while
Takovou sestřičkou while je tzv. do…while . Chová se naprosto stejně jako while, akorát podmínka se vyhodnotí
až po provedení smyčky, nikoliv na jejím začátku. Zapisuje se:
do
příkaz
while(podmínka);
Na rozdíl od samotného while je tady navíc slovo do (anglicky „dělej“) a while je až na konci. Nejprve se vykoná
blok kódu. Potom se vyhodnotí podmínka. Je-li pravdivá, celé se to opakuje. Pokud nepravdivá, program pokračuje
normálně dál. Žádná velká věda.
Smyčka for
Občas potřebujete něco udělat přesně několikrát. Třeba třikrát pípnout. Potom se používá tzv. for-cyklus, který
se zapisuje
for(nastavení proměnné; podmínka; co se s proměnnou v každém cyklu provede)
příkaz;
Ano, v závorce jsou tři části oddělené dvěma středníky. Je to o trochu složitější, než bylo while, takže rovnou
uvedu praktický příklad.
int i;
for(i = 0;i < 3;i = i + 1)
{
//fiktivni funkce ktera pipne
pipnout_prosim();
}
Tento kód ze všeho nejdřív nastaví proměnnou i na 0. Potom se vyhodnotí podmínka. Pokud je pravdivá (tedy
i je menší než tři), provede se blok kódů (zde bych ani blok kódu použít nemusel, ale udělal jsem to, aby byl kód
lépe čitelný). Na konci takového bloku kódu se provede poslední část závorky, tedy i = i + 1. Jinými slovy: i se zvýší
o jedu. Je samozřejmě na Vás, jaké počáteční hodnoty zvolíte a jestli budete přičítat jedničku nebo například
odečítat trojku. Nakonec se počítač vrátí zpátky k for, zjistí, jestli je podmínka stále pravdivá a znova se začne
vykonávat blok kódu.
Určitě si někteří z vás všimli, že to samé můžeme zapsat i pomocí while . Ano, můžeme (pod odstavcem jsem to
udělal, aby jste se měli čeho chytnout). Ale for-cyklus se používá, protože pak každý hned pozná už z prvního
řádku (protože tam je ta důležitá závorka), co taková smyčka dělá a nemusí hledat v bloku kódu, kde se která
proměnná mění atp.
int i;
//i musime nastavit na nulu
i = 0;
//pokud je i mensi nez 3
while(i < 3)
{
//fiktivni funkce, ktera zaridi pipnuti
pipnout_prosim();
//k i pricteme 1
i = i + 1;
}
Proměnné (v tomto případě i; většinou se používá, potřebujete-li do sebe vnořit více for-cyklů, proměnných
s názvy i, j, k atd., ale můžete je s klidným svědomím nazvat, jak chcete), která počítá počet cyklů, (kolikrát se
vykonal blok kódu za závorkou) se říká čítač.
Učíme se číst
Hodně vám pomůže, když se naučíte „číst“ tyto konstrukce. Zkuste si to trochu osvojit, obzvláště pokud máte
s touto látkou problémy. Jako bonus i pár řádek k minulé hodině.
while (i > 3)
{
//nejaky kod
}
dokud je i větší než tři, vykonávej blok kódu
while (teplota_vody <= 100)
{
//nejaky kod
}
dokud je teplota_vody menší nebo rovna stu …
do
{
//nejaky kod
}
while (i >= 100 && i =< 200)
vykonávej blok kódu dokud i je větší nebo rovno stu a zároveň i je
menší nebo rovno dvěma stům
for (i = 0;i < 25;i = i + 1)
pro i rovno nule přičítej v každém cyklu jedna, dokud i je menší než
25
if (!(cislo == 0))
pokud neplatí (to je ten !), že se cislo rovná nule
if (cislo < 18 || cislo == 25)
pokud je cislo menší než 18 nebo je cislo rovno 25
Shrnutí
Teď už víme, co je to while, do…while a for-cyklus. Všechno jsou to stále důležité věci, takže experimentujte.
S dotazy se, jako obvykle, můžete obracet do diskuse pod článkem. Slibovanou hru si napíšeme v bonusovém
díle, který by vychází společně s tímto.
Programování v C/C++ 6. díl
Po trochu delší odmlce je tu opět další díl našeho seriálu. Doufám, že si ještě něco pamatujete a že jste
případně sami trochu pokročili. Každopádně naším dnešním cílem je povědět si něco víc o tom, jak ještě fungují
funkce a kde všude se dají použít.
Vyhodnocování výrazů
Vezměte si například, takovou matematickou funkci abs (tedy absolute - je to funkce, která vrátí absolutní
hodnotu z nějakého celého čísla, jednoduše pokud je číslo záporné, změní jeho znaménko na plus, pokud už je
kladné, nedělá nic). Pokud bychom měli nějakou proměnnou a chtěli si do ní uložit právě absolutní hodnotu, dá se
to, jak už všichni nejspíš víte, udělat asi takhle:
//promenna cislo, do ktere se bude ukladat nase obsolutni hodnota
int cislo;
cislo = abs(-5000);
Je jasné, že do proměnné číslo se uloží hodnota 5000. I když si myslím, že už to většina z vás okoukala, přeci
jenom to ještě trochu dovysvětlím.
Program běží a dostane se na naši řádku s funkcí abs. Protože do proměnné cislo se má uložit hodnota
z "druhé strany rovná se", tj. výsledek funkce abs, vyhodnotí se, jakou hodnotu vrací funkce abs s daným
parametrem a ten se uloží. To je samo o sobě docela zřejmé a chápatelné. Z toho ale také vyplývá, že místo čísla
můžete kdekoliv použít i nějakou funkci, která číslo toho druhu vrací. Můžete je tedy například použít i u podmínek
(pokud si budete chtít kód otestovat, na začátek programu přidejte ještě řádku #include )
int hodnota;
hodnota = -30;
//ne zrovna ukazkova podminka na zjisteni toho, jestli
//je cislo kladne nebo zaporne
if(hodnota == abs(hodnota))
{
printf("cislo je kladné!");
}
To platí samozřejmě i například u while a jiných. Nebojte se experimentovat. Funkci můžete použít i jako
parametr jiné funkce, opět je ale potřeba dbát na to, aby byla zachována přehlednost kódu. Pokud bude váš kód
vypadat takhle:
a(b(), c(), d(e(), 10), f());
moc z něj asi nevyčtete. Navíc si dávejte pozor, aby funkce, které dáváte jako parametr, nebyly extrémně
paměťově náročné (snad se o tom zmíním někdy později, ale bývají to jen vyjimečné případy). Taky si všimněte, že
když je funkce takhle někde "zabudovaná", nepíšeme za ni středník. Jestli si pamatujete, mluvili jsme o tom, že
středník odděluje jednotlivé funkce/příkazy. Tady ale chápejte celou tu konstrukci (ať už je to funkce co má za
parametr nějakou další funkci nebo podmínka nebo něco jiného) jako jeden příkaz.
Líné vyhodnocování
Má to ale ještě jeden háček. V C/C++ se totiž uplatňuje tzv. líné vyhodnocování. Jakmile je jasné, že podmínka
nemůže být splněna, přestane její vyhodnocovaní. Například při
if(abs(0) && abs(-1))
je hned po vyhodnocení abs(0) jasné, že podmínka nemůže být splněna (&& - AND - a zároveň… obě strany
musí být pravdivé, abs(0) je rovno 0, tj. nepravda, z toho plyne, že podmínka nemůže být splněna). Vyhodnocování
tedy skončí a abs(-1) se už neprovede! To se vám může stát osudným, pokud by jste ve funkci, která je
v podmínce, vykonávali ještě nějakou důležitou činnost, např.
if(NahrajZvuky() && NahrajGrafiku())
{
printf("zvuky i hudba v poradku nahrany");
}
Zde by stačilo, aby chyběly zvukové soubory (které ale třeba nejsou k běhu hry/programu vůbec potřeba a
uživatel si je nestáhl) a nenahraje se ani grafika (která už je k běhu programu většinou potřeba). Dávejte si na to
pozor.
To samé platí i u ||, jakmile je jedna část podmínky pravdivá, je jasné, že je podmínka splněna a druhá část se
nevykoná/nevyhodnocuje.
Těchto vlastností můžete využít ve svůj prospěch, ale můžou vás i stát hodně nervů. Pokud to vyřešíte jiným
způsobem, nikdo vám to nebude mít za zlé. Navíc by to opět udělalo váš program čitelnější. Pokud se tedy
rozhodnete nějaké podobné vlastnosti použít, nezapomeňte se o tom zmínit v komentářích, tohle není úplně běžně
používaná konstrukce.
Funkce
Po takovém opakovacím a upozorňovacím začátku bych se rád pustil do funkcí. Když si představíte, že celý
Váš program (zrovna například píšete třeba průlomový operační systém) je napsaný tak, jako ho píšeme doteď
….
int main()
{
//tady tisíce různých řádek
return 0;
}
měli by jste se zhrozit. Samozřejmě spousta začátečníků píše programy podobně "v kuse". To se hodí pro
krátké utilitky nebo jiné menší programy, ale rozhodně už ne ani pro většinu normálních aplikací. Program totiž
můžeme šikovně rozdělit na spoustu menších dílů (funkcí/procedur). Každá funkce se potom stará o nějakou
specifickou činnost a když potřebujeme takovou činnost vykonat, prostě "zavoláme" příslušnou funkci a ona to
vykoná za nás.
Tady bych rád podotknul, že s jazykem C/C++ je "přibalena" už i spousta předpřipravených funkcí. Z těch, se
kterými jsme se už seznámili to jsou např. printf, getch nebo o pár odstavců výše uvedené abs.
Je mnoho případů, kdy se hodí kus programu "vyříznout" a dát do funkce. Třeba když některou činnost
v programu často opakujete nebo když se jedná o rozsáhlý kus kódu, který by nemusel působit přehledně (i když je
v celém programu jen jednou) a vám by se hodilo celou tu velkou hrůzu někam schovat. Typickým příkladem
nějaká rozsáhlá inicializace programu, kde se nahrává spousta věcí ze souboru atp. Je lepší to schovat do nějaké
funkce a pak jen zavolat
nahraj_vse_potrebne_a_nastav_promenne();
Pro často opakující se kus kódu by příkladem mohla být například naše matematická funkce abs. Ta může být
potřeba v programu na různých místech nesčetněkrát (tedy v nějakém matematickém programu).
Protože už jste s funkcemi pracovali (volali jste je když jste potřebovali počkat na stisk klávesy nebo něco
vypsat na obrazovku), měli by jste vědět, že funkcím lze dávat parametry a taky že nám funkce mohou vracet
hodnotu.
V praxi
Dost teorie, ukážeme si konečně, jak se taková funkce definuje (definuje znamená, že píšeme, co má funkce
dělat)
typ_navratove_hodnoty
jmenofunkce(typ_parametru
jmeno_parametru,
typ_parametru
jmeno_parametru, …., typ_parametru jmeno_parametru)
{
//tady je vlastni kod funkce
}
pozn: ano, poznali jste, main je také funkce… je to funkce, která se pustí po začátku programu a chová se
v mnoha věcech trochu podivně
Typ návratové hodnoty je prostě to, co chcete vracet. Může to být float, int, char nebo jiné typy, se kterými jsme
se už seznámili. Pokud nechcete vracet hodnotu, potom je návratový typ void (anglicky prázdnota). Následuje
jméno funkce, například tedy MojePrimovaFunkce (jméno se řídí stejnými pravidly jako jména proměnných). Za ní
obyčejné závorky () a v nich jednotlivé paramtery oddělené čárkou (pokud žádné parametry nechcete, jsou to jen
prázdné závorky; u parametru je vždy nejprve jeho typ a pak jeho jméno)
například:
void ahoj();
int SpocitejVekVeDnech(char den_narozeni, char mesic_narozeni, int rok_narozeni);
char je_cislo_mocinou_dvojky(int cislo);
Určitě vás už zajímá, jak se ve funkci přistupuje k parametrům. Úplně stejně, jako by to byla proměnná (která
má ale platnost jen uvnitř funkce, ona to také je proměnná… kompilátor za vás totiž vytvoří pro každý parametr
odpovídající proměnnou a do ní uloží hodnotu, která byla funkci předána). Z toho vyplývá, že cokoliv pak
s parametrem/proměnnou uděláte, se projeví jen uvnitř funkce
Takováto funkce by určitě nefungovala
void pricti_jednicku(int cislo)
{
cislo = cislo + 1;
}
Protože jak jsme si řekli, ve funkci pricti_jednicku se vytvoří proměnná cislo a k té se přiřadí hodnota 1. Zhruba
takhle
…
{
cislo = 1;
cislo = cislo + 1;
}
Můžete ale hodnotu z funkce vrátit. Nejprve musíme uvést, že budeme hodnotu vracet
int pricti_jednicku(int cislo)
a pak ve vlastním bloku funkce (říká se těle funkce, tedy ve vlastním těle funkce) příčteme k proměnné cislo
jedničku a vrátíme. To se dělá pomocí slova return a za ním napsané hodnoty nebo proměnné. Slovem return
(anglicky návrat) se ale funkce ukončí (návrat k hlavnímu programu). Opět,
int pricti_jednicku(int cislo)
{
cislo = cislo + 1;
return cislo;
}
V programu potom
{
int moje_promenna;
moje_promenna = 1;
//pricteme k moje_promenna jednicku
moje_promenna = pricti_jednicku(1);
//ted by se melo vypsat na obrazovku cislo 2
printf("%d", moje_promenna);
}
Už je toho celkem hodně. Dnes už na vás nebudu více nakládat. Jen si pamatujte, že parametr se
zkopíruje/přiřadí do proměnné, kterou pro vás připraví kompilátor a s tou vy pak pracujete (jak to obejít si někdy
ukážeme; ve funkci můžete přistupovat ke globálním proměnným (to jsou proměnné deklarované mimo tělo nějaké
funkce - platí totiž v celém programu), ale není to vždy zcela dobrý přístup, obzvláště opět kvůli přehlednosti při
používání ve více funkcích najednou). Dále že návratová hodnota se vrací před slůvko return a že se tak také
ukončí funkce (pozn. když je návratový typ void, tedy nic, můžete funkci také ukončit předáním "žádné" návratové
hodnoty: samotným return).
Pokud si chcete látku trochu ozkoušet: Napiště funkci, která bude vracet absolutní hodnotu z čísla předaného
v parametru
řešení (funkce + vlastní program):
#include
#include
#include
int absolutni_hodnota(int cislo)
{
//pokud je cislo zaporne, vratime -cislo, tedy cislo * -1
if(cislo < 0)
return -cislo;
else
return cislo;
}
int main()
{
int promenna;
promenna = absolutni_hodnota(-5);
printf("%d", promenna);
getch();
return 0;
}
Programování v C/C++ 7. díl
Po dlouhé době opět další díl (chtěl bych poděkovat především všem těm, kteří mne svými reakcemi přesvědčili
seriál obnovit). Minule jsme si řekli jak vypadá funkce a něco o vyhodnocování výrazů. Pokud už si z toho něco
nepamatujete, nebojte se zkonzultovat minulý díl. Lidé zapomínají, od toho je nápověda.
Dnešní téma
Úkolem bude vysvětlit si, co je to preprocesor a k čemu a jak se využívá. V druhé půlce si představíme některé
dosud neznáme operátory (znaménka).
Preprocesor a jeho direktivy
V nějakém vašem programu jste už určitě narazily na řádky, které začínají #. Váš editor je odlišuje barevně
nebo třeba tučnějším písmem, a já jsem vám zatím jejich výraz zatajil.
Nazývají se direktivy preprocesoru. To je sice krkolomný název, ale nikdo vás přece nenutí si ho pamatovat.
Jde o to, že ještě předtím, než přijde řada na samotnou kompilaci (kdy se tedy z vašeho .cpp souboru stává .exe),
vezme počítač váš zdrojový kód a nechá ho předzpracovat právě preprocesorem (pre = před, procesor =
"zpracovávač"). Ten podle toho, co jste za # napsali, s vaším programem něco provede.
#include<soubor.h>
Vezme soubor s jménem, které jste uvedli mezi < a > a celý ho vloží na místo, kde je vaše #include< >. Tento
soubor pak obsahuje zdrojový kód pro některé předpřipravené funkce, které můžete ve vašem programu použít.
Často jsme vkládali soubory stdio.h, stdlib.h a conio.h.Právě ty obsahují některé funkce jako printf, getch atp.
#include"soubor.h"
Liší se od #include< > jenom v tom, odkud soubory bere. #include< > je bere z adresáře, který je přednastavený
ve vašem vývojovém prostředí, zatímco #include" " je bere z aktuálního adresáře. Zatím tuhle direktivu nebudete
potřebovat, ale může se stát, že na ni narazíte.
#define JMENO HODNOTA
Direktiva, se kterou budete pracovat asi velmi často. Preprocesor nahradí výskyt všech JMENO ve zdrojovém
kódu slovem HODNOTA. To se používá, pokud máte nějakou hodnotu, která se v kódu často vyskytuje. Příkladem
by bylo třeba řecké PI.
#define PI 3.1415
…
float vysledek;
vysledek = sin(PI);
…
Hlavním účelem je, aby byl text přehledný. Obzvláště když píšete matematické programy nebo potřebujete
počítat něco podle vzorce, můžete se dostat časem do situace, kdy už nevíte, co tohle nebo tamto číslo vlastně má
znamenat a proč jste ho tam dali. Takhle ho můžete krásně pojmenovat. Dalším, ještě důležitějším účelem je
potom to, že změnou jednoho řádku s #define změníte snadno všechny výskyty na jinou hodnotu. Například
#define PRACOVNIKU
10
int rozpocet_na_platy;
int i;
rozpocet_na_platy = 100000;
for(i = 0;i < PRACOVNIKU;i++)
{
…
}
printf("kazdy pracovnik dostane %d Kc", rozpocet_na_platy / PRACOVNIKU);
Teď si představte, že potrebujete změnit ve vašem výpočtu počet pracovníků. Není nic snažšího, než místo
#define PRACOVNIKU 10
napsat
#define PRACOVNIKU 25
Můžete sice namítnout, že můžeme počet pracovníků uložit do proměnné, což je naprostá pravda. Ale jsou
i případy, kdy se to nehodí (zmíním se o tom v některém z podějších dílů).
Další direktivy
mezi další direktivy, na které můžete narazit, patří například jiné formy #define, #ifdef, #pragma apod. To jsou
všechno věci, bez kterých se zatím rozhodně obejdete.
Některé další operátory
Když jsme mluvily o operátorech jako +, -, * a /, vynechali jsme ještě některé jejich mutace (tohle už nejsou
znaménka jak je známe z matiky, ale trochu nestandardní znaménka z programování… budeme jim prostě říkat
nadále jen operátory). Kolikrát už jsme potřebovali přičíst k proměnné nějaké číslo, třeba 5? Zapisovali jsme to
vždy jako
char promenna;
promenna = promenna + 5;
Dá se to ale zapsat i daleko pěkněji. Potřebujete-li tedy některou proměnnou vynásobit, vydělit, zvětšit nebo
zmenšit o nějakou hodnotu nebo nějakou hodnotou, můžete na to použít operátory z tabulky. Ty tu nejsou od toho,
aby nám dodali nějakou novou funkčnost, ale aby nám usnadnili zápis našeho programu.
nový operátor
a += b
a -= b
a *= b
a /= b
jinak zapsáno
a=a+b
a=a-b
a=a*b
a=a/b
čte se
plus-rovná se; zvětšeno
mínus-rovná se; zmenšeno
krát-rovná se
děleno-rovná se
Názvy jsou spíše nápověda, můžete je slyšet i pod jiným jménem. Tyto operátory se běžně používají a můžete
je bez bázně a hany používat pořád (popravdě je to lepší, než psát delší formu, opět to totiž zvyšuje čitelnost
programu).
Operátory -- a ++
Pokud potřebujete proměnnou zvětšit nebo zmenšit o jedna, můžete k tomu použít operátory ++ a -- (nic
obdobného pro * a / neexistuje).
a = 1;
a++;
printf("%d", a); //tady se vytiskne 2
Má to ale jeden háček, můžete totiž napsat jak
a++;
tak
++a;
Rozdíl nastane, když by jste napsali (což už je celkem složité)
a = 0;
promenna = a++;
a
a = 0;
promenna = ++a;
V prvním případě se totiž promenna bude rovnat 0, v druhém 1. a++ totiž přičte k a číslo 1, ale "vrátí"-nahradí
se hodnotou původního a. U ++a je to naopak. Také se zvětší o jedna, ale je nahrazeno už o jedna zvětšeným
číslem místo čísla původního. Tedy
Napíšeme-li promenna = a++, je to stejné, jako bychom napsali
promenna = a;
a += 1;
Napíšeme-li promenna = a--, je to jako
a += 1;
promenna = a;
Já osobně používám ++ a -- jen pokud potřebuji proměnnou zvětšit nebo zmenšit, ale ničemu ji nepřiřazuji,
například for cykly jako
int i;
for(i = 0; i < cislo; i++)
{
…
}
a podobně. Nedoporučuji (a to celkem silně) vám používání operátoru ++ a --, když chcete něčemu přiřazovat
hodnotu, hlavně opět kvůli přehlednosti (a aby jste pak nehledali dlouho chybu, kolikrát už jsem kvůli vlastní lenosti
strávil hodiny hledáním chyb). Může se vám stát, že to někde uvidíte.
Program
Napiště program, který pro každého druhého pracovníka z 20 vypíše jeho číslo (nápověda: vypsat každého
druhého vlastně znamená posouvat se při procházení o dva)
#include<stdlib.h>
#include<stdio.h>
#include<conio.h>
#define PRACOVNIKU 20
void main()
{
int i;
for(i = 0;i < PRACOVNIKU;i += 2)
{
printf("%d\n", i);
}
}
Příště
Přijdou na řadu tzv. pointery. To je jedna z nejdůležitějších věci (vše co bereme je důležité, ale pointery
obzvláště) a bude potřeba jít malinko do nitra počítače a vysvětlit si, jak to funguje s pamětí.
Programování v C/C++ 8. díl
Dnes načneme téma, které je nadmíru důležité a rozdělíme ho dokonce na několik článků. Tím tématem jsou
ukazatele, nebo anglicky pointery (oba výrazy se používají).
Pointery
Jak vypadá paměť
Nejprve si řekneme, jak zhruba vypadá paměť v počítači (RAM). Ono nám ani tak nejde o to, jak ve skutečnosti
vypadá, ale jak s ní pracujeme. Pro naše účely je tedy paměť spousta "políček" nebo "buněk", které jdou za sebou.
Každé takové políčko je jeden byte [bajt]. Když to tedy shrneme, spousta políček o velikosti jednoho bytu za sebou.
Co je to byte[bajt]: jednotka počítačové paměti. Skládá se z osmi bitu [bit], přičemž každý bit může být buď 1
nebo 0 (to jsou právě ty často zmiňované jedničky a nuly). Na úrovni bitů my ale pracovat nebudeme, budeme
pracovat s byty.
Každé to políčko má svoje číslo. Takovému číslu se říká adresa, nebo delším názvem adresa v paměti. Jako
kdyby každá buňka paměti byl jeden obyvatel velkého sídliště :-).
Proměnné a paměť
Každá proměnná musí být samozřejmě někde uložená v paměti. Mohli by jste se ptát, kolik té paměti nám
sebere. Konkrétní velikosti jednotlivých druhů proměnných uvádím pro orientaci v tabulce. Nejsou ani tak důležíté,
důležité je si uvědomit, že hodně z nich má přes 1 bajt.
typ proměnné její velikost v bytech
char
1
short, short int
int, long, long int
2
4
float
4
double
8
podle operačního systému se velikost proměnných v bytech může lišit. Tato tabulka platí pro 32-bitové operační
systémy jako Windows 95/98/NT/2000/Me a podobné, Linux a jiné Unixové sýstémy. Neplatí to ale například vždy
v DOSu, kde jsou některé velikosti menší.
Stack a heap
Kvůli některým vlastnostem pointerů je potřeba povědět si, co je to stack (tzv. zásobník) a co je to heap (tzv.
halda). Já budu používat v článku anglickou terminologii, ale běžně se používá oboje. Obecně pro stack i pro heap
platí, že jsou to místa v paměti, lépe řečeno kusy paměti, které jsou vyhrazené pro nějaký specifický účel a podle
toho se odvíjí i jejich vlastnosti.
Stack je kus paměti, který se vytvoří při startu aplikace (tedy ono je to trochu jinak, ale v zásadě). Ukládájí se na
něj všechny proměnné, které ve funkci deklarujete (kromě globálních). Když jsme se bavili o parametrech funkcí,
říkali jsme si, že se v těle proměnné vytvoří duplikáty proměnných, které jsme předali jako parametr. Ty se ve
skutečnosti, stejně jako všechny proměnné vytvořené v těle funkce, přidají na stack. Když pak z funkce vyskočíte,
zase se ze stacku umažou.
S heapem už je to jednodušší. To je ta zbylá část paměti, kterou můžeme používat (sou i místa v paměti, které
používat nemůžeme). Stack a heap se nepřekrývají, ale platí pro ně to stejné číslování adres. To znamená, že jsou
vlastně v paměti na políčkách umístěny za sebou, ale jeden nemůže přerůst do druhého. V pravdě bývají jejich
velikosti většinou pevné.
ilustrační schéma paměti (ve skutečnosti jsou políček miliony)
Pointery
Teď už vám prozradím, co přesně je pointer. Je to vlastně úplně normální proměnná (identická s int, long nebo
long int), a číslo, které jí přiřadíme, je adresa. Stejně jako normalní int se ukládá na stack a když se skončí funkce,
ve které je deklarována, umaže se. Přesně jako každá normální proměnná.
Takový pointer tedy obsahuje adresu. Ta adresa nám ukazuje do paměti, na nějaké políčko (100 na 100.
políčko, 101 na 101. políčko atp). Ale kompilátor ještě chce vědět, na co pointer ukazuje. Jestli na tom místě, na
které ukazuje, leží (začíná) float, nebo jestli tam leží třeba char. To mu také musíme říct.
Jak se deklaruje pointer
Pokud bychom tedy chtěli nadeklarovat proměnnou pointer, budeme postupovat takto
typ_promenne_na_ktery_pointer_ukazuje
*jmeno_promenne_tedy_vlastne_pointeru;
Mnoho lidí pointery nepochopí, přitom je to snadné. Pointer (to, že je to pointer a ne obyčejná proměnná, značí
právě ta * v jeho deklaraci) je vždy proměnná, která obsahuje číslo. Ta proměnná má v 32-bitových systémech jako
Windows vždy velikost 4 byty (aha! 4 byty je 32 bitů!). To, na co ukazuje pointer, se překladači řekne na přes
typ_promenne_na_ktery_pointer_ukazuje.
Tohle je opravdu důležité, klidně si to pročtěte vícekrát, nesyďte se.
Celé se to potom čte jako pointer na typ, konkrétně tedy třeba "pointer na float", "pointer na double" ap.
int *pointer_na_int; float *pointer_na_float, *druhy_pointer_na_float;
Kam mohou pointery ukazovat
Ne, že by pointery nemohli ukazovat na proměnné na stacku, ale jejich účelem je možnost ukazovat na heap,
tedy mimo stack. Problémem stacku totiž bývá, že má často hodně omezenou velikost a proměnné se na něj tedy
nevejdou (řekněme, že budeme schopni proměnných udělat tisíce) (když se zamyslíte, že pointer má 4 byty a
většina proměnných stejně nebo míň, mělo by vám to přijít podivné nahrazovat malou proměnnou jako char třeba
pointerem na char. To vysvětlíme v přespříštím díle)
Shrnutí
Tahle lekce byla hlavně teoretická (z praxe jste se akorát dozvěděli, jak se pointer zapisuje). Nesnažte se zatím
s pointery pracovat. Ještě zdaleka nevíte jak a taky by se vám mohlo stát (pokud by jste byli dostatečně zvídaví),
že se vám povede "shodit" nebo "zaseknout" počítač.
Měli by jste si být vědomi toho, co je to stack, že se na něj proměnné přidávají a ubírají se zněj a kdy (ty
deklarované ve funkci se přidávají na začátku funkce, odebírají po jejím skončeni). Měli by jste vědět, že heap je
nějaký kus paměti. Taky je dobré vědět, jak se zapisuje pointer a co to je.
Programování v C/C++ 9. díl
Je tu dokončení minulého dílu o pointrech. Tentokrát už se dozvíme, jak se s pointery pracuje a co všechno se
s nimi dá dělat. Na konci nastíníme i k čemu nám budou do budoucna.
Co je to alokace
Posledně jsme si řekli, že pointer ukazuje kamsi do paměti. Ovšem my nemůžeme pointer vzít, přiřadit mu
libovolnou adresu a na ni jen tak něco zapsat (jak se do poitneru přiřazuje hodnota a jak se poitneru přiřazuje
adresa se dozvíte níže. Pokud vám dělá problém rozlišit tyto dva pojmy, přečtěte si znovu minulý díl). Problém by
byl v tom, že náš pointer by potom ukazoval na nějaké místo v paměti, kde už mohou být uloženy různé věci. Co
kdyby se vám povedlo ukazovat na místo, na které už ukazujete jinde (například když by jedna vaše funkce
pracovala s nějakým místem v paměti a poté, o několik týdnu později, by jste dopsali jinou funkci, která by
pracovala s tím samým místem paměti, jelikož vy už jste zapomněli, že s tímto místem pracujete)?
Funguje to tak, že když chcete něco do paměti uložit, řeknete to systému a ten vám vyhradí určitý úsek
z heapu. Současně s tím vám samozřejmě řekne, jakou adresu tento kus heapu má.
Tedy chcete-li něco uložit do paměti:
· řeknete to systému
· systém vám řekne, na jaké adrese začíná pro vás vyhrazená paměť.
Tomuto všemu se říká alokace. Vy tedy alokujete například jeden float a operační systém vám řekne, na jakou
adresu ho můžete uložit. Stejně je to samozřejmě i s ostatními typy.
Jak se alokuje
Když už víme co je to alokace, řekneme si ještě, jak se provádí. Protože víme, že nám systém vrátí adresu do
paměti, musíme ji mít kam uložit. Když bychom chtěli uložit adresu například na float, napsali bychom
float *poitner_na_float;
Což tedy mluví samo za sebe. pointer_na_float je pointer, ve kterém je uložena adresa, která ukazuje na float.
Teď když se zeptáme systému, kde bychom mohli svůj float uložit, bude to schematicky (new je anglicky nový,
jako "nový kus paměti")
nejky_pointer = new typ_na_ktery_bude_pointer_ukazovat;
nebo s naším floatem
poitner_na_float = new float;
Systém teď ví, že má vyhradit paměť pro float, vyhradí ji (na heapu!! nikdy ne na stacku) a vrátí vám adresu na
začátek tohoto kusu paměti, kterou vy si uložíte do pointer_na_float.
Dealokace
Když už nepotřebujete daný kus paměti používat - taková situace nastává nejčastěji na konci programu - musíte
zase systému říct, že už paměť nepotřebujete. Tohle by jste měli dělat vždy! Když to budete opomíjet, může se
vám stát, že systému bude docházet pamět (protože vy jste systému neřekli, že už paměť nepotřebujete a on vám
jí pořád drží rezervovanou). Je to jako kdyby jste nevraceli knihy do půjčovny. Za chvíli nebude co půjčovat. To je
také důvod, proč jsem minule vysvětlil, co je to stack a co je heap. Protože pointer, jako každá normální proměnná,
je na stacku, zatímco ty alokovaná data jsou v heapu. Jenomže to, že přestane existovat pointer ještě neznamená,
že se odalokují data na heapu. Tohle v budoucnu využijeme ve svůj prospěch, tentokrát to ale uvádím, aby jste
věděli, proč se má dealokovat. (důvodem k tomu, že se se zánikem pointeru automaticky nedealokují i data je ten,
že na jednu adresu může ukazovat i více pointerů (což není špatně, pokud to tak chcete))
Syntaxe je následující (delete je anglicky smazat)
delete nejaky_pointer;
V našem případě tedy
delete pointer_na_float;
Měli by jste dealokovat jenom platné pointery (viz níže).
Práce s pointery
Jak je nám známo, pointer je vlastně normální proměnná s hvězdičkou navíc, která uchovává adresu do paměti.
Jak ale můžeme pracovat s pamětí, na kterou pointer ukazuje? Jak můžeme do pointeru přiřazovat adresy?
Začneme naší druhou otázkou, tedy jak můžeme do pointeru přiřazovat adresy? Celkem jednoduše! Jako
kdybychom chtěli do normální proměnné přiřazovat hodnotu. Tedy
pointer = adresa;
Než budu pokračovat, rád bych se zmínil o tom, že by vám kompilátor neměl dovolit přiřadit do pointeru číslo,
které by vyjadřovalo adresu (no neměl, on vám to nedovolí), tedy například
pointer_na_float = 456789; //tohle vám kompilátor nedovolí
Kompilátor by vám měl dovolit do pointeru přiřadit jen odpovídající typ pointeru, tedy pointer na float do pointeru
na float, do pointeru na char pointer na char ap.
float *pointer_1, *pointer_2;
char *pointer_na_char;
pointer_1 = pointer_2; //tohle jde
pointer_na_char = pointer_1; //tohle je spatne!
Tak to by byla práce s adresami.
Ale co když chcete pracovat s tou proměnnou, na kterou pointer ukazuje? Dělá se to tak, že se v kódu před
pointer napíše hvězdička:
char *pointer_na_char;
char normalni_promenna_typu_char;
*pointer_na_char = 25; //tady je videt ta pridana hvezdicka
normalni_promenna_typu_char = *pointer_na_char; //tady taky
V prvním případě se do kusu paměti (tedy do proměnné typu char uložené na heapu), na který pointer_na_char
ukazuje, zkopíruje hodnota 25. V druhém případě se hodnota proměnné, na kterou pointer_na_char ukazuje,
zkopíruje do naší normální proměnné.
Takový zápis s hvězdičkou pak kompilátor chápe stejně, jako kdyby jste pracovali s normální proměnnou, a čte
se jako "to, na co ukazuje pointer na char", tedy například "to, na co ukazuje pointer na char, se rovná 25".
NULL a co je tam, když tam nic není??
Jediná číselná hodnota, kterou vám kompilátor dovolí do pointeru přiřadit, je 0. A to proto, že 0 je dohodou
adresa, která je neplatná. Když tedy chcete říct, že pointer ještě na nic neukazuje, přiřadíte mu hodnotu 0. Místo 0
se ale píše NULL, kvůli přehlednosti, což je někde zapsáno jako
#define NULL 0
takže je to vlastně to samé jako psát 0. Taky se vám při alokaci může stát, že new vrátí NULL. To znamená, že
není dost paměti na heapu pro vaši proměnnou. Je dobrým zvykem kontrolovat, jestli se alokace opravdu povedla.
Proč my ale vlastně přiřazujeme pointeru 0? Protože my nevíme, jaká adresa je v pointeru uložena. To je
v postatě věc náhody, záleží to na tom co tam bylo předtím (když paměť byla používána jinou aplikací), jestli je
zrovna úplněk a podobně. Tedy nic, co bychom dovedli předpovědět. Bude v tom něco naprosto náhodného. Proto
je dobré nulovat (nebo NULLovat?;-)) pointery.
Když pointer ukazuje na stack
Všechna alokovaná data jsou sice na heapu, ale to neznamená, že pointer nemůže ukazovat na stack! To on
totiž může a celkem často se toho využívá. Kdybychom třeba chtěli (z důvodů, které osvětlím v nějakém pozdějším
díle) předat jako parametr funkce pointer, ale programátor, který by naši funkci používal, by měl hodnotu uloženou
jenom v normální proměnné, tedy na stacku. Když vy chcete pointer a on má jenom normální proměnnou, udělá se
to tak, že vám dá adresu na svoji normální proměnnou, tedy na stack.
Teď už by jste měli i vědět, proč by v žádném případě neměl zkopírovat hodnotu své proměnné do vašeho
pointeru (krom toho, že to nejde nijak zapsat, když předáváme parametry funkci)… zamyslete se… protože vy od
něj chcete pointer, tedy místo v paměti, ze kterého si budete "brát" svá data, a on se na nějakou úplně náhodnou
adresu (viz výše - ten parametr samozřejmě obsahuje nějakou náhodnou adresu, tedy ukazuje na nějaké špatné
místo) snaží něco nahrát. Vy chcete znát místo v paměti, ne zkopírovat do pointeru nějaká data. Z toho plyne, že
vy ani ten pointer nealokujete! To jen tak mimochodem.
Pokud ale chcete předat adresu na nějakou proměnnou (ve stacku):
int *pointer_na_int;
int normalni_int;
pointer_na_int = &normalni_int;
Napíšete před vaší normální proměnnou & a kompilátor ji v tomto zápisu bude vnímat jako pointer. Čte se to
vše jako "adresa/pointer na normalni int", respektive "pointer na int se rovná adrese normálního int").
Shrnutí
Naučili jsme se, co je to alokace, kde se nachází alokovaná data (heap), proč bychom měli dealokovat, že
můžeme ukazovat i na stack, nejenom na heap. Dále také jak se dá pracovat s proměnou, na kterou máme
pointer, i jak získat pointer na normální proměnnou.
Slušné chování
Je pár zásad, které by jste vždy měli dodržovat.
·
Kontrolovat, jestli jste naalokovali v pořádku (když nebudete a uživatel si pustí k vašemu programu ještě film,
bude něco stahovat atp. atp., vaše aplikace spadne).
·
·
·
·
Odalokovávat jen platné pointery
Vždy zapisovat jen do alokovaných pointerů
Pokud používáte adresu na stack, být si jistý(á), že v době, kdy budete s obsahem proměnné, na kterou
pointer ukazovat, pracovat, bude proměnná ještě stále na stacku. Tedy když přiřadíte pointeru adresu na
nějakou proměnnou z dané funkce a budete s ní chtít pracovat poté, co funkce skončí, program spadne.
Proměnná už totiž nebude na stacku, neboť funkce už skončila a všechny její proměnné se tedy vymazali ze
stacku (viz minulý díl)
být opatrní; když spadne program, je to na 90% kvůli práci s pointery.
Cvičení
Napište program, kde budou tři pointery na int. Do prvního se uloží číslo, do druhého číslo a do třetího výsledek
násobení.
//nase tri pointery
int *cislo1 = NULL, *cislo2 = NULL, *cislo3 = NULL;
//pokud jsou platne, dealokuje poitnery
void Uklid()
{
if(cislo1 != NULL)
delete cislo1;
if(cislo2 != NULL)
delete cislo2;
if(cislo3 != NULL)
delete cislo3;
}
int main()
{
//naalokujeme cislo1 a zkusime, jestli se nam alokace opravdu povedla
cislo1 = new int;
//nepovedla, ukoncime program
if(cislo1 == NULL)
{
//uklidime a ukoncime aplikaci
Uklid();
return -1;
}
//naalokujeme cislo2 a zkusime, jestli se nam alokace opravdu povedla
cislo2 = new int;
if(cislo2 == NULL)
{
Uklid();
return -1;
}
//naalokujeme cislo3 a zkusime, jestli se nam alokace opravdu povedla
cislo3 = new int;
if(cislo3 == NULL)
{
Uklid();
return -1;
}
//ted uz mame vsechny tri pointery naalokovane, jdeme s nimi pracovat
*cislo1 = 2;
//priradime hodnoty
*cislo2 = 3;
//vysledek do cislo3
*cislo3 = *cislo2 * *cislo1;
printf("%d * %d = %d", *cislo1, *cislo2, *cislo3);
Uklid();
}
Jan Beneš

Podobné dokumenty

Jazyk C

Jazyk C int main(){printf("Hello, world!\n");return 0;}

Více

Hodnocení povahových vlastností československých vlčáků podle

Hodnocení povahových vlastností československých vlčáků podle bála se lidí. Období socializace probíhá mezi 3. týdnem života až do 14 týdnů, ale není ukončeno najednou. Toto období bylo původně nazváno kritická perioda, ale nyní je označováno jako senzitivní ...

Více

obchodník

obchodník Čo chýba slovenským startupom, prečo sa rozhodol investovať do startupov spolu s 42angels.com, kde robia žiadatelia o investíciu chyby a ako sa dá predať startup z domu v pyžame do Ameriky, hovorí ...

Více

Praktická cvičení algoritmů - Informatika Ostravská Univerzita

Praktická cvičení algoritmů - Informatika Ostravská Univerzita MAXINT - obsahuje největší dovolenou hodnotu , kterou lze uložit do proměnné typu INTEGER, což je 32 767. MAXLONGINT - obsahuje největší dovolenou hodnotu , kterou lze uložit do proměnné typu LONGI...

Více

české PDF

české PDF  O OK: stáhl se celý soubor. Přesnější popis chyby je možno získat z object request->stat.state. To je některá z konstant S xxx, která určuje přímo typ chyby, ke které došlo, nebo stav, ve kterém ...

Více

Jaroslav SKÁLA poznámky k přípravě na reparát

Jaroslav SKÁLA poznámky k přípravě na reparát 20. KVALITNÍ ZPRACOVÁNÍ FORMULÁŘOVÝCH DAT ........................................................... 14 21. PRÁCE SE SOUBORY ..........................................................................

Více

Příručka_programování_3D_tisk.

Příručka_programování_3D_tisk. objektu v režimu náhledu OpenCSG a nemá žádný vliv na vykreslování mnohostěnu syntaxe: polyhedron(points = [ [x, y, z], ... ], faces = [ [p1, p2, p3..], ... ], convexity = N); příklad čtvercové pyr...

Více