Tvorba programu - algoritmizace a programování

01-Tvorba programu

Postup při řešení problému

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í.

  1. Shora dolů - rozklad úlohy na segmenty, které se neustále zjemňují, až můžeme řešit problemativu v konkrétním programovacím jazyce.
  2. Zdola nahodu - z elementárních kroků vytváříme prostředky ke zvládnutí problému
  3. oba současně

Definice algoritmu

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"

  1. Hromadnost – algoritmus musí být použitelný pro všechny úlohy stejného typu (dva lidé dojdou podle stejného popisu na stejné místo)
  2. Elementárnost - algoritmus se skládá z konečného počtu jednoduchých kroků (elementy: chůze, jízda autobusem)
  3. Resultativnost – od libovolných vstupních dat musíme dospět k požadovaným výsledkům (vždy dojdeme k výsledku - nezasekneme se uprostřed)
  4. Konečnost – činnost algoritmu skončí v reálném čase
  5. Determinovanost – v každém kroku je znám následující krok (člověk vystoupí z autobusu a ví, že jde na nějaké místo)

Program

Možnosti zápisu algoritmu:

  1. Slovně
  2. Graficky - vývojové diagramy a strukturogramy.
  3. Pomocí programovacího jazyka
  4. Matematicky

Ke kreslení vývojových diagramů používáme standardní grafické symboly:

Jednotlivé značky spojujeme čárami a spojkami:

Slovní vyjádření

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:

Vývojové diagramy

Je to symbolický, algoritmický jazyk, který se používá pro názorné zobrazení algoritmu. Zápis ošetřen normou.

Výhody:

Nevýhody:

Počítačový program

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:

Matematické vyjádření

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:

Zpracování programu

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"

  • Editor
  • Preprocesor
  • Kompilátor
  • Linker
  • Debugger

Preprocesor

  1. Vkládá knihovny (#include...)
  2. Odstraňuje komentáře
  3. Rozvíjí makra (s parametrem / bez parametru, projde a nahradí)
  4. Zpracovává podmíněný překlad (nástroj k testování určité části programu - ostatní je jako vykomentováno)

[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!

Kompiler

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.

  • Syntaxe - pravidla zápisu, pravidla jazyka - lze dohledat v dokumentaci jazyka
  • Sémantika - význam napsaného kódu

Linker

Poskládá OBJ moduly s knihovnami a vytvoří *.exe soubor. Také vkládá absolutní adresy paměti.

Debugger

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)

  • F7 - trace into
    - na řádku, kde jsem se zastavil je volání nějaké mé funkce, zmáčknu F7, tak krokuje tu mou funkci (skočím dovnitř a krokuju)
  • F8 - step over
    - zavoláme svou funkci - už se nekrokuje - dodělá se a jde na další řádek
  • F4 - run to cursor - nepotřebuje break pointy
  • F9 - pokračuj

Řídící struktury - větvení

02-Větvení

Typy podmíněných

  • if-else
  • switch
  • Ternární operátor

Konstrukce if

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");

Konstrukce switch

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");
}

Ternární operátor

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

  • Úplná
  • Neúplná
  • Vícenásobná

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.

Řídící struktury - cykly

03-Cykly

Typy a charakteristika cyklů

  • while / for (cyklus s podmínkou na začátku)
  • do-while (cyklus I s podmínkou na konci)

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ří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.

Příkaz do-while

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

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; }

Poznámky k cyklům

  • Používat pouze jednu řídící proměnnou
  • Řídící proměnná ovlivňována pouze v řídící části cyklu a ne v těle cyklu.
  • Pokud má cyklus prázdné tělo, jeho ukončující středník je na nové řádce.
  • Continue lépe nahradit čitelnějším konstrukcí if-else.
  • Break by se měl vyskytovat jen v nejnutnějším případě a na jednom místě.
  • Přednost while a for před do-while, kvůli přehlednosti.

Grafický zápis

Příkazy break, continue

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.

Příkaz break

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 continue

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).

Příkaz return

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.

Datové typy a proměnné v jazyce C a C++

04-Datové typy

