Kurz C++ (2.)


Dostává se vám do rukou druhá lekce kurzu o programování v C++. V této lekci se konečně dostaneme k vlastnímu programování, i když se bude jednat o pouhé základy C bez kterých se ovšem neobejdete. Součástí kurzu je příklad, na kterém budeme společně pracovat a který si nakonec budete moci uložit na disk a spustit. Vřele ovšem doporučuji si příklad napsat vlastníma rukama a třeba si ho zkusit modifikovat.

2.1 Základní datové typy

Datové typy jsou typy proměnných, které je možno vytvořit. Proměnná může představovat paměťovou buňku, ale většinou se jedná o blok paměti, kde je uložena informace jistého typu. Právě typ proměnné je nejdůležitější atribut proměnné. Jedna proměnná může uchovat záporné číslo a druhá zase jen kladné. Musíte předem vědět k čemu proměnnou chcete využít, aby program fungoval správně. Některé chyby odhalí kompilátor při překladu, ale pozor si musíte dát především na ty skryté, které se projeví nesprávnou funkcí programu. V průběhu programu se hodnota proměnné mění, podle druhu operace. Můžeme například sešíst dvě proměnné stejného typu atd. Lze použít i jisté konverze typů, ale o tom si podrobně povíme v některé příští lekci.

Přehled nejpoužívanějších jednoduchých typů jazyka C :

Deklarace Typ informace Velikost v bytech
int celé číslo 4 (2)
long celé číslo 4
short celé číslo 2
float reálné číslo 4
double reálné číslo 8
long doublereálné číslo 8
char znak 1
unsigned char byte 1
BOOL 0 nebo 1 (v C definován jako int) jako int
unsigned short 16-bitové slovo 2
unsigned long dvojnásobné slovo 4
unsigned int neznaménkový int 4
a další   

Některé typy mají dvě varianty a to sice znaménkové (signed) a neznaménkové (unsigned). Implicitně jsou tyto typy znaménkové. Pokud chcete vytvořit neznaménkový typ, stačí před klíčové slovo typu vložit další klíčové slovo unsigned. Co ale přesně znamená znaménkový a neznaménkový typ? Je to snadné. Znaménkové typy mohou nabývat i záporných hodnot, ale mají oproti neznaménkovým typům poloviční maximální hodnotu, takže například platí:

TypRozsah hodnot
char-128 až +127
unsigned char0 až 255

Maximální hodnotu znaménkové proměnné zjistíme takto: 2^(n-1), kde n je počet bitů, které proměnná zabírá v paměti (viz tabulka nahoře - 1 bajt má 8 bitů).


Poznámka: V 16-bitovém OS má typ int int velikost 2 bajty, ale ve Windows a jiných 32-bitových OS má 4 bajty! Co z toho plyne? Pokud například pod DOSem zapíšete do souboru dvě proměnné typu int (2x2bajty) a pak je pod Windows přečtete, vznikne chyba, protože původně dvě hodnoty se přečtou jako jedna a při pokusu o další čtení program vyhodí nejspíš vyjímku (exception). Zabráníte tomu používáním vhodnějších typů pro zápis do souboru.


2.2 Definice proměnné v programu


Za prvé si musíme uvědomit, jaký typ informace chceme „skladovat“. Pak můžeme určit datový typ proměnné.
Tak tedy jak definovat proměnnou v programu? Definovat znamená, že překladač přidělí jméno a paměť pro naší proměnnou zatímco deklarace pouze přiřadí jméno proměnné – překladač nealokuje žádnou paměť!!!

Příklad definice proměnné :

  int i;
  double d = 1.0;

Takto vytvoříme celočíselnou a reálnou proměnnou. Reálná proměnná typu double je ihned inicializována na hodnotu 1.0. Proměnná totiž po vytvoření není inicializována, takže v ní může prakticky cokoliv (většinou je to dost obludné záporné číslo) a tímto se vyvarujem zbytečných chyb. Překladač nás upozorní pokud používáme nějakou proměnnou, aniž by jsme jí před tím inicializovali.

