Návrh seshora dolů byl dalším z nových postupů. Myšlenka spočívá v rozdělení velkého programu do menších, lépe zvladatelných úkolů. Je-li jeden z těchto úkolů stále příliš rozsáhlý rozdělte ho do ještě menších úkolů. Pokračujte tímto způsobem dokud nebude program rozškatulkován do malých, jednoduše programovatelných modulů. Návrh C tento přístup usnadňuje, podporuje vás, abyste navrhovali programové jednotky nazývané funkce, které představují jednotlivé moduly úlohy. Jak jste si mohli všimnout, postupy strukturovaného programování odrážejí postupný myšlenkový proces, jenž přemýšlí o programu v rámci činností, které provádí.
Algoritmus
Tvorba algoritmů S algoritmy se setkáváme v běžném životě. Těmito algoritmy jsou například recepty na vaření, postup ovládání video kamery, cesta do školy.
Vlastnosti algoritmů "HERgoD"
Program
Ke kreslení vývojových diagramů používáme standardní grafické symboly:
Jednotlivé značky spojujeme čárami a spojkami:
Používá se pro skupinu lidí, která nemá programátorské vzdělání - návody k obsluze, recepty, postup práce
Výhody:
Nevýhody:
Je to symbolický, algoritmický jazyk, který se používá pro názorné zobrazení algoritmu. Zápis ošetřen normou.
Výhody:
Nevýhody:
Používá se pro zápis instrukcí z vytvořeného algoritmu, kterému počítač rozumí a umí z něho vytvořit strojový kód.
Výhody:
Nevýhody:
Používá se tam, kde je možné daný problém popsat matematickým vyjádřením - řešení kvadratická rovnice, apod.
Výhody:
Nevýhody:
Integrované vývojové prostřední (IDE - integrated development enviroment) = soubor programů, které si volám podle toho, co potřebuju.
Skládá se z: "EPic KLDr"
[SKE] Příkazy pro něj začínají "#" typicky incude: když ho najde, vyhodí ten řádek a vloží tam tu dotyčnou knihovnu.
V hlavickovych souborech nejčastěji: deklarace fcí, maker, konstant. "<>" zobáčky znamenají, že se includy hledají v defaultním místě standartních inkludů.
Jiná možnost: napsat do uvozovek - hledá se v aktuálním adresáři: "C:\MOJE\MOJE.h" nedávají se znaky "\" dvakrát, bo to není string!
Přeloží každý soubor programu - výsledkem je *.OBJ (object module - binární soubor) v TurboC++, DevC++ *.o. V OBJ nejsou odvolávky z knihoven, kontroluje syntaktické chyby. Tyto soubory si předávají členové týmu, protože neodhaluje know-how.
Poskládá OBJ moduly s knihovnami a vytvoří *.exe soubor. Také vkládá absolutní adresy paměti.
Ladící prostředek součástí IDE. Hledá běhové chyby při spuštěném programu. Umí breakpointy a skoky po instrukcích: (Turbo)
Příkaz if umožňuje zapsat v kódu příkaz (nebo blok příkazů), který se bude provádět pouze tehdy, pokud bude splněna zadaná podmínka. V příkazu lze definovat pomocí klíčového slova else také větev, jejíž příkazy se budou provádět při nesplnění podmínky. Příkaz if může být vnořen (tj. vyskytovat se v libovolné větvi jiného příkazu if).
if (x == 0) printf("Proměnná x je nula.\n");
if (x > 0) printf("x je kladné\n"); else printf("x není kladné\n");
Příkaz switch slouží k větvení programu do libovolného počtu větví v závislosti na předaném výrazu základního datového typu. Jednotlivé větve tohoto příkazu se provádějí, má-li předaný výraz jim odpovídající hodnotu. Nelze zde tedy vytvářet libovolné podmínky. V konstrukci switch lze od selektoru definovat také default větev, která se bude provádět, pokud výraz nenabude žádné z uvedených hodnot.
switch (cislo){ case 1: printf("cislo je 1\n"); break; case 2: printf("cislo je 2\n"); break; default: printf("neco jineho\n"); }
Vynecháním příkazů break na konci jednotlivých větví je možné vytvořit také větev pro více hodnot daného výrazu. Z toho vyplává, že pokud má case 1 i 2 stejnou funkci není potřeba je oddělovat příkazem break; a po volbě prvního se automaticky provede kód v druhém casu.
switch (cislo){ case 1: case 2: printf("cislo je 1 nebo 2\n"); break; default: printf("neco jineho\n"); }
má stejný význam jako příkaz if-else, jen syntaxe je jiná. V některých případech je lepší použít ternární operátor kvůli kratšímu zápisu na
jeden řádek. Ternární, protože má jako jediný operátor 3 operandy.
Ternární operátor má následující syntaxe:
vyraz_podm ? vyraz_1 : vyraz_2;
i = (a != 0) ? 5 : 10; // Pokud je a ruzne od 0, i bude rovno 5 // V opacnem pripade bude i rovno 10
Grafický zápis vývojového diagramu je dán státní normou. Pokud je větvení neúplné, pak na straně, kde podmínka neplatí je prázdno.
Cykly jsou strukturované příkazy, součástí každého z nich je příkaz, jehož opakované provádění cyklus předepisuje. Tento příkaz nazýváme tělo cyklu.
Opakování těla cyklu závisí na hodnotě výrazu, o kterém budeme také hovořit jako o podmínce opakování. Jednotlivé průchody tělem cyklu označujeme
jako iterace.
Někdy potřebujeme provádění cyklu ukončit předčasně. K tomu můžeme použít příkaz break.
Jestliže potřebujeme předčasně ukončit jednotlivou iteraci (vynechat při průchodu zbytek těla cyklu), můžeme použit příkaz continue.
Příkaz while představuje cyklus s podmínkou na počátku.
while ( podmínka ) příkaz
Kde příkaz je tělo cyklu. Podmínka je výraz, který lze implicitně převést na typ bool. Podmínka může obsahovat deklaraci proměnné (jen v C++) a tato proměnná bude lokální v těle cyklu.
Význam příkazu while:
Nejprve se vyhodnotí podmínka, což je výše zmíněná podmínka opakování. Je-li nepravdivá, tělo cyklu se neprovede a provádění příkazu while skončí.
Je-li podmínka splněna, provede se příkaz tvořící tělo cyklu. Poté se opět vyhodnotí podmínka.
Je-li nyní nepravdivá, tělo cyklu se již neprovede a příkaz while skončí. Je-li opět splněna, znovu se provede tělo cyklu, vyhodnotí se podmínka atd.
Podmínka opakování se tedy vyhodnocuje před každou iterací. Není-li podmínka před prvním průchodem splněna, neprovede se tělo cyklu while ani jednou.
Zkráceně označovaný jako příkaz do. Je to cyklus s podmínkou na konci.
do příkaz while ( výraz );
Výraz v příkazu do musí mít hodnotu číselného typu, typu ukazatel nebo objektového typu, pro který existuje možnost přetypování na typ bool. Syntaktická pravidla nyní charakterizují podmínku opakování jako výraz; jediný rozdíl se srovnání s podmínkou v příkazu while je v tom, že výraz zde nemůže obsahovat deklaraci.
Význam příkazu do:
Nejprve proběhne tělo cyklu, tedy příkaz. Pak se vyhodnotí výraz. Pokud je roven false, znamená to, že podmínka opakování není splněna
a provádění příkazu do skončí. V opačném případě se opět provede tělo cyklu a znovu se testuje podmínka opakování atd. To znamená, že tělo cyklu se
provede vždy alespoň jednou.
Příkaz for je asi nejsložitější z cyklů v jazyce C++
for ( inicializační_příkaz; podmínka ; krok ) příkaz;
Inicializační_příkaz je příkaz, který chceme provést před vstupem do cyklu. Končí středníkem, může obsahovat deklaraci (C++) a může být prázdný
(tj. může obsahovat pouze středník).
Podmínka představuje podmínku opakování a má stejný význam jak u ostatních cyklů; může obsahovat deklaraci a také zde můžeme zapsat prázdný výraz.
Krok je vlastně výrazový příkaz (bez středníku), který chceme provést po každém průchodu tělem cyklu; zpravidla předepisuje změny proměnných,
které je třeba po průchodu tělem cyklu udělat. I tento výraz může být prázdný.
Proměnné, deklarované v inicializačním_příkazu a v podmínce, jsou lokální v těle cyklu. Přesněji: Proměnné, deklarované v inicializačním_příkazu,
můžeme použít v podmínce, v kroku a v těle cyklu. Proměnné, deklarované v podmínce, můžeme použít v kroku a v těle cyklu.
Příklad: Příkaz for se často používá při zpracování polí. Chceme-li např. vynulovat všechny prvky pole A o 100 prvcích, napíšeme:
const N = 100; int A [N] ; // ... for(int i = 0; i < N; i++) A[i] = 0;
Tento příkaz znamená: Na počátku uložíme do proměnné i hodnotu 0. Pak vyhodnotíme podmínku opakování; protože je i < 100, je tato podmínka splněna, takže proběhne tělo cyklu — do A[0] se uloží 0. Pak se vyhodnotí výraz i++, tj. v i bude nyní 1. Opět se vyhodnotí podmínka opakování, a protože je stále i < 100, znovu proběhne tělo cyklu — do A[l] se uloží 0. Vyhodnotí se výraz i++, takže v i bude 2, atd. Při posledním průchodu tělem cyklu bude i == 99 a do A [99] se uloží 0. Po vyhodnocení reinicializačního výrazu (kroku) bude i == 100, podmínka opakování nebude splněna a provádění příkazu for skončí.
for (;;) { //nekonečný cyklus; }
Podobně jako ve většině jiných programovacích jazyků se i v C++ příkazy za normálních okolností vyhodnocují sekvenčně, tj. v pořadí, v jakém jsou zapsány. Některé příkazy ovšem mohou tuto přirozenou posloupnost přerušit a přenést řízení na jiné místo programu.
Tento příkaz smíme použít jen v těle cyklu (for, while, do) nebo v těle příkazu switch; vvskytne-li se jinde, ohlásí překladač chybu.
Význam příkazu break:
Ukončuje nejvnitřnější neuzavřenou smyčku a hned opouští cyklus. A také obsáhleji:
Příkaz break způsobí ukončení příkazu cyklu nebo přepínače (switch), v jehož těle se vyskytl. Při několika do sebe vnořených příkazech
do, while, for nebo switch způsobí ukončení nejvnitmějšího z nich, který tento příkaz break obklopuje.
Příkaz umožňuje předčasně ukončit daný průchod tělem cyklu (iteraci). Smí se vyskytnout pouze v těle cyklu (v příkazech for, do a while).
Jeho použití kdekoliv jinde způsobí chybu.
Význam příkazu continue:
Skáče na konec nejvnitřnější neuzavřené smyčky a tím vynutídalší iteraci smyčky, cyklus neopouští. Také:
Příkaz continue přenese řízení na pokračovací část nej vnitřnějšího příkazu while, do nebo for, v jehož těle se vyskytl. To znamená,
že ukončí právě probíhající iteraci a způsobí vyhodnocení podmínky pro pokračování (a v cyklu for ještě reinicializaci, tedy vyhodnocení výrazu).
Význam příkazu return:
Příkaz return ukončí provádění těla funkce, v níž se nachází, a vrátí řízení funkci volající.
První varianta, ve které není uveden výraz, se používá ve funkcích typu void (které nevracejí žádnou hodnotu, tedy v procedurách).
Uvedeme-li výraz, vrátí funkce hodnotu tohoto výrazu. Typ výrazu musí být možné implicitně převést na typ vracené hodnoty uvedený v deklaraci funkce,
podobně jako např. při přiřazení.
Funkce typu void nemusí příkaz return obsahovat. Dojde-li řízení v těle funkce až ke složené závorce „}“ ukončující její tělo, je to stejné, jako kdyby zde byl zapsán příkaz return; (bez výrazu).
Z funkcí jiného typu (tj. z funkcí, které vracejí nějakou hodnotu) se musíme vždy vrátit pomocí příkazu return s výrazem odpovídajícího typu.
Co určuje datový typ: "PHO" (!)
Tvoří dvě základní skupiny — typy „se znaménkem“ a „bez znaménka“. Celočíselné typy se znaménkem mohou obsahovat jak kladná tak i záporná čísla,
zatímco typy bez znaménka mohou obsahovat pouze kladná čísla a 0.
Celočíselné typy se znaménkem jsou signed char, short, int a long a ještě typ long long.
Celočíselné typy bez znaménka jsou unsigned char. unsigned short. unsigned. unsigned long a unsigned long long.
Standard jazyka nepředepisuje jejich rozsah, stanoví však, že rozsah typu short nesmí být větší než typ int, rozsah typu int nesmí být větší než typ long.
short <= int <= long <= long long
unsigned short <= unsigned int <= unsigned long <= unsigned long long
int má délku podle kompilátoru a to:
V jazyce C++ máme k disposici 4 znakové typy
Typy char, unsigned char a signed char zabírají 1 B a slouží především pro práci s evropskými znaky. Typ wchar.t zabírá typicky 2B nebo 4B a používá se pro práci s asijskými znaky nebo s kódem UNICODE.
Typ char je v C++ implementován jako 1 B se znaménkem nebo bez znaménka, tedy vlastně jako signed char nebo unsigned char, považuje se však za zvláštní typ. Typ wchar_t je v C++ základní typ, v jazyce C je implementován pomocí deklarace typedef. V obou jazyích se chová jako jeden z celočíselných typů bez znaménka.
Se znaky zacházíme podobně jako s ostatními celočíselnými typy, to znamená, že je můžeme používat ve výrazech podobně jako celá čísla. Jediný zásadní rozdíl se týká vstupů a výstupů znaků pomocí objektových datových proudů (cin. cout atd.), kdy se hodnoty znakových typů chovají opravdu jako znaky, nikoli jako čísla.
Tyto typy specifikují určité konečné podmnožiny množiny reálných čísel. V paměti jsou tato čísla zpravidla uložena ve tvaru odvozeném od semilogaritmického zápisu, tedy od zápisu, jako je 6,27 x 10^12. (Počítač ovšem samozřejmě využívá dvojkovou soustavu.) Číslo 6,27 zde nazýváme "mantisa", číslo 12 "exponent". V C++ můžeme pro práci s reálnými čísly používat typy float, double a long double. Také zde norma jazyka nestanoví rozsah těchto typů, pouze požaduje, aby platilo:
float <= double <= long double
Způsob pro zavedení nového datového typu založeného na struktuře lze provést pomocí klíčového slova typedef.
Nejúplnější definice vypadá takto:
typedef struct clovek { char jmeno[50]; int vek; double hmotnost, vyska; }CLOVEK;
Kde clovek je název struktury a CLOVEK název nového datového typu. Jinými slovy slovní spojení struct clovek vyjadřuje totéž, co jedno slovo CLOVEK a výše definované proměnné nyní mohu zavést i takto:
CLOVEK Jenda, Bara;
Při použití typedef je třeba pojmenovat strukturu pouze v případě, že by některou z jejích složek byl pointer na ni samu. Jinak lze název struktury vynechat.
Položka dané struktury nemůže být právě vytvářeného datového typu přesněji název typu každé položky musí být různý od jména struktury, která ji obsahuje,
či od jména právě vytvářeného typu. Položka však může být typu pointer na strukturu ve které je obsažena (nikoli však pointer na typ,
který vytváříme pomocí typedef ).
K jednotlivým položkám proměnných tohoto typu pak přistupuji takto:
strcpy(p_Bara->jmeno, "Bára Zvučná"); p_Bara->vek = 20; printf("%d",p_Bara->vek);
Následující datové typy jsou probírány v samostatné kapitole číslo 8.
FILE *fw;
class Trida { private: // zde vidi jen objekty tridy int i; protected: // zde vidi jen objekty teto a zdedenych trid float f: public: Trida(); // implicitni konstruktor
~Trida(); //destruktor void nacti(); // deklarace fce - definice radeji na konci programu };
V C++ používáme proměnné — tedy pojmenovaná místa v paměti, do kterých ukládáme nějaké hodnoty. Každou proměnnou je třeba deklarovat, tj. oznámit překladači, jak se bude jmenovat a jaké hodnoty do ní budeme ukládat (jakého bude typu).
C: Deklarace na začátku bloku
C++: Deklarace může být kdekoliv
Bude dostupná ve všech funkcích v programu za místem své deklarace.
Jejich platnost je omezena na tělo funkce.
Inicializace při deklaraci znamená, že jsme proměnné přidělili počáteční hodnotu.
Alokace paměti je vymezení místa v paměti pro proměnnou. Každé proměnné musí být během své existence přidělen paměťový prostor, který je dán datovým typem.
Jméno proměnné je vlastně symbolická adresa tohoto prostoru.
Mění chování proměnných
Nadbytečná třída, pozůstatek dřívějších verzí, pro lokální proměnné. Ukládají se na zásobník. Při každém vstupu má náhodnou hodnotu, proměnná existuje od vstupu do funkce a zaniká výstupem.
Implicitní PT pro globální proměnné. Je uložena v datové oblasti. Používá se pro oddělený překlad souborů. (2 a více souborů sdílí tuto proměnnou).
V jednom souboru nemusí být "extern", ve všech ostatních musí toto klíčové slovo být.
Neexistuje implicitní definice. (nutné vždy uvést) Tato PT je uložena v datové oblasti. Využívají lokální proměnné uvnitř funkcí - ponechají si svou
hodnotu mezi jednotlivými voláními funkce.
Existuje od prvního volání funkce až do ukončení programu. Není přístupná z vnějšku funkce.
Globální proměnná funkce mohou být static, pak jsou viditelné jev modulu, kde jsou definovány.
Pokud nechci, aby mi někdo z modulu zasahoval do mé proměnné, tak static (opak extern).
static int a; static float f,g; //g není static!
void fce() {static int x = 0;} //pouze při prvním volání, z lokální globální, pamatuje si hodnotu mezi voláními. x++
Při prvním volání fce() se x vynuluje při každém volání funkce FCE se hodnota q x zvýší o 1 výsledkem programu jsou vypsána čísla 1 až 10.
Proměnná uložena pouze v registru CPU (jen jestli je volný), velmi rychlá, nedá se přesně určit, kde se uloží. Výhradně jako lokální proměnná.
Platí, že v programu můžeme pracovat jen s tím, co překladač už zná. Nelze se odvolávat na funkci nebo na proměnnou, kterou jsme ještě nedeklarovali.
To se týká i datového proudu std::cout. (C++) Toto je, instance objektového typu; je uložena ve standardní knihovně. Abychom nemuseli její deklaraci opisovat do
každého programu (a abychom nemuseli opisovat tisíce dalších deklarací knihovních funkcí, proměnných, operátorů, tříd atd.), máme v C/C++ k dispozici tzv. hlavičkové soubory.
Ty obsahují všechny informace, které překladač nezbytně potřebuje, aby mohl přeložit volání knihovní funkce, použití knihovního objektu apod.
Standardní hlavičkové soubory v C++ jsou bez přípony, standardní hlavičkové soubory v jazyce C mají příponu .h. Pro uživatelem definované hlavičkové
soubory se zpravidla používá přípona .h.
Informace o základních vstupních a výstupních proudech jazyka C++ jsou v hlavičkovém souboru iostream (ve starších překladačích se jmenoval iostream.h).
Aby je měl překladač k dispozici, musíme tento soubor vložit do svého programu, a to před prvním použitím proudu std::cout — nejlépe hned na počátku
zdrojového souboru. K tomu slouží direktiva #include, za kterou v lomených závorkách < a > následuje jméno souboru.
Umožňuje vyčlenit některé části zdrojového kódu programu do zvláštních souborů, které lze opakovaně používat. Standardní knihovna jazyka C a standardní knihovna jazyka C++ tradičně deklarují své funkce v hlavičkových souborech.
Vlastní knihovna musí být ve stejném adresáři jako program.
Využívá se zde podmíněný překlad!
Aby se nevkládal vícekrát.
funkce.h
void funkce(void);
funkce.c
void funkce(void) { /* ... */ }
main.c
#include "funkce.h" int main(void) { funkce(); return 0; }
Všimněte si, že souboru main.c v našem příkladu neodpovídá žádný main.h, neboť v main.c neposkytujeme žádné funkce ani proměnné k použití z jiných souborů.
Direktivy preprocesoru umožňují předepsat, že v závislosti na určitých podmínkách chceme či nechceme překládat určité úseky zdrojového textu.
V následujícím syntaktickém popisu označuje úsek-programu několik řádků zdrojového textu; úsek programu může být i prázdný, nemusí obsahovat žádný zdrojový text.
Může také obsahovat další direktivy preprocesoru, i direktivy pro podmíněný překlad. Každá direktiva musí být stejně jako ostatní direktivy preprocesoru na samostatném řádku.
Syntaxe:
direktivy_pro_podmíněný_překlad:
#if konstantní_výraz nový_řádek #ifdef identifikátor nový_řádek #ifndef identifikátor nový_řádek #elif konstantní_výraz nový_řádek #else nový_řádek #endif nový_řádek
#ifdef makro1 printf("Definováno makro 1"); #elif makro2 printf("Definováno makro 2"); #else printf("Makro 2 není definováno"); #endif
Podmíněně překládaný úsek programu začíná direktivou #if (nebo #ifdef nebo #ifndef, které slouží jako zkratky pro #if s určitým podmíněným výrazem). Pak může následovat několik úseků začínajících direktivou #elif. Po nich může přijít jeden úsek začínající direktivou #else. Úsek programu s podmíněnou kompilací musí končit direktivou #endif.
Konstantní výrazy mohou obsahovat celočíselné literály, identifikátory definované v direktivě #define a případně operátor defined. V C++ mohou obsahovat i konstanty deklarované pomocí modifikátoru const a inicializované konstantním výrazem.
Význam: V následujícím popisu označuje úsek programu několik řádků zdrojového textu: úsek_programu může být i prázdný, nemusí obsahovat žádný zdrojový text. Může také obsahovat další direktivy preprocesoru, i direktivy pro podmíněný překlad. Každá direktiva musí být stejně jako ostatní direktivy preprocesoru na samostatném řádku.
#if konstantní_výraz_l úsek_programu_l #elif konstantní_výraz_2 úsek_programu_2 #else usek_programu_3 #endif
Preprocesor vyhodnotí konstantní_výraz_1. Je-li jeho hodnota true (nenulová), přeloží úsek_programu_1. Úseky programu za direktivami #elif a #else ze zdrojového textu vypustí a přejde na zdrojový text za direktivu #endif. Je-li hodnota konstantního_výrazu_1 rovna false, resp. 0, odstraní preprocesor úsek_programu_1 a vyhodnotí konstantní_výraz_2 za #elif. Je-li jeho hodnota nenulová, přeloží úsek_programu_2, odstraní úsek_programu_3 pak přejde za direktivu #endif. Je-li i hodnota konstantního_výrazu_2 rovna false, odstraní se ze zdrojového textu také úsek_programu_2 přeloží úsek_programu_3.
Když tvořím třídu, deklaraci píšu do souboru s příponou h, do cpp píšu definici (metody).
Můžu vkládat všechny soubory, nejen hlavičkové, preprocesor tam vloží všechno (#include "tvuj.c"), vyleze z toho jeden OBJ soubor (zpracuje kompiler), při překladu kompiluje oba dva soubory. Tento postup je nevhodný ve velkých týmech, kdy se při malé změně, kompiluje vše znovu.
Více samostatných souborů 'muj.c' a 'tvuj.c' musím dát do projektu (záleží na vývojovém prostředí).
Musím vložit hlavičkový soubor #include"tvuj.h", musím tam mít podmíněný překlad a deklarace funkcí, konstant, makrer, datových typů, globálních proměnných.
V tomto kroku se dva OBJ soubory se spojují do jednoho EXE: muj.obj, tvuj.obj.
Výhoda: při malé změně kódu se přeloží jenom jeden změněný soubor.
Tento princip je ve vývojových prostředích podporován vytvářením projektů do projektu vkládám jen soubory s příponou *.c, nebo OBJ,
H tam dává preprocesor sám!!!
Pokud částečně bojujeme v týmu, nedám kolegovi *.c, ale poskytnu mu OBJ a H. Zpracování těchto souborů má nastarosti linker.
Když je include to v uvozovkách, tak se hledá soubor v aktuální složce. Při použití < > se soubory hledají tam, kde běžné includy.
V projektech se využívá podmíněný překlad a oddělený překlad. Ve složitějších projektech je vhodné překládat hlavičkové soubory podmíněně. Hlavičkový soubor se může používat ve více souborech, a tím pádem by se vložil několikrát.
#ifndef _muj_h #define _muj_h int Maximum(int a, int b, int c); int Minimum(int a, int b, int c); #endif
Nejprve si vytvoříme adresář, do kterého následně uložíme všechny soubory projektu (programu).
V tomto adresáři vytvoříme další podadresáře pro přehlednost v projektu a to tyto:
Soubory s *.c uložíme do podadresáře src. Soubory projektu uložíme v adresáři projektu (tam kde vidíme podadresáře).
Velmi vhodné je přejmenovat soubor Unit1.c na main.c, abychom věděli, kde se nachází funkce main() a také pro přehlednost.
Přes menu Projekt – Options (Shift+Ctrl+F11) zobrazíme okno se záložkami pro nastavení specifických vlastností tohoto projektu.
Na kartě „Path and Defines“ připojíme náš podadresář include: Tlačítkem Edit vyvoláme okno Path, tlačítkem "..." nalezneme náš podadresář a tlačítkem Add jej připojíme k projektu.
Dále zatrhneme položku „Specify object search path“ a stejným způsobem jako v předchozím případě připojíme náš podadresář obj.
Na kartě „Output settings“ zatrhneme „Final output directory“ a připojíme náš podadresář bin.
Klik pravou myší na název modulu a zvolit „Rename“. Jméno modulu se změní jak u zdrojového (*.c) tak také u hlavičkového (*.h) souboru.
Do souborů doplníme příslušný obsah dle typu souboru a uložíme do podadresáře „src“ našeho adresáře projektu. Zde budou uloženy oba soubory modulu *.c i *.h .
Struktura není nic jiného než soubor proměnných které mají nějakou logickou souvislost a lze je jako celek nějak použít.
Název datového typu struktury se píše velkými písmeny!
typedef struct muj //nazev struktury { }MUJ; //název datového typu
x.a = 10; strcpy(x.b,"Ahoj"); MUJ x, *z; z = (MUJ*)malloc(sizeof(MUJ))
V haldě se vytvořila struktura z s částí a a b, přístup do ní (vnořeně) (*z).a
(tečka má větší prioritu)
(šikpa na haldu, tečka na zásobník)
z->a=15;
Do struktury do funkce posíláme jen adresu, přes '->', přístup pomocí *.
1. Struktura není pojmenována, nedá se využít dále v programu, lze používat pouze definované proměnné:
struct { int vyska; float vaha; } pavel, honza, karel;
2. Struktura je pojmenována, dá se využít dále v programu
struct miry { int vyska; float vaha; } pavel, honza, karel;
struct miry jan, martin; // definice proměnných
miry jan, martin; // chyba
3. Definice struktury jako nový datový typ pomocí příkazu typedef, k definici proměnných není potřeba klíčové slovo struct
typedef struct { int vyska; float vaha; } MIRY;
MIRY pavel, honza, karel;
4. definice pomocí typedef, kdy je struktura pojmenována, používá se, když struktura odkazuje na sebe sama
typedef struct polozka { int hodnota; struct položka *dalsi; } POLOZKA;
1. Přístup k prvkům pomocí tečkové notace
pavel.vyska = 186;
2. Pole struktur
MIRY lide[100]; lide[50].vyska = 176;
3. Je možné pracovat s celou strukturou najednou (honza = pavel – kopie celé struktury najednou)
4. Pole ve struktuře
typedef struct { int pole[10]; } STR_POLE;
STR_POLE x; x.pole[1] = 5;
5. Pointer na strukturu
MIRY *clovek; clovek = (MIRY*) malloc(sizeof(MIRY));
6. Přístup k dynamické struktuře
(*clovek).vaha = 70.4; // nebo jednodušeji clovek->vaha = 70.4;
7. Inicializace struktury
MIRY a = {190, 84.3};
TSTRUKT pole[5]; //pole 5ti prvků, každý prvek má své položky main: pole[1].cislo=15;
Když posílám pole do fce, posílám tam jenom adresu. Mám to uloženo v pointeru.
fce:
p[1].cislo=25;
Struktura ve struktuře, přístup přes tečky
p[0].datum.rok=2012;
Protože se jednotlivé prvky unionu překrývají, je jasné, že po přiřazení hodnoty některému z nich se dá pracovat již pouze s tímto prvkem, a to až do doby, kdy dojde k novému přiřazení. V opačném případě bychom pracovali s „deformovanými“ daty (i když i tato vlastnost se dá v některých případech patřičně využít). Zde se projeví nevýhoda unionů, které neuchovávají informaci o tom, který jejich prvek je právě využíván. Možností, jak toto obejít, je zabalit union do další struktury obsahující navíc položku s informací (např. číselnou) o právě používaném prvku unionu:
Zjistí, které je větší a/b a vyalokuje jen to větší.
Union si dělá místo pro největší položku, jednotlivé položky se přepisují. Přístup přes tečku.
typedef union { char c; int i; float f; } TEST;
Jedná se vlastně o jakousi náhradu za symbolické konstanty vytvářené pomocí makra #define. Uživatel si pro nový výčtový typ sám definuje množinu přípustných hodnot uvedením
všech jejich prvků. Není tedy problém vytvořit si pomocí enum nový typ, jež může nabývat například hodnot CERVENA, MODRA, ZELENA.
Vnitřně jsou pak tyto konstanty reprezentovány celými čísly, a to nejmenšího znaménkového typu, který je schopen všechny prvky nového typu obsáhnout.
Slouží k definici seznamu symbolických konstant, které jsou většinou vzájemně závislé.
typedef enum { MODRA, CERVENA, ZELENA, ZLUTA } BARVY;
BARVY x = MODRA;
Čísluje se implicitně od nuly, lze explicitně inicializovat:
typedef enum { MODRA = 5, CERVENA = 8, ZELENA, ZLUTA = 15 } BARVY; // ZELENA bude mít číslo 9
typedef enum { raz, dva, tri } POCTY; POCTY a;
můj název pro 0 je raz
Nemůžu řetězec načíst "dva" a nemůžu jej tak vypsat, není to řetězec!
//souvislost s barvami webových stránek
Výraz je konstrukce předepisující výpočet hodnoty. Je tvořen operandy a operátory. Operand je proměnná, zápis funkce nebo složitější konstrukce mající opět hodnotu (tedy zase výraz). Operátory určují, co provést s hodnotami operandů. Jazyk C++ nabízí celou řadu unárních a binárních operátorů a jeden operátor ternární (operátory s jedním, dvěma a třemi operandy). Unární operátory jsou zpravidla prefixové (zapisují se před operand) nebo postfixové (píší se za operand). Binární operátory jsou nejčastěji infixové (píší se mezi operandy).
PINK (2 dělení) podle počtu operandů a++ (1 operand) unární a+b (2 operandy) binární ternární podle funkce aritmetické porovnávací logické inkrementace, dekrementace přiřazovací a++ ++a a*=5+c vezme jako a*=(5+c)
Operátor určuje operaci, která se má s operandem nebo operandy provést. Mohou být unární (1 operand) nebo binární (2 operandy).
Unární operátory:
Syntax:
podmínka ? podmínka_splněna : podmínka_nesplněna
Význam v zápisu
E1 ? E2 : E3
se operand E1 automaticky konvertuje na typ bool. Je-li hodnota E1 rovna true, je výsledkem E2, jinak je výsledkem E3. V případě tohoto operátoru C++ zaručuje, že se jako první vyhodnotí operand E1 a teprve pak E2 nebo E3
(vždy jen jeden z nich).
Pro typ výsledku platí:
Poznámka:
Tento operátor se nepovažuje za jediný symbol, můžeme ho rozdělit bílými znaky.
určuje pořadí zpracování největší - závorka nejmenší - čárka neučit se tabulku prioritu tabulka MAK sem
Prioritu označíme čísly od 1 (nejvyšší) do 16 (nejnižší). Dříve se vyhodnocují pochopitelné operátory s vyšší prioritou, pořadí ale můžeme upravit kulatými závorkami. Asociativita určuje, zda se zřetězené operátory vyhodnocují v pořadí zprava doleva nebo zleva doprava. Napříkal operátor sčítání + je asociativní zleva doprava, a to znamená, že výraz:
a + b + c
se vyhodnotí jako
(a + b) + c
Jak jistě víte, jsou situace, kdy pro sčítání na počítači neplatí asociativní zákon, a proto je důležité vědět, jak zpracování takovéhoto výrazu proběhne. Některé operátory, například přiřazovací, jsou asociativní zprava doleva. Výraz:
a = b = 3.1;
se vyhodnotí jako
a = (b = 3.1);
Proměnné a se přiřadí výsledek operace b = 3.1, tedy hodnota uložená v b.
Pozor: C++ sice předepisuje, v jakém pořadí se vyhodnotí operace, ale nestanoví pořadí, ve kterém se připraví operandy. Jestliže napíšeme
f() + g() + h()
máme sice zaručeno, že se hodnoty, vrácené těmito funkcemi, sečtou v pořadí
(f() + g()) + h()
ale to neznamená, že se funkce f(), g() a h() budou také v tomto pořadí volat. Jestliže to bude překladač pokládat za vhodné (třeba kvůli optimalizaci), může nejprve zavolat funkci g(). výsledek si uložit, pak zavolat třeba h(), výsledek si opět uložit, a nakonec zavolat f (). Tyto dílčí výsledky ovšem musí sečíst v pořadí určeném asociativitou použitých operátorů.
V následující tabulce P znamená prioritu, A asociativitu, je v C říká, zda lze tento operátor používat i v jazyce C, a sloupec l-hodnota říká, zda daný operátor vytváří l-hodnotu.
Operátory, které se skládají z více znaků, nesmíme při zápisu rozdělit, tj. mezi jednotlivé znaky tvořící operátor nesmíme vložit bílé znaky. Výjimkou jsou operátory (), [] a :?.
Přetěžování operátorů znamená rozšíření definice některého z existujících operátorů na nový datový typ. Jazyk C++ dovoluje přetížit velkou většinu operátorů. Přetížené operátory budou mít stejnou prioritu i ostatní vlastnosti jako stejnojmenné standardní operátory, budou ale pracovat s novým datovým typem. Přetížený operátor deklarujeme jako volnou („obyčejnou“) funkci nebo jako metodu se jménem tvořeným klíčovým slovem operátor, za kterým následuje symbol operátoru. Jestliže operátor přetížíme jako volnou funkci, musí mít alespoň jeden operand objektového nebo výčtového typu.)
//Sčítání dvou zlomků (struktur s prvky cit a jmen) Zlomek operator+(Zlomek zl1, Zlomek zl2) { Zlomek soucet; soucet.cit=((zl1.cit*zl2.jmen)+(zl1.jmen*zl2.cit)); soucet.jmen=zl1.jmen*zl2.jmen; return soucet; }
Komplexní čísla: Upravená deklarace třídy complex tedy může vypadat takto:
// Soubor COMPLEX.H class complex { public: complex(double r = 0, double i = 0); //Konstruktor complex operator+(complex& c); //Aritmetické operátory complex operator*(complex& c); complex operátor-(complex& c); complex operator/(complex& c); double Re(); //Reálná a imaginární část double Im(); double abs(); //Absolutní hodnota private: double re, im; //Datové složky };
Možná vám připadá podivné, že binární operátory +, - a další zde mají jen jeden parametr.
To je v pořádku: Zápis a+b, kde a a b jsou instance typu complex, bude překladač interpretovat jako volání metody a.operator+(b);
a podobné je to i v ostatních případech.
Funkce vyjadřují dílčí podprogramy. Připomeňme si, že v C a C++ se za označením "funkce" skrývají všechny druhy podprogramů, i ty, které se v jiných jazycích označují jako „procedury“.
Rozlišujeme definiční a informativní deklaraci. Informativní deklaraci obvykle nazýváme prototyp. Aby mohl překladač přeložit volání funkce, stačí, když zná její prototyp; definiční deklarace může být někde jinde.
Obě bychom je mohli popsat takto:
definice funkce: void fce(int i);
Deklarace: void fce(int i) { if(i==5)printf("Cool"); }
volání funkce: fce(5);
Příklady:
int f1(double, double);
Funkce f1() má dva parametry typu double a vrací hodnotu typu int.
void *f2(int);
Funkce f2() má jeden parametr typu int a vrací ukazatel bez doménového typu (tj. void*).
int (*f3()) [10];
Funkce f3() je bez parametrů a vrací ukazatel na pole deseti hodnot typu int.
long (*f4(char)) ();
Funkce f4() má jeden parametr typu char a vrací ukazatel na funkci typu long bez parametrů.
Všimněte si, že v deklarátoru funkce stojí vždy bezprostředně za identifikátorem funkce závorky se seznamem formálních parametrů.
Specifikace formálních parametrů v deklaraci funkce se podobá deklaraci proměnných, až na to, že každý formální parametr musíme specifikovat zvlášť a parametry mohou mít pouze paměťovou třídu auto nebo register.
Jména parametrů můžeme za jistých okolností vynechat. Specifikace jednotlivých parametrů oddělujeme čárkami.
Chceme-li deklarovat funkci bez parametrů, použijeme buď klíčové slovo void nebo prázdné závorky. Obě možnosti jsou v C++ ekvivalentní:
int f(); // Obojí znamená v C++ totéž int f(void);
Hlavička funkce není ukončena středníkem! Všechny parametry jsou volány hodnotou, neexistuje přímo volání odkazem. Mezi jménem funkce a levou závorkou se nedělá mezera,aby nebylo nutné odlišovat volání funkce od volání makra a parametry.
Formální parametry (z hlavičky funkce) jsou volány:
Parametry předávané:
Formální parametry a lokální proměnné jsou přístupné pouze ve funkci, ve které byly definovány.
Rekurzivní funkce volá sama sebe.
Faktoriál rekurzí: Zavolá se ještě předtím než skončí běh, ukončuje podmínka. Rekurze je náročná na paměť, výhodou je její krátký kód.
Lze tak zapsat cykly (některé programovací jazyky je neumí)
double faktorial(double x) { double vys; if (x > 1) vys=x*(faktorial(x-1)); else return(vys); }
Přetěžovat lze funkce pouze v C++. Funkce se pak stejně jmenuje, ale má jiný počet nebo datové typy parametru.
Překlad zdrojového programu v C++ probíhá, jak víme, v několika fázích. První z nich je zpracování tzv. preprocesorem. Po něm následuje samostatný překlad jednotlivých modulů a nakonec sestavení celého programu. První dvě fáze,
zpracování preprocesorem a překlad, mohou ve skutečnosti probíhat zároveň; třetí fáze je vždy samostatná.
Preprocesor např.
Činnost preprocesoru můžeme řídit direktivami, které začínají znakem # (hash). Identifikátory vyjadřující direktivy nejsou klíčová slova. Každá direktiva musí být na samostatném řádku zdrojového textu. Může jí předcházet
libovolný počet bílých znaků a musí být ukončena přechodem na nový řádek. Na jednom řádku nemohou být dvě direktivy nebo zároveň direktiva a příkaz.
Poznámka: Při ladění programu občas potřebujeme vidět výsledek zpracování preprocesorem. V Borland C++ Builderu máme k dispozici samostatný preprocesor CPP32.EXE, jehož výstupem je soubor se stejným jménem jako zdrojový soubor
a s příponou .I.
Příkaz preprocesoru ukončuje '\', začíná '#'. Jmenuje se direktiva.
#define MAX 10
Kdekoliv v kódu najde MAX, nahradí to 10. (case sensitive)
Pokud nemají parametr, tak se píší VELKÝMI písmeny, s parametrem malým.
Za makro s parametrem nelze napsat mezeru!
#define sou(a,b) a+=b;
main() { sou(5,1.2); //nahradí a-5kou a místo řádku v mainu napíše 5+=1.2 floaf x=5, y; sou (x,3.2) // zapíše, že x+=3.2;; // bacha na středníky!!! }
Vlastnosti:
= nástroj programátora
Direktivy preprocesoru umožňují předepsat, že v závislosti na určitých podmínkách chceme či nechceme překládat určité úseky zdrojového textu.
V následujícím syntaktickém popisu označuje úsek-programu několik řádků zdrojového textu; úsek programu může být i prázdný, nemusí obsahovat žádný zdrojový text.
Může také obsahovat další direktivy preprocesoru, i direktivy pro podmíněný překlad. Každá direktiva musí být stejně jako ostatní direktivy preprocesoru na samostatném řádku.
Syntaxe:
direktivy_pro_podmíněný_překlad:
#if konstantní_výraz nový_řádek #ifdef identifikátor nový_řádek #ifndef identifikátor nový_řádek #elif konstantní_výraz nový_řádek #else nový_řádek #endif nový_řádek
#ifdef makro1 printf("Definováno makro 1"); #elif makro2 printf("Definováno makro 2"); #else printf("Makro 2 není definováno"); #endif
Podmíněně překládaný úsek programu začíná direktivou #if (nebo #ifdef nebo #ifndef, které slouží jako zkratky pro #if s určitým podmíněným výrazem). Pak může následovat několik úseků začínajících direktivou #elif. Po nich může přijít jeden úsek začínající direktivou #else. Úsek programu s podmíněnou kompilací musí končit direktivou #endif.
Konstantní výrazy mohou obsahovat celočíselné literály, identifikátory definované v direktivě #define a případně operátor defined. V C++ mohou obsahovat i konstanty deklarované pomocí modifikátoru const a inicializované konstantním výrazem.
Význam: V následujícím popisu označuje úsek programu několik řádků zdrojového textu: úsek_programu může být i prázdný, nemusí obsahovat žádný zdrojový text. Může také obsahovat další direktivy preprocesoru, i direktivy pro podmíněný překlad. Každá direktiva musí být stejně jako ostatní direktivy preprocesoru na samostatném řádku.
#if konstantní_výraz_l úsek_programu_l #elif konstantní_výraz_2 úsek_programu_2 #else usek_programu_3 #endif
Preprocesor vyhodnotí konstantní_výraz_1. Je-li jeho hodnota true (nenulová), přeloží úsek_programu_1. Úseky programu za direktivami #elif a #else ze zdrojového textu vypustí a přejde na zdrojový text za direktivu #endif. Je-li hodnota konstantního_výrazu_1 rovna false, resp. 0, odstraní preprocesor úsek_programu_1 a vyhodnotí konstantní_výraz_2 za #elif. Je-li jeho hodnota nenulová, přeloží úsek_programu_2, odstraní úsek_programu_3 pak přejde za direktivu #endif. Je-li i hodnota konstantního_výrazu_2 rovna false, odstraní se ze zdrojového textu také úsek_programu_2 přeloží úsek_programu_3.
Pointer je proměnná, jejíž obsahem je adresa jiného datového prostoru. Standartně to není dynamická proměnná!
(u dynamické se její velikost určuje až za běhu programu. Nemá svůj identifikátor - nejmenuje se, dostanu se k ní pouze přes pointer)
Vlastnosti pointerů:
float *s, a; (s je pointer, a je proměnná)
a = 3,1 == *s = 3.1;
Pro tento výraz platí odkaz na statickou proměnnou na zásobníku:
p = &a;
Pointer p ukazuje na dynamickou proměnnou:
p = (float*)malloc(sizeof(float));
*p = 2.8;
scanf(" %f", &a); //normální proměnná scanf(" %f", p); //zápis pointeru
Nezapomenout odalokovat paměť!
free(p); //odstraní "obdélníček" kolem hodnoty v paměti p = NULL; //vynuluje se adresa a "zruší se šipka" (teď ukazuje na začátek paměti)
v C++
p = new float; delete p; //ekvivalent free
p = new float[5]; //vytvoří pole s 5 prvky delete []p;
Pro pointery můžeme mimo operátory * a & použít taky operátory: ++, --, +, - a další. Veškeré operace se však musí provádět s ohledem na datový typ pointeru. Toto má význam pro práci s polem. == má smysl pouze, když pointery ukazují do stejného adresního prostoru tedy pole.
ukazatel + n
je adresa n-tého prvku za prvkem na který ukazuje ukazatel. Posun je součin čísla n a velikosti bázového datového typu.
ukazatel - n
je adresa n-tého prvku před prvkem, na který ukazuje ukazatel.
Když odečtu 2 pointery zjistím, kolik je mezi němi 'chlívků'.
ukazatel1 – ukazatel2
Zápis má smysl pouze pokud oba pointery ukazují na stejný celek paměti. Vrací počet položek, které se nacházejí mezi těmito pointery.
ukazatel1 – ukazatel2
Má smysl pokud oba ukazatelé ukazují na stejný celek paměti a jsou stejného datového typu.
Pointerovou aritmetiku používáme k přístupu jednotlivým prvkům pole.
Pomocí všech tří pointerů vypište adresu i hodnotu čtvrtého prvku pole p pointerovou aritmetikou.
int pocty(int a, int b); int main() { int x = 5, y = 3, s; s = pocty(x, y); //skutečné parametry printf(“Součet je %d“,s); return 0; } int pocty(int a, int b) //formální parametry { return (a+b); }
Skutečné parametry předají hodnotu formálním parametrům.
Po skončení funkce končí platnost lokálních proměnných funkce pocty – jsou odalokovány.
Pomocí return můžeme vracet jen jednu hodnotu! NELZE: return(a+b, a-b);
Pointery využíváme ve funkcích pokud chceme, aby funkce vracela více než jednu hodnotu!
int pocty(int a, int b, int *c); int main() { int x = 5, y = 3, s, r; s = pocty(x,y,&r); printf(“Součet je %d“, s); printf(“Rozdíl je %d“, r); return 0; } int pocty(int a, int b, int *c); { *c = a - b; return (a+b); }
Pozn. přidat referene!
Pole je uspořádaná množina hodnot stejného datového typu, tj. homogenní strukturovaný datový typ
V době překladu musí být znám počet prvků pole.
int p[5];
Vytvoří pětiprvkové pole celých čísel a pointer p, který ukazuje na toto pole (obsahuje adresu prvního prvku pole). Vše se nachází na zásobníku. Pointer p je statický a po celý běh programu neměnný!
K jednotlivým prvkům pole přistupujeme pomocí indexů. Jazyk C vždy indexuje od nuly a nekontroluje meze pole.
int polee[4]= {8,2,4,3};
Pole se může při deklaraci inicializovat – naplnit.
int poole[ ]= {10,20,30};
Překladač vytvoří tříprvkové pole podle počtů hodnot v závorce.
int a; float pole[a];
Nelze provést, protože v době překladu neznáme hodnotu proměnné a a nevíme kolik prvků je potřeba alokovat.
int p[3]; p[0]= 10; scanf(“%d”,&p[1]); //15 srand((long)time(NULL)); p[2]= rand() % 100; //55 p[3]= 8;
Zapíše hodnotu na nevyalokované místo v paměti – C nekontroluje meze pole.
char a[ ]={‘a’, ‘e’, ‘i’, ‘o’, ‘u’, ‘y’}; char b[6]; for (i=0; i<6; i++) b[i]=a[i];
float a[5]={0,2,4,6,}; for (i=0; i<5; i++) printf(" %f", a[i]);
Při deklaraci musíme znát jen datový typ prvků pole. Nemusíme znát počet prvků. Prvky musíme alokovat a odalokovat sami.
int *p;
Vytvoří pointer na celé číslo a je jen na nás jestli bude ukazovat na jedno číslo nebo na celé pole. Práce se statickým a dynamickým polem je stejná. Rozdíl je v alokaci a odalokaci pole a jeho umístěním v paměti.
int a[5], *p; p = (int*)malloc(sizeof(int)*5); ... free(p); p=NULL;
void bublesort(int *pole, int N) { int i,j,pom; /* pozice v poli*/ for(i = 0; i < N; i++) { for(j = 0; j < N-1; j++) if(pole[j] <= pole[j+1] ) { pom = pole[j]; pole[j] = pole[j+1]; pole[j+1] = pom; } } }
Maturitní otázky 2010/2011 z programování
Stephen Prata - Mistrovstvi v C++
Joff Kent - C++ bez předchozích znalostí
Miroslav Virius (ČVUT) - Programování v C++
struktura programu.doc
FORM_rid.doc
T_Sekvence.doc
Algoritmus.doc
Joff Kent - C++ bez předchozích znalostí
Miroslav Virius (ČVUT) - Programování v C++
Physics.ujep.cz
Blabik.cz
Physics.ujep.cz
UPOL.cz sbírka úloh z jazyka C
Časopis Programátor