Rozdělení typů

  • jednoduché
    • celočíselné
      • char (1B)
      • short (2B)
      • int (podle prostředí)
      • long (4B)
      • enum (pojemnované konstanty, třeba PI)
      • boolean (u C++)
    • reálné
      • float (4B)
      • double (8B)
      • long double (10B))
    • void
  • strukturované
    • struktura
    • pole (nemá uvozovací slovo)
    • řetězec
    • třída
    • union
    • file (také struktura, ale říkáme, že je nový datový typ)

Co určuje datový typ: "PHO" (!)

  • velikost v paměti
  • množina hodnot (celá čísla v jakém rozsahu)
  • množina operací, kterou můžu udělat

Celočíselné typy

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:

  • x16 = 2B
  • x32 = 4B
  • x64 = 8B

Znakové typy

V jazyce C++ máme k disposici 4 znakové typy

  • char
  • signed char
  • unsigned char
  • wchar.t

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.

Reálná čísla typy

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

Deklarace typů

  • %c - znak
  • %d - celé číslo dekadické
  • %x - celé číslo vypsané HEXa
  • %f - vypiš jako float
  • %s - text
  • %ld - long dekadické
  • %u - celé číslo unsigned
  • %lf - double
  • %Lf - long double

Definování nových typů

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.

Výčtový typ

Struktura

Unie

Soubor

FILE *fw;

Třída

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
};

Proměnné v jazyce C a C++

05-Proměnné

Charakteristika

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

Globální proměnné

Bude dostupná ve všech funkcích v programu za místem své deklarace.

  • Definují se mimo funkce (tz. i mimo funkci main)
  • Automaticky se nulují
  • Paměť se alokuje na začátku programu a uvolňuje na konci (uloženy v datové oblasti)

Lokální proměnné

Jejich platnost je omezena na tělo funkce.

  • Definují se uvnitř bloku nebo funkce za { a jen v ní platí
  • Nenulují se automaticky
  • Paměť se alokuje a uvolňuje v průběhu programu (v zásobníku)

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.

Paměťové třídy

Mění chování proměnných

  • Určí, ve kterých částech paměti bude proměnná kompilátorem umístěna a kde bude dostupná
  • Rozšiřují použití lokálních a globálních proměnných
  • 4 typy: "SERA"
    1. static
    2. extern
    3. register
    4. auto

Auto

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.

  • Je přímo nastavena pro lokální proměnné
  • Nepoužívá se

Extern

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.

  • Je přímo nastavena pro globální proměnné
  • Sdílená proměnná
  • Nealokuje se, ale moduly používají stejnou proměnnou

Static

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!
  • L: Paměť se alokuje v datové oblasti, mezi dvěmi volání funkce si uchovává svou hodnotu
  • G: Je použitelná pouze v souboru, ve kterém ji definujeme
  • G: V každém modulu používáme vlastní globální proměnnou, vzájemně neovlivňují
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.

Register

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á.

  • Proměnná bude uložena registru procesoru, pokud je volný, jinak bude zapsána do zásobníku
  • Pouze pro lokální proměnné
  • Používá se pro rychlejší přístup k proměnné, ale adresu této proměnné nemůžeme získat (není uložena v paměti nelze &a)
  • Používá se pro proměnné cyklu, nebo pro často volané formální parametry

Zdrojové a hlavičkové soubory v C a C++

06-Hlavičkové

Stavba programu

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.

Struktura programu:

  • Hlavička
  • Příkazy preprocesoru: #include<stdio.h>
  • Pojmenovaná konstanta: #define N 10
  • Deklarace globálních proměnných: int a, b;
  • Definice globálních proměnných: int x=50;
  • Deklarace funkcí: int secti(int a, int b);
  • Hlavní funkce: void main(void)
  • Hlavní program: {
  • Definice lokálních proměnných: int a;
  • Tělo programu
  • Konec hlavního programu: }
  • Definice funkce: int secti (int a, int b)

Vlastní hlavičkový soubor

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ů.

Podmíněný překlad

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.

  • Libovolný z úseků_programu může opět obsahovat direktivy #if , #endif. Přitom ovšem musíme dát pozor na správné párování \#if a #endif.
  • Preprocesor odstraní kromě nepřekládaného zdrojového textu samozřejmě také tyto direktivy.

Obvyklá stavba při tvorbě tříd

Když tvořím třídu, deklaraci píšu do souboru s příponou h, do cpp píšu definici (metody).

Projekty

07-Projekty

Práce s více moduly

Vkládání souboru:

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.

Odděleným překlad

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

Práce s projekty v C

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:

  • bin – pro uložení výsledného EXE souboru
  • include – pro uložení hlavičkových souborů s příponou h
  • o – adresář pro soubory s příponou o nebo obj
  • src – adresář pro soubory s příponou c


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, union a výčet v jazyce C

08-Struktura, union a výčet

Struktura

  • struktura je (strukturovaný) heterogenní datový typ (je složen z libovolného počtu prvků různých datových typů)
  • navenek vystupuje jako jeden objekt

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;

Použití

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;

Union

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.

  • Vyhradí se paměť pro největší položku ze všech položek v unionu definovaných
  • Všechny položky unionu se překrývají (ve struktuře by ležely za sebou), tzn. může být v jednom okamžiku použita pouze jedna položka
  • union neposkytuje informaci o prvku, který do něj byl naposledy uložen
typedef union {
               char c;
               int i;
               float f;
              } TEST;

Výčet

Jednoduchý celočíselný

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

Operátory v jazyce C a C++

09-Operátory

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).