Definice může být buď vně nebo uvnitř těla funkce. Proměnným, které jsou mimo tělo jakékoliv funkce říkáme globální a jsou přístupné ze všech funkcí daného modulu (souboru .cpp). Globální proměnná je zrušena, když je ukončen celý program. Pokud je proměnná definována uvnitř těla funkce, její platnost se omezuje pouze na dobu trvání funkce tzn. že když program ukončí funkci zároveň je zrušena i lokální proměnná. K lokální proměnné můžeme přistupovat pouze z funkce, kde je tato proměnná definována.

Příklad definice globální a lokální proměnné :

  int i; //globalni celociselna promenna viditelna v celem programu
  main()
  {
       double d; //lokalni realna promenna viditelna pouze z funkce main()
  }


Poznámka:Obecně platí, že v souborech s příponou .cpp provádíme definici a hlavičkových souborech .h provádíme deklaraci proměnných a funkcí. K čemu tedy používáme hlavičkové soubory? Právě k deklaraci globálních proměnných či deklaraci funkčních prototypů (vše bude vysvětleno později). Navíc, pokud používáte více modulů najednou (více na sobě závislých implementačních souborů .cpp), můžete použít společný hlavičkový soubor common.h, ve kterém budou definovány společné globální proměnné a funkce. Více si o hlavičkových souborech povíme dál v této lekci.


2.3. Přiřazení

Nyní máme definované jméno proměnné v programu a můžeme začít s proměnnou pracovat. Kompilátor nám přiřadil buňky v paměti, takže můžeme získat adresu naší proměnné. Adresu využijeme až se budeme zabývat ukazately (ukazatel = pointer) někdy v příštích lekcích, ale dneska to pro nás znamená, že proměnná, právě protože má adresu v paměti, je tzv. l-hodnota (l-value). Hodnotu proměnné přiřadíme operátorem „rovná se“ ( = ). Takže například, pokud chceme do proměnné i uložit číslo 5, napíšeme :

  int i; // definice promenne
  i = 5; // v i je nyní hodnota 5


Důležité ovšem je, aby na levé straně příkazu byla vždy l-hodnota. L-hodnota představuje adresu, tedy proměnná je l-hodnota, konstatna 5 l-hodnotou není. Následuje triviální příklad:

  int u = 3; // definice a inicializace promenne u
  i = u; // promenna muze byt vlevo i vpravo, i = 3
  i = 2 + u; // i = 5
  i = u * 5 + 10; // i = 25, vyraz muze byt jen na prave strane
  i + 1 = u; // CHYBA!!! Prekladac nastesti tyto chyby odhali pri prekladu...
  1 = u; // JESTE VETSI CHYBA!!!

V C je navíc možné několikanásobné přiřazení. Takto můžeme inicializovat několik proměnných najednou.

  u = i = 5; //promenne i a u budou mit hodnotu 5


Poznámka: Všimněte si, že na konci každého řádku je středník! Každý příkaz v C je ukončen středníkem. Mezi základní slušné mravy programátora patří psát každý příkaz na nový řádek.


2.4. Funkce main()

Ve starém C musel mít každý program funkci main(), která se použila jako vstupní bod (entry point) do vašeho programu. To znamená, že když se váš program zkompiloval a spustil, jako první se vždy zavolala tato funkce. Z této funkce jste pak volali vaše funkce a váš program se prováděl dokud funkce main() neskončila. Takže touto funkcí vše začalo a taky to pěkně skončilo. Toto platilo až na vyjímky (pokud jste třeba pracovali s více vlákny (threads)) na 100%.