Rozdělení

  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).

Aritmetické operátory

  • Unární
    • +
    • -
  • Multiplikativní
    • *
    • /
    • %
  • Aditivní
    • +
    • -
  • Relační
    • ==
    • !=
  • Nerovnosti
    • >
    • <
    • <=
    • >=
  • Logické
  • Konjunkce
    • &&
  • Disjunkce
    • ||
  • Inkrementace a dekrementace
    • ++
    • --
  • Přiřazovací
    • +=
    • %= atd.

Unární operátory:

  • Mají zpravidla vyšší prioritu než binární operátory
  • Unární operátory využívají zpravidla prefixový zápis (nejprve operátor, pak operand)
  • Vyjímkou jsou operátory ++ a --, které mají různý význam v prefixovém a postfixovém zápisu

Ternární

Operátor podmíněného výrazu ?:

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í:

  • Jsou-li oba operandy typu void, je i výsledek téhož typu.
  • Jsou-li oba operandy stejného typu a jsou-li to l-hodnoty, je výsledek téhož typu a je to také l-hodnota.
  • Jinak se operandy převedou pomocí standardních konverzí na stejný typ a to bude i typ výsledku.

Poznámka:
Tento operátor se nepovažuje za jediný symbol, můžeme ho rozdělit bílými znaky.

Priorita operátorů

 
  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ů

10-Přetěžování

Pravidla

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.)

  • Nelze přetížit operátory
    • =
    • ::
    • ?:
    • .
    • *
    • sizeof
    • []
    • #
  • Nelze definovat nové operátory (např. vzít znak @ a definovat jej jako operátor)
  • Nelze měnit obecné charakteristiky operátorů – počet operandů, prioritu, asociativitu.
  • Nelze měnit definici operátorů pro standardní datové typy.
  • Operátorové funkce (mimo new a delete) mohou vracet jakýkoliv datový typ.

Bez tříd

  //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;
  }
  

Ve třídách

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 v jazyce C a C++

11-Funkce

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“.

Stavba a vlastnosti

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.

Parametry

Formální parametry (z hlavičky funkce) jsou volány:

  • hodnotou – převezmou ze skutečného parametru hodnotu, ale skutečný parametr nemohou ovlivnit
  • odkazem – do formálního parametru (pointeru) se uloží adresa skutečného parametru, proto může skutečný parametr změnit

Parametry předávané:

  • Hodnotou
    • skutečný, ten kdy voláme
    • formální, odkazem / hodnotou - vezme si ze skutečných hodnotu, nemůžeme je přepsat
  • Odkazem - pointer na proměnnou, stejnou fci obsluhujeme ze 2 míst, nemůžou se jmenovat stejně

Formální parametry a lokální proměnné jsou přístupné pouze ve funkci, ve které byly definovány.

Rekurzivní

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í)

Přímá rekurze

double faktorial(double x)
{
 double vys;
	
 if (x > 1) vys=x*(faktorial(x-1));
  else return(vys);
}

Přetěžování

Přetěžovat lze funkce pouze v C++. Funkce se pak stejně jmenuje, ale má jiný počet nebo datové typy parametru.

Činnost preprocesoru v jazyce C

12-Preprocesor

Charakteristika

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ř.

  • začlení soubory vkládané direktivou #include
  • odstraní ze zdrojového textu komentáře
  • odstraní části určené direktivami #if.... , jež nemají být kompilovány
  • rozvine předdefinovaná makra atd.

Č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.

Tvorba maker

Příkaz preprocesoru ukončuje '\', začíná '#'. Jmenuje se direktiva.

#define MAX 10

Kdekoliv v kódu najde MAX, nahradí to 10. (case sensitive)

Makra s parametrem

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:

  • neřeší se datové typy
  • nealokuje se paměť
  • rychlejší
  • špatně se hledají chyby
  • dostatečně závorkovat!

Podmíněný překlad

= 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.

  • Libovolný z úseků_programu může opět obsahovat direktivy #if , #endif. Přitom ovšem musíme dát pozor na správné párování \#if a #endif.
  • Preprocesor odstraní kromě nepřekládaného zdrojového textu samozřejmě také tyto direktivy.

Hlavičkové soubory

Pointery v jazyce C a C++

intro

13-Pointery

Deklarace a charakteristika

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á)

  • &s : adresa pointeru
  •  s : obsah pointeru (adresa kam ukazuje)
  • *s : hodnota na adrese, kam ukazuje
  • &a - adresa proměnné
  •  a - obsah proměnné (hodnota)

a = 3,1 == *s = 3.1;

Pro tento výraz platí odkaz na statickou proměnnou na zásobníku:

p = &a;

Dynamické

Pointer p ukazuje na dynamickou proměnnou:

p = (float*)malloc(sizeof(float));
  • malloc alokuje místo v paměti
  • malloc musíme přetypovat, protože jinak vrací ukazatel na void
*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;

Pointerová aritmetika

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.

Součet pointeru a celého čísla

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.

součet pointeru a celého čísla

Rozdíl pointeru a celého čísla

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ů'.

rozdíl pointeru a celého čísla

Odečítání pointerů

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.

odečítání pointerů

Porovnání pointerů

ukazatel1 – ukazatel2
Má smysl pokud oba ukazatelé ukazují na stejný celek paměti a jsou stejného datového typu.

porovnání pointerů

  • <
  • <=
  • >
  • >=
  • ==
  • !=

Pointerovou aritmetiku používáme k přístupu jednotlivým prvkům pole.

pointerová aritmetika

Pomocí všech tří pointerů vypište adresu i hodnotu čtvrtého prvku pole p pointerovou aritmetikou.

pointerová aritmetika

pointerová aritmetika

Pointery a funkce

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);

parametry

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);
}

pointery a funkce

Pozn. přidat referene!

Jednorozměrné pole v jazyce C

intro

14-Jednorozměrná pole

Pole je uspořádaná množina hodnot stejného datového typu, tj. homogenní strukturovaný datový typ

Statické pole

V době překladu musí být znám počet prvků pole.

Deklarace

int p[5];

jednorozměrné pole

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.

Plnění pole

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.

jednorozměrné pole

Kopírování prvků pole

char a[ ]={‘a’, ‘e’, ‘i’, ‘o’, ‘u’, ‘y’};
char b[6];
  for (i=0; i<6; i++)
   b[i]=a[i];

jednorozměrné pole

Výpis pole

float a[5]={0,2,4,6,};
 for (i=0; i<5; i++)
  printf(" %f", a[i]);

Dynamické pole

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.

dynamické 1 rozměrné pole

int a[5], *p;
p = (int*)malloc(sizeof(int)*5);
...
 free(p);
 p=NULL;

dynamické 1 rozměrné pole

Pole jako parametr funkce

Třídící algoritmus

  • Bubble sort - bublinové
  • Heapsort - haldou
  • Insertion sort – vkládáním
  • Merge sort - slučováním
  • Quick sort - rychlé
  • Selection sort - výběrem
  • Shell sort - Shellovo
  • Bucket sort – přihrádkové
  • Radix sort – třídění podle základu
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;
      }
   }
  }

26-Zdroje

Seznam

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