V objektovém modelu programování se na tomto až zas tak moc nemění. A proč tedy o tom vůbec mluvím? Ve Visual C++ (VC++), ve kterém se nyní učíte programovat pracují programy zcela jinak! Neplatí to však jen pro VC++. Vývojové nástroje jako Visual Basic nebo Delphi pracují naprosto stejně. Je to dáno tím, že se jedná o nástroje pro Windows. Takže původce těchto změn jsou právě Windows (a jiné multitaskingové OS - znamená to, že může pracovat více programů najednou). Způsob, kterým pracují Windows je na delší povídání a nás zatím nezajímá, ale nebojte se určitě se k němu časem prokousáme.

Nyní vám stačí vědět, že programy, které budeme vytvářet budou mít "zatím" jen funkci main(), kterou začíná a končí program. Funkce je ohraničena složenými závorkami {}, jako každá funkce, kterou kdy v C napíšete. Funkci main() nikdy nebudete muset volat z vašeho kódu. Tuto funkci volá operační systém, ať už stařičký DOS nebo padavá Windows.

Funkce main() může, ale nemusí mít parametry, ale zpravidla vrací nějakou návratovou hodnotu, většinou typu int. Pokud má funkce main() parametry, tak je to řetězec, který napíšete jako parametr, když program spouštíte y příkazové řádky. Možná se vám to zdá složité a tak uvedu jednoduchý příklad, jak taková funkce může vypadat.
Jak tedy může funkce main() vypadat? V různých vývojových prostředích různě, například takto:

   //prvni priklad
   void main()
   {//zde jsou videt slozene zavorky

     //zadne vstupni parametry
     //zadna navratova hodnota
     //i takovato funkce se muze vyskytnout
     //ale neni to zvykem

   }

   //jiny priklad fce main(), casteji viditelny
   int main()
   {
     //zadne vstupni parametry
     //ale vraci hodnotu, nejcasteji int


     return 0;
   }

   //tak do tretice priklad fce main()
   int main(char args[])
   {
     //tato fce prijima jako parametr retezec z prikazove radky
     //a vraci hodnotu int jako v predchozim priklade
     //jmeno vstupniho parametru se muze lisit od ruznych IDE
     //dokonce jich muze byt i vic!


     return 0;
   }


Poznámka: Opět si všimněte dobrého zvyku. Vidíte, že tělo funkce je o kousek odsazeno oproti složeným závorkám a jmenu funkce. Toto odsazení samozřejmě nemá vliv na funkci programu, ale výrazně zpřehledňuje kód!

O funkcích si samozřejmě povíme více nejspíš v příští lekci našeho kurzu. Když program spouštíte z příkazového řádku (Windows) nebo v DOSu, můžete zapsat nějaký parametr, který je pak předán funkci main() jako argument args[], což je pole znaků čili řetězec. Tedy například:

   C:\WINNT\
   C:\WINNT\cd..
   C:\cd Programs
   C:\Programs\cd Main
   C:\Programs\Main\main.exe test

Zde předáváte funkci main() řetězec "test", to znamená, že po spuštění programu v proměnné args[] bude opravdu řetězec "test" a vy s ním můžete cokoliv dělat, třeba vypsat na obrazovku. Takto může uživatel vašeho programu měnit chování programu, aniž by znal, jak vypadá vlastní kód.

My si ukážeme konkretní příklad, jak parametry fungují ve Visual C++, pokud vytvoříte konzolovou aplikaci (postup je vysvětlený v kurzu o IDE VC++). Když se podíváte na funkci main(), určitě vám přijde složitá, protože vůbec nevypadá tak, jak jsem před chvilkou říkal. Máte pravdu ve VC++ vypadá funkce main() jinak než jinde. Na první pohled vidíte, že má dva parametry, místo jednoho. První parametr argc určuje počet vámi zadaných parametrů v příkazové řádce. Jeho minimální hodnota je 1, protože se jako parametr bere i název programu, tedy například "main.exe" a i tento řetězec "vniká" do funkce main(). Druhý parametr argv[] je trošičku složitější, ale o to vychytanější. Je to ukazatel (že nevíte co to je? tím se zatím netrapte) čili adresa nějaké buňky v paměti. Ukazatel na pole znaků. Zní to nesmírně složitě, ale ve skutečnosti je to velmi jednoduché. Zkrátka parametr argv[] uchovává všechny řetězce, které jste zapsali do příkazové řádky. K jednotlivým řetězcům přistupujeme pomocí indexů v intervalu 0 až (argc-1). Následující příklad vypíše všechny parametry v příkazové řádce nezávisle na jejich počtu.

   #include "stdafx.h"

   //vlozeni standardniho hlavickoveho souboru (kvuli fci cout)
   #include <iostream.h>

   int main(int argc, char* argv[])
   {
     //smycka typu for bezi od 0 do argc-1
     for(int i = 0; i < argc; i++) {
         //funce cout vypisuje jednotlive parametry
         //na obrazovku
         //vsimnete si formatovaní jednotlivych radku
         //funkci cout a cin si popiseme pozdeji

         cout << "Argument #" << i << " :" << argv[i] << "\n";
     }

    return 0;
   }



Poznámka: O cyklu for se více dozvíte v příští lekci, která se bude zabývat mimojiné také iteračními cykly! Opět si ale všimněte odsazení uvnitř těla cyklu.

2.5. Hlavičkové soubory a funkce cout a cin

"Funkce" cout a cin jsou deklarovány v hlavičkovém souboru iostream.h, který musíte vložit do svého programu, abyste je mohli použít. Vkládání hlavičkových souborů je vidět na předchozím příkladu. Pokud se jedná o standardní hlavičkový soubor, tzn. o soubor vytvořený týmem fy Microsoft, uzavírá se jméno souboru do lomených závorek: <>. Pokud jde o váš hlavičkový soubor, který jste napsali vy, uzavírá se jméno souboru do úvozovek: "". IDE tak pozná odkud má vkládat hlavičkové soubory. Standardní hlavičkové soubory jsou uloženy v adresáři vlastního IDE (např. C:\Program Files\Microsoft Visual Studio\VC98\Include) a vaše soubory jsou uloženy v adresáři vašeho programu (např. C:\Programs\Main\).

Možná se divíte, že za funkcemi cout a cin nepíši závorky jako u funkce main(). Je to proto, protože cout a cin vlastně nejsou funkce:-) Jedná se o objekty jazyka C++, které používají tzv. proudy (streams). Proudy mohou být různé, například vstup a výstup na tiskárnu, do souboru a na obrazovku (cout a cin). V C pod DOSem se používali funkce printf() a scanf(), ale my budeme požívat objekty cin a cout, protože se učíme C++. Pro přístup k proudům se používají speciální operátory: << a >>, jak jste si jistě všimli v předchozím případě.

Objekt cout, který jsme před chvilkou použili, se používá pro výstup na monitor. Použití si ukážeme na jednoduchém příkladu:

   int i = 5;//definice promene i = 5

   cout << "Ahoj\n";// vypise na monitor "Ahoj" a odradkuje
   cout << i << "\n";// vypise na monitor obsah promene i a odradkuje
   cout << "Obsah i: " << i << "\n";//vypise na monitor "Obsah i:", obsah i a odradkuje

Je vidět, že cout můžeme použít k zobrazení řetězce i obsahu proměné. Asi se divíte, copak dělají znaky "\n". Tento "dvojznak" je ve skutečnosti jeden znak, který zajistí odřádkování. Odborně se nazývá escape sekvence, kterých je celá řada:

SekvenceHodnotaVýznam
\n0x0Anová řádka (newline, linefeed - LF)
\r0x0Dnávrat na začátek řádky (carrige return - CR)
\f0x0Cnová stránka (formfeed - FF)
\t0x09tabulátor (tab - HT)
\b0x08posun doleva (backspace - BS)
\a0x07písknutí (alert - BELL)
\\0x5Czpětné lomítko (backslash)
\'0x2Capostrof (single quote)
\00x00nulový znak (null character - NUL)


Poznámka: Pokud znáte HTML, tak escape sekvence jsou něco podobného jako znaková entita.

Objekt cin (nyní víte, že se jedná o objekt) se používá velmi podobně. Následuje opět jednoduchý příklad:

   int i; //definice promene i

   cin >> i;//nacteni ciselne hodnoty do promene i
   cout << "Obsah i: " << i << "\n";//vypsani obsahu i (viz vyse)

Všiměte si, že "šipky" u cin jsou na opačnou stranu než u cout. To proto, že se jedná o opačný směr proudu - z klávesnice do proměnné je >>, zatímco z proměnné na monitor jsou "šipky" opačně <<. A to je celé kouzlo vstupu a výstupu na obrazovku. Pokud jste to pochopili, pak máte vyhráno a pokud ne, tak si s tím nelámejte hlavu, protože časem se všechno vyjasní :-)

2.6. Operátory

Pomocí operátorů provádíme aritmetické, ale i logické operace s proměnnými. Nyní už víte, jak proměnnou vytvořit (definovat), takže už nezbývá nic jiného, než se naučit s proměnnými pracovat. Uvedu krátký přehled, kde by měly být uvedeny všechny základní operátory. Všechny tyto operátory jsou binární tzn, že potřebují dva operandy a vrací výsledek.

Binární operátory
Význam operace Syntaxe Příklad
Sčítání + 50 + 50 = 100
Odečítání - 100 - 50 = 50
Násobení * 10 * 10 = 100
Dělení / 100 / 10 = 10
Dělení modulo % 7 % 3 = 1 (zbytek po dělení)
Bitový posun doleva << 10 >> 1 = 5
Bitový posun doprava >> 10 << 1 = 20
Logický součin & 1 & 2 = 0 nebo 3 & 6 = 2
Logický součet | 1 | 2 = 3 nebo 3 | 6 = 7


Dále rozlišujeme unární operátorý, kterým stačí jeden operand.

Unární operátory
Význam operace Syntaxe Příklad
Unární plus - kladné číslo + +12547
Unární minus - záporné číslo - -12547

Speciální unární operátory

  • inkrement ++
  • dekrement --
Význam:
    ++proměnná
  • proměnná je inkrementována (zvětšená o jedničku) před použitím
  • nejdříve je proměnná zvětšena o jedničku a poté je vrácena tato nová hodnota
    proměnná--
  • proměnná je dekrementována (zmenšená o jedničku) před použitím
  • je vrácena původní hodnota proměnné a poté je proměnná zmenšena o jedničku

Poznámka:Operátor inkrement a dekrement lze použít pouze na l-hodnotu čili proměnnou. Oba operátory mohou být použity buď jako předpona (prefix) nebo jako přípona (suffix).

C navíc poskytuje celou řadu dalších přiřazovacích operátorů, které oproti binárním operátorům zkracují zápis.

Zkrácený zapisNormální zápis
l-hodnota += výrazl-hodnota = l-hodnota + výraz
l-hodnota -= výrazl-hodnota = l-hodnota - výraz
l-hodnota *= výrazl-hodnota = l-hodnota * výraz
l-hodnota /= výrazl-hodnota = l-hodnota / výraz
l-hodnota %= výrazl-hodnota = l-hodnota % výraz
l-hodnota <<= výrazl-hodnota = l-hodnota << výraz
l-hodnota >>= výrazl-hodnota = l-hodnota >> výraz
l-hodnota &= výrazl-hodnota = l-hodnota & výraz
l-hodnota |= výrazl-hodnota = l-hodnota | výraz


2.7. Co bude v příští lekci?

Příště si povíme něco o řídících strukturách (if a if-else) a iteračních cyklech (for, while a do-while), abyste dovedli řídit běh programu. Dále si povíme něco o příkazech switch, break a continue. Pokud budete mít nějaké dotazy, napište mi na emailovou adresu jiri.formanek@seznam.cz.

Těším se příště nashledanou.

Jiří Formánek