Hledejte v chronologicky řazené databázi studijních materiálů (starší / novější příspěvky).

29. Konstruktory a destruktory.

Konstruktor

Každá instance je vytvořena pomocí speciální metody - konstruktoru. Konstruktor se musí jmenovat stejně jako třída, jejíž instance bude vytvářet, nesmí mít návratovou hodnotu a měl by být veřejný. Každá třída může mít k dispozici více konstruktorů s různými parametry. Pak se jedná o přetěžování konstruktorů(netýká se otázky). Nemá-li třída žádný konstruktor (Například třída MojePrvniTrida v předcházející otázce č. 26.) vytvoří překladač takzvaný implicitní konstruktor bez parametrů. V konstruktoru by se měla provést inicializace dané instance. Nastavit všechny její atributy, vytvořit instance tříd, které obsahuje atd.

Příklad třídy i s konstruktory:

class Zlomek
{
private:
int Jmenovatel, Citatel;
public:
Zlomek(); /* Bezparametrický konstruktor */
Zlomek(int cislo);
Zlomek(int citatel, int jmenovatel);

~Zlomek(); //Bezparametrický destruktor.
/* Ostatní metody třídy zlomek: */
int dejCitatel();
int dejJmenovatel();
void nastavCitatel(int citatel);
void nastavJmenovatel(int jmenovatel);
float hodnota();
};

Zlomek::Zlomek() //1. konstruktor
{
Jmenovatel = 1;
Citatel = 0;
}

Zlomek::Zlomek(int cislo) //2.konstruktor
{
Citatel = cislo;
Jmenovatel = 1;
}

Zlomek::Zlomek(int citatel, int jmenovatel) //3. konstruktor
{
Citatel = citatel;
if (jmenovatel != 0)
Jmenovatel = jmenovatel;
else
Jmenovatel = 1;
}

int Zlomek::dejCitatel()
{ return Citatel; }

int Zlomek::dejJmenovatel()

{ return Jmenovatel; }

void Zlomek::nastavCitatel(int citatel)
{ Citatel = citatel; }

void Zlomek::nastavJmenovatel(int jmenovatel)
{
if (jmenovatel != 0)
Jmenovatel = jmenovatel;
}

float Zlomek::hodnota()
{ return ((float)Citatel)/Jmenovatel; }



Zlomek::~Zlomek()
{
cout << "Loučí se s vámi instance třídy zlomek. " << endl; << dejCitatel() << '/' << dejJmenovatel() << endl;
}

Destruktor
Destruktor, jak již asi sám jeho název napovídá, slouží k likvidaci objektů. Destruktor (stejně jako konstruktor) je metoda, která je zavolána na instanci v momentě, kdy je instance likvidována. Destruktor se jmenuje stejně jako třída, jen před názvem třídy je znak ~ . Destruktor nesmí mít návratovou hodnotu a žádné parametry. V destruktoru by se měly uvolnit všechny zdroje, se kterými instance pracovala. Například uzavřít datové soubory, uvolnit případnou alokovanou paměť a podobně. Nemá-li třída destruktor, překladač vytvoří implicitní destruktor. Ve třídě Zlomek naprosto stačí implicitní destruktor. Ale jako příklad je uveden.

28. Odvozené třídy: předkové, potomci, dědictví.

Vztahy mezi třídami:
Mezi třídami existují 3 druhy vztahů (vazeb):
Agregace - "obsažení" (Anglicky aggregation) Jestliže třída A obsahuje třídu B, potom instance třídy A v sobě obsahují instance třídy B. Například v každé instanci třídy počítač existuje instance třídy procesor. Znázorněno v diagramu:

Asociace - "Link" (Anglicky association) Jestliže Třída A spojuje(asociuje) třídu B, potom instance třídy A nějakým způsobem musejí "vědět" o instancích třídy B. Zná-li objekt nějaký jiný objekt, znamená to, že jej může používat, zasílat mu zprávy a podobně. Například instance třídy učitel zná instance třídy žák. Není zde vhodná vazba agregace, protože učitel neobsahuje žáky uvnitř sebe. Asociace je obecnějsí vazba než agregace. Znázorněno v třídním diagramu:

Dědičnost - "Specializace" (Anglicky inheritance) Třída A dědí ze třídy B, jestliže třída A je podmnožinou třídy B. Třída A se nazývá podtřídou třídy B, a třída B se nazývá nadtřídou třídy A. V tomto případě instance třídy A jsou zároveň instancemi třídy B, naopak to však nemusí platit. Například třída okna v grafickém OS je nadtřídou třídy dialogových oken, nadtřídou třídy modálních oken a podobně. Znázorněno v třídním diagramu:

Rozdíl mezi asociaci a agregaci je zřejmý. Při agregaci objekty vždy obsahují jiné objekty v sobě. Při vztahu agregace (A agreguje B) by měly cizí objekty požadovat služby instancí agregované třídy (Třídy B) jen pomocí služeb instancí třídy A. Často se chybně zaměňuje mezi agregací, a dědičností. Je-li vztah mezi objekty "má", nebo "vlastní", potom se jedná agregaci. Například řetězec má (vlastní) znaky. Naopak je-li mezi objekty vztah "je", potom se jedná o dědičnost. Například dialogové okno je okno.

27. Přístupová práva, inicializace, objektové typy a přiřazovací příkaz.

Přístupová práva viz otázka č. 26

Inicializace
Inicializace statických složek může být provedena v jejich definiční deklaraci . V ostatních případech je třeba využít konstruktorů.

Objektové typy a přiřazovací příkaz
Jsou-li aa a bb dvě instance téhož typu třída, např. class A, lze použít přiřazovací příkaz

aa = bb;

ten způsobí, že hodnoty všech datových složek bb jsou přiřazeny odpovídajícím složkám aa.

26. Deklarace třídy, datové složky(atributy), metody(členské funkce).

Viz předchozí otázka č. 26

25. Objektové typy v C++, typy struktura, třída a unie.

Struktura viz otázka č. 16

Datový typ union:
Druhým datovým typem, o kterém dnes bude řeč, je union. Union si lze jednoduše představit jako obyčejnou strukturu, kde ale všechny její položky sdílejí jedno paměťové místo, tedy se překrývají (jsou na stejné adrese). Zatímco u obyčejných struktur bylo pro každou položku alokováno zvláštní místo v paměti, v případě unionů je tedy alokováno místo pouze pro největší z nich.

Definice unionů je prakticky shodná s definicí struktur:

union UN{
char c;
int pocet;
double delka;
};

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:

struct UNIE{
int info;
UN x;
};

Třídy (class) - vytváření tříd:
Definice třídy
K vytvoření třídy slouží klíčové slovo class. Definice vypadá následovně:

class NázevTřídy [: předchůdci třídy]
{ Položky třídy };

Tato definice třídy není jediná. Třída je v C++ velmi podobná struktuře a také jsou k dispozici stejné možnosti pro vytváření třídy a struktury.

[: předchůdci třídy] je nepovinná část, která se týká dědičnosti(viz otázka č. 29). Položky třídy jsou proměnné, nebo metody(vztahuje se k následující otázce č. 27). Pojmu metoda se v C++ také někdy říká členská funkce (Member function). Metoda je vlastně funkce, která není globální, ale je volatelná jen v souvislosti s instancemi dané třídy. Položky(týká se přístupových práv z otázky č. 28) mohou být soukromé(private), chráněné(protected) a veřejné(public).

Soukromé položky mohou být proměnné, ke kterým mají přístup pouze metody dané třídy, nebo metody, které mohou být volány jen jinými metodami dané třídy.

Naopak veřejné položky může využívat kdokoliv. Veřejné položky tvoří rozhraní objektu dané třídy. Soukromé položky jsou zapouzdřeny, a přístup k nim se provádí jen přes veřejné rozhranní. Jako veřejné položky by neměli být proměnné, protože by docházelo k porušení zapouzdření. V praxi se ale na zapouzdření někdy moc nehledí. Chráněné položky jsou vlastně soukromé položky, které se někdy chovají jako veřejné. Jako veřejné se chovají k tak zvaným přátelským funkcím a vůči instancím tak zvaných přátelských tříd. Použití chráněných položek je vlastně také porušení zapouzdření.

Příklad třídy:

class MojePrvniTrida
{
private: /* Následující položky jsou soukromé.*/
int Cislo;
char Znak;
public: /* Následující položky jsou veřejné.*/
int VratMiTvojeCislo();
void NastavSiCislo(int noveCislo);
};

V této definici třídy jsou deklarovány hlavičky metod. Mohli bychom napsat těla metod hned v definici třídy, ale u větších tříd by takový zápis nebyl příliš přehledný.
Nedůležité: Také každá metoda, jejíž tělo je definováno v definici třídy je překládána, jako inline. Tedy funkce (nebo metoda) nebude volána, ale překladač vloží její tělo na místo jejího zavolání. Inline metoda je něco jako-by makro. Na rozdíl od makra, které je vkládáno na úrovni zdrojového textu, je inline funkce vkládána na úrovni přeloženého binárního kódu.
Inline není pro překladač závazný!

Definice těla metod:
int MojePrvniTrida::VratMiTvojeCislo()

/* Operátor :: (čtyř-tečka) oznamuje překladači, že VratMiTvojeCislo() není globální funkce, ale členská metoda danné třídy.*/
{
int a = 3; /* Ukázka lokální proměnné*/
return Cislo;
}

V těle této metody je lokální proměnná. Proměnná Cislo je proměnná, která je definována v třídě. Každá instance této třídy bude obsahovat svou proměnnou Cislo. V těle metod můžeme také použít globální proměnné, kdyby nějaké byly.

void MojePrvniTrida::NastavSiCislo(int noveCislo)
{
Cislo = noveCislo;
}

V čistě objektově orientovaném programu neexistují globální funkce, ale jen metody. V C++ je trochu problematické napsat čistý objektově orientovaný program(proto je ideální např. Java). Už jen proto, že funkce main je globální funkcí, není metodou žádné třídy.

24. Operace s ostatními soubory, ovládání souborů prostřednictvím ukazatelů.

Práce se soubory:
V jazyce C++ je práce se soubory realizována pomocí tzv. datových proudů (streams), které si lze představit jako určitou konečnou posloupnost bytů, jako jsou například soubory. Datové proudy ale nemusí nutně představovat jenom soubory, ale mohou to být třeba data proudící sítí apod. Datovými proudy jsou například i standardní vstup a výstup.

Souborový proud je v C++ představován ukazatelem na strukturu FILE(možno použít jako příklad předchozí otázky ukazatel na struktury☺), ve které jsou uloženy nejrůznější informace o příslušném souboru, a tak můžeme tento proud definovat stejně jako jakýkoliv jiný ukazatel v C.

FILE *identifikátor;

Takto nadefinovaný pointer je ale samozřejmě neinicializovaný a pro přístup k souboru jej tedy ještě nelze použít. Pro jeho inicializaci použijeme funkci fopen().


Otevření souboru

FILE *fopen(char *jméno_souboru, char *mód_otevření);

Funkce fopen() se pokusí otevřít soubor, jehož jméno jsme jí předali v řetězci jméno_souboru. V případě, že se soubor podařilo otevřít, vrátí funkce příslušný ukazatel. Pokud se však soubor otevřít nepodařilo (například pokud nebyl nalezen), vrací hodnotu NULL. Protože možnost, že se soubor nepodaří otevřít, je velice reálná, neměli bychom zapomínat tuto skutečnost testovat.
Druhým parametrem mód_otevření určujeme, v jakém módu bude soubor otevřen. I tento parametr je definován jako řetězec. Jednotlivé režimy otevření souboru určujeme pomocí kombinace několika znaků.

r (read) Soubor bude otevřen pro čtení.

w (write) Soubor bude otevřen pro zápis. V případě, že otevíraný soubor již existuje, bude nejdřív zkrácen na nulovou délku. Pokud otevíraný soubor dosud neexistuje, vytvoří se nový.

a (append) Stejně jako v předchozím případě bude soubor otevřen pro zápis. Pokud otevíraný soubor neexistuje, vytvoří se nový, ale jestliže soubor již existuje, nebude, narozdíl od módu w, nijak zkracován.

Po otevření souboru se automaticky nastavuje pozice tzv. file-pointeru, tedy místa, ze kterého se bude číst, nebo kam se bude zapisovat. Pro módy r a w je file-pointer nastaven na začátek souboru a pro soubor otevřený v režimu append je nastaven na pozici za posledním bytem souboru. Mód append je tedy určen pro připojení nových dat k souboru.

Všechny tři režimy otevření souboru lze ještě doplnit o znaménko +. Pak bude soubor otevřen najednou pro čtení i zápis.

Dále můžeme v C++ otevírat soubory buď jako textové, nebo jako binární(zvlášť vhodné pro struktury). To určíme pomocí znaků b nebo t připojených k řetězci mode. Oba způsoby jsou skoro stejné, ale narozdíl od souborů, které byly otevřeny jako binární, pro soubory otevřené v textovém módu se při čtení i při zápisu provádí konverze oddělovače řádků. Je- li tedy, například v DOSu, oddělovačem řádků sekvence znaků '\13' a '\10', ze souboru se celá dvojice přečte jako znak '\n'. Naopak, pokud zapisujeme znak '\n', ve skutečnosti se zapíše celá kombinace '\13' a '\10'.

Příklad: Testování, zda byl soubor otevřen

FILE *f;

...

f = fopen("soubor.txt", "rb+");
if (f==NULL) { puts("Chyba: Soubor se nepodařilo otevřít.");
return(false);
}
...

Uzavření souboru
Přestaneme-li s otevřeným souborem pracovat, měli bychom ho, ačkoliv to není povinné, hned uzavřít. Počet najednou otevřených souborů je totiž omezen. Uzavření souboru provedeme voláním funkce fclose().

int fclose (FILE *file);

Podařilo-li se soubor uzavřít, vrací funkce hodnotu 0. Opačný případ, tedy že se soubor uzavřít nepodařilo, nastává nejčastěji, snažíme-li se uzavřít v dané chvíli neotevřený soubor. V takovém případě vrací funkce hodnotu EOF. Jiný důvod selhání prakticky nenastává, a proto se návratová hodnota funkce fclose() většinou ani netestuje.

Testování konce souboru
Testovat konec souboru se dá v zásadě dvěma způsoby. Buď kontrolou návratových hodnot čtecích funkcí, nebo použitím funkce feof(). První způsob se používá především pro čtení pomocí funkce getc(), která vrací hodnotu EOF, pokud bylo dosaženo konce souboru. Druhým způsobem, pomocí funkce feof(), můžeme stejnou skutečnost zjistit pro libovolnou operaci čtení. Pokud se poslední taková operace pokoušela číst již za koncem souboru, vrací funkce feof() pravdivou (nenulovou) hodnotu. V opačném případě vrací nulu.

int feof(FILE *file);

Funkce pro zápis do souboru
Pro zápis do souboru nám poslouží funkce putc(), fprintf(), a fputs(), jejichž deklarace jsou zde:

int putc(int c, FILE *file);
int fprintf(FILE *file, char *formát, ...);
int fputs (char *string, FILE *file);

Po provedení některé z těchto funkcí se file-pointer posune v souboru za poslední zapsaný byte. To ostatně platí i pro všechny další funkce zapisující do souboru.

Funkce fwrite() – slouží pro binární zápis

int fwrite(void *ptr, int size, int n, FILE *file);

Funkce fwrite() zapíše do proudu file n položek přečtených z paměti označené ukazatelem ptr. Velikost jedné položky v bytech udává parametr size, takže celkový počet zapisovaných bytů je roven size*n. Funkce fwrite() vrací počet úspěšně zapsaných položek, nikoli bytů!

Funkce pro čtení ze souboru

int getc(FILE *file);
int fscanf(FILE *file, char *formát, ...);
char *fgets(char *string, int n, FILE *file);

Zajímavá je především funkce fgets, která oproti příbuzné funkci gets umožňuje definovat maximální počet přečtených znaků a lze ji použít i ke čtení ze standardního vstupu.

char str[50];

fgets(str, 49, stdin);

I funkce čtoucí ze souboru posouvají file-pointer, a to za poslední přečtený znak souboru.

Funkce fread() – slouží pro binární čtení
Pro blokové čtení ze souboru nám poslouží funkce fread():

int fread(void *ptr, int size, int n, FILE *file);

Z datového proudu file přečte n položek o velikosti size bytů a uloží je do paměti označené ukazatelem ptr. Návratovou hodnotou je počet skutečně přečtených položek.

Nesekvenční přístup:
Na souborové datové proudy lze použít tzv. nesekvenční (náhodný) přístup, což znamená, že nemusíme číst jeden byte za druhým tak, jak jsou v souboru uloženy, ale můžeme „skákat“ (přesunovat file-pointer) na libovolnou pozici v souboru. K tomuto účelu slouží funkce fseek().
int fseek (FILE *file, long offset, int mode);

Funkce přesune file-pointer na pozici vzdálenou offset bytů od pozice mode, přičemž za parametr mode je možné zvolit jednu z následujících konstant:

SEEK_SET Začátek souboru
SEEK_CUR Aktuální pozice v souboru
SEEK_END Konec souboru

Příklady:

zápis file----------------pointer se přesune na...
fseek(f, 0, SEEK_SET); začátek souboru
fseek(f, 0, SEEK_END); konec souboru
fseek(f, -10, SEEK_CUR); pozici o deset bytů zpět od aktualní pozice
fseek(f, 10, SEEK_SET); jedenactý byte souboru

Je-li soubor, se kterým pracujeme, otevřen v módu pro čtení i zápis, musíme mezi každou operací čtení a operací zápisu volat funkci fseek(). Tato nepříjemnost je nutná kvůli synchronizaci file-pointerů, které jinak nemusí být správně nastaveny. Funkci fseek() ale můžeme volat i s nulovým posunem.

Chceme-li zjistit, jaká je aktuální pozice file-pointeru v souboru, můžeme použít funkci ftell(), která tuto pozici vrací jako svou návratovou hodnotu.

long ftell (FILE *file);

23. Ukazatele na struktury, ukazatele na funkce.

Ukazatel na struktury:
Na struktury lze ukazovat stejně jako na jakýkoliv typ proměnné.

název_struktury *identifikátor;

Příklad:

struct DATUM{
int den;
int mesic;
int rok;
};

struct CD{
char nazev[31];
int cislo;
DATUM vydano;
};

void main()
{
CD *test = new CD; //deklarace ukazatele test
printf(“%d“,test->cislo); /*Vypíše obsah proměnné cislo ze struktury na
kterou ukazuje test.*/
printf("%d",(*test).cislo); //Udělá totéž
printf(“%d“,(*test).vydano.den); /*Vypíše obsah proměnné den ve struktuře DATUM, kterou obsahuje struktura CD.*/
}

Z předcházejícího příkladu je viděť, že lze vytvářet libovolně vnořené struktury a přistupovat k nim pomocí pointerů.

Největší pointerů je, pokud chceme vytvořit dynamickou databázi struktur. Pak by struktura CD ještě obsahovala ukazatel sama na sebe, které bychom vždy přiřadily adresu následující struktury CD. Příklad viz maturita z Cčka :)

Ukazatele na funkce:
Kromě klasických datových ukazatelů, s kterými jsme se doposud setkávali, nabízí jazyk C(C++) i ukazatele kódové. Ty ukazují na určitý úsek kódu (funkci) programu, který pak můžeme spustit nepřímo, právě pomocí těchto ukazatelů.

Definice ukazatele na funkci
Uvažujme, že máme nadefinovanou tuto funkci pro sčítání čísel double:

double secti(double a, double b)
{ return a+b;
}

Tuto funkci můžeme volat normálně, jak jsme zvyklí, nebo si můžeme vytvořit ukazatel na ni a volat ji pomocí něho. Formální zápis definice takového pointeru vypadá takto:

typ (*identifikátor)(seznam_typů_parametrů)

Typem rozumíme datový typ, který vrací funkce, na kterou bude ukazovat náš pointer. Identifikátor je pak označením tohoto nového pointeru a v seznamu_typů_parametrů jsou, v odpovídajícím pořadí, uvedeny všechny typy parametrů odkazované funkce. Tento seznam sice můžeme nechat prázdný, ale kompilátor by v tomto případě neměl informace o typech parametrů funkce. Pokud bychom pak volali funkci, nepřímo přes ukazatel, se špatnými typy parametrů, mohlo by to vést ke špatné funkci programu.

Příklad na funkci secti():

double (*pf)(double, double);

Pointer pf teď může ukazovat na jakoukoliv funkci, která má dva parametry typu double a hodnotu typu double i vrací, tedy může ukazovat i na naši funkci secti(). Tento ukazatel je ale zatím neinicializovaný a pro volání funkce ho ještě nemůžeme použít. Jeho inicializaci provedeme prostým přiřazením identifikátoru existující funkce.

Inicializace kódového ukazatele

pf=secti;

Do proměnné pf byl uložen ukazatel na funkci secti(). Identifikátor přiřazované funkce se musí uvádět bez závorek se seznamem parametrů a nemusíme(ale můžeme) použít operátor & pro získání adresy. Kompilátor dokáže rozeznat, že chceme přiřadit ukazatel na funkci a ne funkci samotnou.

pf=§secti;

Ukazatel na funkci se dá inicializovat i jinými způsoby, podobně jako datový ukazatel. Můžeme tedy přiřazovat vzájemně kompatibilní ukazatele, inicializovat návratovou hodnotou funkce apod.

Volání funkce pomocí ukazatele
Jak už jsme si řekli, lze k volání funkce použít i ukazatele na tuto funkci. Zápis takového volání se řídí podle jednoho z následujících zápisů:

(*identifikátor_ukazatele)(seznam_skutečných parametrů);

nebo

identifikátor_ukazatele(seznam_skutečných parametrů);

Oba zápisy jsou funkčně ekvivalentní, a protože druhý z nich je jednodušší a bližší skutečnému zápisu volání funkce, budeme ho nadále používat.
Výše nadefinovaný a inicializovaný pointer pf bychom tedy pro zavolání jím odkazované funkce použili následujícím způsobem.

pf(15,4);

Výsledkem po vyhodnocení tohoto výrazu bude double hodnota 19.0, stejně jako kdybychom zavolali funkci

secti(15,4);

na kterou pointer pf ukazuje.

Kódový ukazatel jako parametr funkce a její návratová hodnota
I kódový ukazatel můžeme bez problémů předat funkci jako její parametr, a to stejně, jako bychom to udělali s proměnnou jiného typu.

22. Rekurze, prototyp funkce.

Rekurze:

Jako rekurzivní označujeme takové volání funkce F, které nastane dříve, než je dokončeno volání předchozí.

Příklad

Klasickým příkladem na rekurzivní výpočty je funkce faktoriál:

unsigned long Fak(unsigned n)
{
if(n) return n*Fak(n-1);
else return 1;
}


Prototyp funkce(deklarace funkce) – zkopírováno z otázky č.5:

Deklarace funkce je vlastně způsob jak dát překladači všechny potřebné údaje o funkci, aniž bychom ji museli celou definovat(viz Definice funkce výše). Předtím, než funkci zavoláme, měla by být vždy předem definována, nebo deklarována. To proto, aby překladač znal všechny formální parametry, a tak mohl vytvořit správný kód.

Pokud funkci nebudeme ještě před jejím voláním deklarovat ani definovat, bude překladač odhadovat formální parametry podle typu skutečných parametrů (parametry předané funkci při jejím volání), které ale nemusí odpovídat typu parametrů formálních. Výsledkem by pak byl nesprávně sestavený kód. Pokud ale z nějakého důvodu není funkce definována před svým použitím, měla by být alespoň deklarována.

Deklarace funkce vypadá takhle:

návratový_typ identifikátor_funkce (seznam definicí formálních parametrů);

Je to vlastně celá hlavička definice, která je ale zakončená středníkem.

int secti (int a, int b); /*prototyp funkce - deklarace výše definované funkce.*/

21. Aritmetické operátory, relační, logické, inkrementování a dekrementování(celá otázka zkopírována z otázky č.3).

aritmetické operátory
+, -, *
/ Celočíselné, nebo reálné, dělení. To, jestli bude provedeno dělení celočíselné, nebo reálné, záleží na typu operandů. Pokud oba operandy budou celočíselného typu, provede se celočíselné dělení. Pokud ovšem alespoň jeden z operandů bude reálného typu, provede se dělení reálné. V případě, že potřebujeme provést reálné dělení na operandech celočíselných typů, musíme jeden z nich přetypovat pomocí operátoru přetypování (viz. dále) na reálný typ.

% Dělení modulo, neboli zbytek po celočíselném dělení.

= Přiřazení.

relační operátory
<, <=, >, >= menší než, menší nebo rovno, atd...

!= nerovnost

== rovnost

logické operátory
&& logický součin (AND)

|| logický součet (OR)

^ logický XOR

! negace

operátory inkrementace a dekrementace
++ Inkrementace. Zvýší hodnotu operandu o 1.
-- Dekrementace. Sníží hodnotu operandu o 1.

Oba operátory se dají použít jak před svým operandem, tak za ním. Význam je ale pokaždé jiný. Pokud operátor uvedeme před operandem, jedná se o tzv. preinkrementaci. To znamená, že hodnota operandu se nejprve zvýší a tato zvýšená hodnota je vrácena jako výsledek operace. Naopak při postinkrementaci je jako hodnota celého výrazu vrácena původní hodnota operandu a pak je teprve operand samotný zvýšen o 1.

int a,b=1;
a = b++;
/*Postinkrementace. Výraz b++ je nejdříve vyhodnocen (a jeho hodnota přiřazena proměnné a) a pak je teprve inkrementována proměnná b. Proměnná a teď tedy má hodnotu 1 a b je rovno 2.*/

a = ++b;
/*Preinkrementace. Nejdříve je inkrementováno b na hodnotu 3. Tato hodnota je i výsledkem vyhodnocení výrazu ++b a tedy je přiřazena proměnné a. Hodnota a je 3.*/

19. Předávání parametrů hodnotou a odkazem.

Viz otázka č. 4

20. Operátor přetypování, operátor získání adresy a velikosti typu.

Operátor přetypování(zkopírováno z otázky č. 3):
I když se při neshodách typů provádí implicitní přetypování, někdy potřebujeme explicitně přetypovat jeden typ na jiný. To provedeme pomocí operátoru přetypování. Zápis vypadá tak, že před samotný výraz, který chceme přetypovat, uvedeme v kulatých závorkách jméno nového typu.

int a=5, b=3;
double c;
c = a / (double)b; /*Pokud bychom nepoužili přetypování, provedlo by se celočíselné dělení. Pro vynucení dělení reálného musíme hodnotu výrazu b přetypovat na double. */

Operátor získání adresy - Referenční operátor &:
Abychom mohli do ukazatelové proměnné přiřadit hodnotu (nějakou adresu), musíme ji nejprve někde získat. K tomu nám může posloužit referenční operátor '&'. Jeho zapsáním před identifikátor proměnné získáme adresu paměti, kde je tato proměnná uložena. Tuto adresu pak můžeme přiřadit nějaké ukazatelové proměnné.

Příklad:

char *p, c;

c = 12;
p = &c;
// p nyní ukazuje na proměnnou c

Operátor velikosti typu - Operátor sizeof :
Tento operátor slouží ke zjišťování velikosti svého operandu. Existují dva typy operandů, na které můžeme operátor sizeof aplikovat. Je to buď identifikátor některého datového typu:

sizeof( identifikátor_datového_typu )
nebo libovolný výraz:
sizeof výraz

V případě, že operandem bude identifikátor některého datového typu (musí být uveden v závorkách), je výsledkem vyhodnocení velikost, jakou by v paměti zabíral objekt (např. proměnná) tohoto typu. Množina použitelných typů, na které lze sizeof aplikovat, není nijak omezena a je možné použít tento operátor i na uživatelsky definované typy.

Příklad:

sizeof(int) //velikost int
sizeof(int*) //velikost ukazatele na int
sizeof(int [10]) //velikost pole deseti intových položek
sizeof(struct {int x; char a;}) //velikost zadané struktury

Druhý případ, kdy operátor sizeof použijeme na výraz, je podobný. Operátor v tomto případě vrací velikost paměti, jaká je potřeba k uložení výsledku vyhodnocení předaného výrazu. Tedy, je-li výsledkem vyhodnocení výrazu hodnota typu int, pak operátor sizeof, aplikovaný na tento výraz, vrací velikost datového typu int.

18. Vícenásobné definice funkcí, implicitní hodnoty parametrů, nepoužité parametry.

Přetěžování funkcí:
Pozn.: Mělo by jít o vícenásobné definice funkcí, ale nejsem si jistý.
Přetížení funkcí je možnost deklarovat, i definovat více funkcí stejného jména s různým počtem, nebo s různými typy parametrů.

Příklad: Mohu definovat dvě funkce se stejným jménem max.

int max(int a, int b)
{
return (a>b)? a:b;
}

float max(float a, float b)
{
return (a>b)? a:b;
}

Zavolám-li funkci max s parametry typu float vrátí mi větší z nich jako typ float. Zavolám-li funkci max s parametry typu int vrátí mi větší z nich jako typ int. Při přetěžování funkcí musíte dávat pozor na případné nejednoznačnosti. Kdyby jste v našem příkladě zavolali funkci max(1.0,2.0), překladač by vás upozornil na chybu. Neví totiž, zda volat max s parametry float, nebo parametry implicitně přetypovat na int a volat max s parametry int. Tedy volání funkce je nejednoznačné, překladač neví, kterou funkci vybrat. Tento problém vyřešíte, jestliže budete volat max((float)2.0,(float)3.5).

Implicitní hodnoty parametrů funkcí:
Jestliže předpokládáte, že budete funkci často volat s nějakou hodnotou parametru, můžeme tuto hodnotu uvést jako implicitní, a při volání ji v seznamu parametrů neuvádět.

Příklad:

int soucet(int a, int b, int c = 0) /* 3. parametr je implicitně 0. */
{
return (a+b+c); /* vracím součet parametrů*/
}

void main()
{
int cislo;
. . . // Nějaký program
cislo = soucet(1,2,3); /* Zavolá se funkce soucet s parametry a = 1 , b = 2, c = 3 */
. . . // Nějaký program
cislo = soucet(2,3); /* Zavolá se funkce soucet s parametry a = 2 , b = 3, c = 0. Za c se implicitně dosadí 0. Je to stejný výsledek, jako kdybychom napsali cislo = soucet(2,3,0). */
}

Implicitní hodnoty parametrů mohu zadávat jen v seznamu parametrů zprava. Pokud by tedy v naší funkci součet nemel parametr c implicitní hodnotu, parametr b by ji také nemohl mít. Parametr c (1. zprava) implicitní hodnotu má, proto lze definovat i implicitní hodnotu parametru b (2. zprava).

Nepoužité parametry:
Občas se stane, že potřebujeme deklarovat funkci s parametry, které ve skutečnosti nepotřebujeme. Jazyk C++ nabízí následující možnost: v definiční deklaraci uvedeme pouze typ parametru, nikoli jeho identifikátor.Taková deklarace způsobí, že překladač nás nebude upozorňovat, že jsme parametr nepoužili.

Příklad
Uvažujme definiční deklraci

int f(int)
{
/* tělo funkce */
}

To je funkce s jedním parametrem typu int, který nehodláme používat.

17. Deklarace a inicializace proměnných a polí, paměťové třídy.

Deklarace a inicializace proměnných viz otázka č. 2

Pole:
Pole je strukturovaný datový typ, což znamená, že má svou vlastní vnitřní strukturu, která je tvořena položkami určitého datového typu. K těm lze pak přistupovat odděleně.

Definice proměnné typu pole:
Proměnná typu pole se definuje podobně jako proměnná jednoduchého typu. Rozdílem je, že při definici pole se za jménem identifikátoru proměnné ještě uvádějí hranaté závorky, ve kterých určíme počet prvků pole.

bázový_typ identifikátor[počet_prvků];

Bázový_typ neurčuje typ proměnné, ale typ položek pole. Že jde o pole pozná překladač právě podle hranatých závorek. Počet_prvků je jakýkoliv konstantní výraz, tedy takový, který lze vyhodnotit již při překladu. Podívejme se na konkrétní příklad definice pole:

short int moje_pole[10];

Nadefinovali jsme proměnnou typu pole, která obsahuje 10 položek typu short int. Znamená to tedy, že se pro naši proměnnou vyhradilo 20 bytů (10 * sizeof(short int)). Tato paměť tvoří jeden souvislý blok. V případě, že je takto nadefinovaná proměnná statická (static , existuje po celou dobu vykonávání programu), jsou všechny položky pole inicializovány nulovou hodnotou. Naopak, v případě že jde o lokální automatickou proměnnou (auto , vzniká až při běhu programu a existuje pouze omezenou dobu), nejsou jednotlivé prvky pole inicializovány.
Prvky pole lze inicializovat již při definici.

Příklad:

short int moje_pole[5]={1, 0, 443, -46, 987};

V případě, že počet inicializačních výrazů je vyšší než počet položek pole, bude se při překladu hlásit chyba. Pokud je počet inicializátorů menší, chyba se nehlásí a zbylé položky jsou inicializovány buď nulovou hodnotou, nebo nejsou inicializovány vůbec.

Pokud při definici zároveň inicializujeme prvky pole, nemusíme specifikovat velikost pole, ale stačí když uvedeme prázdné závorky. Překladač sám určí velikost pole podle počtu inicializačních výrazů.

Příklad:

short int moje_pole[]={1, 0, 443, -46, 987};

Přístup k prvkům pole
Přístup k prvkům pole se provádí pomocí indexace. V jazyce C++ jsou všechny proměnné typu pole indexovány od nuly. To znamená, že první položka má vždy index 0, což není možné nijak ovlivnit.

moje_pole[0]=250;

Tímto zápisem jsme do první položky pole zapsali hodnotu 250.
S poli můžou pracovat i operátory ==, != a další, nicméně význam použití těchto operátorů je, v souvislosti s poli, jiný.

Příklad:

int moje_pole[4]={15, 35, 66, 1000};
int dalsi_pole[4]={15, 35, 66, 1000};
if (moje_pole == další_pole) {...}

I když by se mohlo zdát, že se porovnají jednotlivé prvky pole, není tomu tak. Ve skutečnosti se provede pouze porovnání ukazatelů na první prvek pole. Tyto ukazatele ale mohou být stejné pouze v případě, že půjde o identická pole a tedy blok v příkazu if v našem příkladu se neprovede nikdy, bez ohledu na položky pole.

Procházení pole
Při procházení položek pole je nutné dát si pozor na to, abychom nikdy omylem nepřekročili hranice pole. Jazyk C++ zásadně nekontroluje meze polí, a tak je bez problémů možné číst i zapisovat do paměti, která nám již nepatří. Protože se indexuje od nuly, poslední prvek pole o n položkách tedy bude mít index n-1.

Příklad:

int moje_pole[20], i;
.
.
.
for (i=0; i<20; i++) moje_pole[i]=0;

Paměťové třídy:
Modifikátory paměťové třídy
Kromě identifikátoru a datového typu jsou proměnné určeny ještě paměťovou třídou, které náleží. Paměťová třída určuje kde bude proměnná v paměti uložena, jakou bude mít viditelnost a jakou životnost.

Modifikátor auto:
Modifikátor paměťové třídy auto je implicitně používán pro všechny lokální proměnné, jejichž paměťová třída nebyla určena jiným modifikátorem. Těmto proměnným se také často říká automatické, protože paměť se pro ně alokuje automaticky až při vstupu do bloku, ve kterém jsou definovány. Po opuštění bloku je tato paměť zase uvolněna. V případě návratu do stejného bloku je místo pro proměnnou alokováno znovu, neexistuje ale žádná záruka, že to bude stejné místo jako při poslední alokaci. Stejně tak neexistuje vůbec žádná záruka, že hodnota proměnné zůstane zachována. Automatické proměnné nejsou nikdy implicitně inicializovány nulovou hodnotou. Inicializovat je musíme explicitně, například při definici proměnné. Výhodou je, že k této inicializaci není potřeba použít konstantní výraz, ale je možné inicializovat i hodnotami jiných proměnných, nebo návratovou hodnotou funkce. Automatická proměnná je viditelná jen v rámci bloku, ve kterém je definována. Existuje–li vedle této automatické proměnné i nějaká globální proměnná se stejným identifikátorem, je zastíněna automatickou proměnnou.

Příklad:

int i=10; //Globální proměnná i
auto int j; //Způsobí chybu

void fce()
{ auto int i; // Lokální proměnná i(může být napsána i bez auto)

i=20;
}

void main()
{
fce();
printf("%d", i);
return 0;
}

V uvedeném příkladu jsou definovány dvě proměnné i. První z nich je globální a druhá, definovaná uvnitř funkce fce(), je automatická lokální proměnná, která ,v oblasti své viditelnosti (uvnitř funkce), zastiňuje globální proměnnou i. Jak bude vidět z kontrolního výstupu funkce printf(), ve funkci fce() jsme skutečně pracovali s lokální proměnnou i. Definice proměnné j s modifikátorem auto vyvolá chybu, protože globální proměnné nemohou být automatické.

Modifikátor static:
Viditelnost statické lokální proměnné je shodná s viditelností automatických proměnných, tedy je viditelná jen v bloku, kde je definována. Životnost statické proměnné je ale po celou dobu vykonávání programu. Paměť pro takovou proměnnou je alokována při startu programu a uvolněna je až po jeho skončení. Z toho vyplývá, že, máme-li statickou proměnnou a opustíme blok, ve kterém byla definována, tato proměnná nezaniká, pouze již není viditelná. I tak ale lze s touto proměnnou pracovat např. pomocí ukazatele. Dojde-li k návratu do bloku s touto proměnnou, můžeme s ní znovu pracovat. Statické lokální proměnné se při definici dají inicializovat jen konstantním výrazem. Tato inicializace se ale provede jen při prvním vstupu do bloku, kde je proměnná definována. Není-li statická lokální proměnná inicializována explicitně, provede se implicitní inicializace nulovou hodnotou (opět jen při prvním vstupu do bloku).
Příklad statické lokální proměnné je zjištění kolikrát byla nějaká funkce volána:

int fce()
{ static int i=0;
return ++i;
}

void main()
{ int j;
for (j=0; j<10; j++) printf("%d ", fce());
return 0;
}

Proměnná i je při prvním zavolání funkce fce() inicializována nulovou hodnotou. Při dalším spuštění funkce se již inicializace neprovede a v proměnné i zůstává hodnota nastavená v předchozím volání funkce.

Modifikátor register:
Modifikátor register patří mezi modifikátory ovlivňující optimalizaci kódu programu. Pro zvýšení efektivnosti je totiž možné uložit některé proměnné přímo do registrů procesoru, a tak umožnit rychlejší přístup k těmto proměnným. Je ale nutné říct, že klíčové slovo register slouží spíše jen jako doporučení kompilátoru, že proměnná se hodí k uložení do registru. Jestli se tak opravdu stane, není zaručeno, už třeba kvůli omezenému počtu registrů. Na registrové proměnné také nelze aplikovat operátor reference &, a to ať už proměnná skutečně je umístěna v registru, nebo není. Životnost a viditelnost registrových proměnných je stejná jako u proměnných automatických. Modifikátor register lze aplikovat pouze na lokální proměnné, a tedy nelze definovat globální proměnnou s modifikátorem register.

Modifikátor volatile:
Druhým z modifikátorů, které ovlivňují optimalizaci, je modifikátor volatile. Pokud tímto klíčovým slovem označíme nějakou proměnnou, kompilátor ji nebude optimalizovat.

Modifikátor const:
Modifikátorem const vlastně neurčujeme paměťovou třídu, ale definujeme pomocí něho tzv. konstantní proměnné. Hodnoty takovýchto proměnných pak v programu nemůžeme přímo měnit (např. přiřazením). Jediným normálním způsobem, jak se dá hodnota konstantní proměnné nastavit, je inicializace při její definici. Pomocí ukazatelů však lze změnit hodnotu konstantní proměnné i z jiných míst programu.

Konstantní proměnné lze použít např. pro inicializaci globálních proměnných, pro definování velikostí polí, apod. jen v C++. Modifikátor const lze použít i v kombinaci s jinými modifikátory.

Příklad:

const int i=20;
int *p=&i;

i=30; //chyba, nelze provést
printf("%d\n", i);
*p=40; //nepřímá změna hodnoty konstantní proměnné
printf("%d\n", i);

16. Vytváření dynamických datových struktur pomocí struktur.

Viz otázka č. 24

15. Struktury, jejich deklarace, složky, operátor přímé a nepřímé kvalifikace.

Struktura, deklarace struktury:
Typ struktura patří mezi tzv. složené datové typy. Struktura je vlastně jakýsi obal, který sdružuje více položek různých typů do jednoho celku.

Příklad:

struct { char jmeno[200];
unsigned char vek;
float vyska;
};

Klíčovým slovem struct uvozujeme vlastní deklaraci těla struktury. To je tvořeno dvojicí složených závorek, mezi kterými jsou deklarovány jednotlivé položky struktury tak, jako bychom definovali proměnné.

Nyní tedy máme nadeklarovanou strukturu, ale nemáme žádnou proměnnou, jejímž typem by tato struktura byla. Chceme-li tedy nějakou takovou proměnnou definovat, uděláme to stejně jako u ostatních datových typů - zápisem, kdy před identifikátor proměnné uvedeme její typ.

Příklad:

struct { char jmeno[200];
unsigned char vek;
float vyska;
} clovek; //Přibyla definice proměnné člověk

Pokud bychom chtěli definovat více proměnných, jednoduše uvedeme více identifikátorů navzájem oddělených čárkou. Jestliže ale budeme chtít vytvořit proměnnou stejného typu na jiném místě programu, musíme samotnou strukturu označit identifikátorem, který uvedeme mezi klíčovým slovem struct a vlastním tělem struktury.

Příklad:

struct osoba { char jmeno[200];
unsigned char vek;
float vyska;
};

Nyní ale, díky označení struktury jejím identifikátorem, můžeme proměnnou „clovek“ definovat i na jiných místech programu takto:

osoba clovek;

Složky:
Snad jen, že to můžou být jakékoliv typy proměnných a dokonce i jiná struktura.

Operátor přímé a nepřímé kvalifikace:
Operátory přímé a nepřímé kvalifikace jsou operátory pomocí nichž pracujeme se strukturou, neboli pracujeme s jednotlivými položkami struktury. Jsou to operátory tečka . a šipka ->. Operátor tečka je tzv. operátor přímé kvalifikace. Pomocí něj se přistupuje k jednotlivým položkám struktury. Například pokud bychom chtěli položce ulice v proměnné bydliste přiřadit řetězec Jecna uděláme to takto: bydliste.ulice = “Jecna”;. Operátor šipka je tzv. operátor nepřímé kvalifikace. Použijeme ho tehdy když chceme k položce ve struktuře přistupovat pomocí ukazatele. Např. pokud chceme přiřadit položce mesto proměnné bydliste řetězec Praha pomocí ukazatele *pbydliste, uděláme to takto:

pbydliste = &bydliste;
pbydliste->mesto = “Praha”;

Nejprve musíme ukazateli pbydliste přiřadit adresu proměnné bydliste a poté pomocí operátoru šipky naplnit její položku. Použití operátoru nepřímé kvalifikace nahrazuje toto:

(*pbydliste).mesto

14. Dynamické proměnné.

Dynamická alokace paměti:
V programu můžeme za běhu (dynamicky) přidělovat oblasti paměti různým proměnným. Přidělená paměť má určenou délku a není pojmenovaná,neboli nemá žádný identifikátor.Přistupuje se do ní pomocí ukazatelů (pointerů).

Dynamické přidělování používáme tehdy,pokud nevíme předem,kolik paměti na data využijeme (např. databáze),nebo budeme za běhu programu vytvářet proměnnou,jejíž velikost bude záviset na hodnotě jiné proměnné.(Pokud bychom zadali pevnou velikost,dynamická alokace ztrácí smysl a je lepší použít typ pole).

K proměnné v takovém případě přistupujeme přes ukazatel. Deklarace ukazatele je stejná, jako normální deklarace ukazatele a pro alokaci paměti se používá operátor new . V takovém případě je po ukončení používání potřeba alokovanou proměnnou zrušit operátorem delete . Obdoba new a delete je v C malloc a free . Není správné ale používat funkce malloc a free , protože jen alokují paměť. Narozdíl od toho new navíc zavolá konstruktor a delete zavolá destruktor (pokud pracujeme se třídou).

Příklad se strukturou, třídy jsou až dál:

struct CD{
int cislo;
};

void main()
{
CD *ukazatel;
ukazatel = new CD;
ukazatel->cislo = 10;
delete ukazatel;
}

13. Ukazatele, aritmetické operace s ukazateli.

Ukazatele
Ukazatel je v podstatě proměnná jako každá jiná. Liší se jen způsob, jak se s ní pracuje. Klasická proměnná představuje určitý kus paměti, ve které je uložena hodnota této proměnné. To platí i pro pointery, jenže v jejich případě je touto hodnotou adresa nějakého místa v paměti. Říkáme pak, že pointer na místo určené touto adresou ukazuje.

Bázový typ ukazatele
Bázový typ ukazatele určuje, jak se bude interpretovat místo v paměti, kam tento pointer ukazuje. Například, je-li bázovým typem ukazatele typ int, znamená to, že obsah paměti, kam pointer ukazuje, se bude interpretovat jako datový objekt typu int. Bázový typ ukazatele určujeme při jeho definici, je ale možné určit ho až za běhu programu.

Definice proměnné typu ukazatel
bázový_typ *identifikátor;

Ukazatele definujeme podobně jako jiné proměnné. Rozdílem je, že před identifikátorem musíme uvést ještě znak ‘*‘, kterým dáme překladači vědět, že definujeme pointer. Není potřeba navzájem od sebe oddělovat definice obyčejné proměnné a pointeru, jejichž bázový typy je stejný.

int i, *p2;

S pamětí, kam tyto pointery ukazují, lze pracovat, tedy číst a zapisovat do ní. V takovém případě můžeme lehce přepsat paměť používanou jinými programy nebo systémem samotným. To má většinou za následek zhroucení programu. Proto musíme ukazatel inicializovat nějakou smysluplnou hodnotou ještě před jeho prvním použitím.

Referenční operátor &
Abychom mohli do ukazatelové proměnné přiřadit hodnotu (nějakou adresu), musíme ji nejprve někde získat. K tomu nám může posloužit referenční operátor '&'. Jeho zapsáním před identifikátor proměnné získáme adresu paměti, kde je tato proměnná uložena. Tuto adresu pak můžeme přiřadit nějaké ukazatelové proměnné.

Příklad:
char *p, c;

c = 12;
p = &c;
// p nyní ukazuje na proměnnou c

Operátor dereference *
Pomocí pointeru můžeme přistupovat k datovému objektu, na který ukazuje. K tomu nám poslouží operátor dereference '*'. Použitím tohoto operátoru na ukazatel získáme objekt, na který ukazatel odkazuje, přičemž typ takto získaného objektu bude shodný s bázovým typem dereferovaného ukazatele.

Příklad:
int i=10, *p;

p = &i;
*p = 20; //zápis ekvivalentní i=20;
printf("%d", i); //Vypíše se 20

Ukazatele NULL
Jedním z případů, kdy ukazatele inicializujeme konstantní hodnotou je přiřazení symbolické konstanty NULL. Má-li pointer přiřazenu hodnotu NULL, znamená to, že nikam neukazuje.

Inicializace při definici
Pointery lze, stejně jako jiné proměnné, inicializovat již při definici, ale namísto konstantní hodnoty se inicializují adresou některé dříve nadefinované proměnné, nebo hodnotou jiného pointeru. Případně konstantou NULL.

Ukazatel typu void (generický ukazatel)
Pokud nadefinujeme ukazatel, jehož bázovým typem bude void (tzv. generický ukazatel), dáváme tím najevo, že bázový typ pointeru není předem určen. Do takového pointeru lze po přetypování přiřadit hodnotu ukazatele libovolného bázového typu.
Chceme-li dereferovat ukazatel typu void, musíme ho vždy nejdřív přetypovat!

int i;
void *p;

(int*)p=&i;
*(int*)p=36;
printf ("%d", i);

Aritmetické operace s ukazateli viz předchozí otázka č. 13

12. Základní typy dat, celočíselné typy, znakové typy, racionální typy, prázdný typ.

Všechno viz otázka 2

Prázdný typ void:
Používá se u funkcí, pokud nepotřebujeme, aby funkce vracela nějakou hodnotu. Pokud funkci napíšeme bez návratového typu, překladač si za návratový typ implicitně(automaticky) doplní typ void.
Další využití typu void je u ukazatelů(viz otázka č. 14 až na konci).


12. Odvozené typy: ukazatele a pole.

Ukazatele a pole:
Pozn.: Myslím, že aritmetické operace patří spíš do této otázky.
V jazyce C++ existuje úzká souvislost mezi datovým typem pole a typem ukazatel. Díky ní se můžeme na výraz typu pole dívat jako na ukazatel, a také s ním podle toho pracovat.
Aritmetické operace s ukazateli:
Součet ukazatele a celého čísla
Řekněme, že máme ukazatel bázového typu int ukazující někam do paměti. Pokud k tomuto ukazateli přičteme nějaké celé číslo n, vyhodnocením tohoto výrazu bude adresa paměti o n intových hodnot dál, než kam ukazoval původní pointer.

Příklad
short int a[4]={15,134,29,104}; //Definování s inicializací pole
short int *p;

p=&a[0]; /*ukazatel p bude odkazovat na začátek pole. Zápis lze zkrátit na p = a, protože a je vlastně taky ukazatel.*/

Následující tabulka ukazuje, kam se vlastně s pointrem p dostáváme, přičítáme-li k němu postupně čísla 1, 2 a 3.
Bajt 0 Bajt 1 / Bajt 2 Bajt 3 / Bajt 4 Bajt 5 / Bajt 6 Bajt 7
a[0]-----------a[1]-----------a[2]---------a[3]
*p-------------*(p+1)--------*(p+2)------*(p+3)
15--------------134------------29----------104

Druhý řádek tabulky ukazuje klasický způsob, jak se dostat k jednotlivým prvkům pole – indexaci. Třetí řádek představuje ekvivalentní zápis pomocí dereferovaného ukazatele. Jak vidíte, přičtení čísla k pointeru neznamená jeho posunutí o jeden byte, ale o celou velikost jeho bázového typu. V případě námi použitého typu short int to jsou dva byty. Přičtu-li tedy k našemu pointeru p číslo jedna, výsledkem operace bude pointer ukazující v paměti o dva byty, neboli o jeden short int, dál. Prakticky tedy takto získaný pointer bude ukazovat na druhý prvek pole a. Dereferencí pak tento prvek získáme stejně, jako kdybychom použili indexaci a[1].
Inkrementujeme-li ukazatel, jednoduše měníme místo, kam ukazuje, a to stejně, jako kdybychom k ukazateli přičetli číslo 1.

Odečtení celého čísla od ukazatele
Situace s odčítáním ukazatele a celého čísla je analogická operaci sčítání, jenom se posunujeme v paměti opačným směrem. Měli bychom si ale být jisti, že paměť, ke které takto přistoupíme, nám skutečně patří. To ale platí pro jakoukoliv práci s pointery a vlastně i s poli.
Stejně jako můžeme na ukazatel aplikovat operátor inkrementace, můžeme použít i operátor dekrementace.

Rozdíl dvou ukazatelů
V jazyce C++ je definován i rozdíl dvou ukazatelů stejného bázového typu. Tato operace má smysl hlavně v případě, že oba pointery ukazují do stejného pole. Pak je výsledkem takové operace celočíselná hodnota, která představuje vzdálenost mezi prvky pole, na které pointery ukazují. Tato vzdálenost je měřena v počtu prvků pole.

Zaměnitelnost ukazatele a pole
Aritmetické operace s ukazateli nalézají asi největší uplatnění při práci s poli. Na pole se totiž můžeme dívat jako na pointer ukazující na první prvek tohoto pole. Proto můžeme všechny operace, které jsme prováděli s pointery, dělat i s poli. Můžeme například dereferovat proměnnou typu pole, čímž získáme první prvek tohoto pole(viz předchozí příklad).
Pole ale nelze považovat přímo za ukazatel. Bez problémů funguje přiřazení p=a, ale zápis a=p není možný. Stejný problém by nastal také při použití některého z operátorů inkrementace či dekrementace na proměnnou typu pole.

11. Volání funkce.

Volání funkce:
identifikátor_funkce (seznam parametrů);

Jednotlivé výrazy v seznamu parametrů jsou odděleny čárkou. Samotné parametry jsou pak výrazy, které jsou před předáním řízení funkci vyhodnoceny a jejich výsledné hodnoty jsou funkci předány. I když funkce nemá žádné skutečné parametry, závorky je nutné vždy uvést! Samotné volání funkce je chápáno jako výraz a to určuje i místo jeho použití.
Např. operand některého operátoru(viz operátory tj. +,-,/,*,==,&&,||,atd.), ve výrazovém příkazu:

if(secti(4,5) == 9){ //výrazový příkaz(aspoň doufám)
vysledek = secti(3,2) + 5; //operand operátoru +
}

Jako skutečný parametr jiné nebo stejné funkce:
vysledek = secti(secti(5,8),3);

Při volání funkce je provedeno několik kroků. Nejdříve je uložena adresa instrukce, na kterou se skočí po skončení provádění funkce. Pak se alokuje paměť pro formální parametry funkce(viz seznam definicí formálních parametrů z Definice funkce), do kterých se následně zkopírují hodnoty parametrů skutečných.

Formální parametry jsou vlastně lokální proměnné, a tedy existují pouze po dobu vykonávání funkce a jsou viditelné pouze z těla funkce. Když vykonávání funkce skončí, je paměť vyhrazená pro tyto proměnné uvolněna a jako výsledek volání funkce se použije její návratová hodnota (většinou tedy hodnota výrazu za příkazem return). Uvedený způsob předávání parametrů se nazývá předávání hodnotou a znamená, že hodnoty skutečných parametrů jsou pouze zkopírovány do formálních parametrů. Ve funkci se pak pracuje pouze s těmito kopiemi. Předávání parametrů odkazem je realizováno pomocí ukazatelů.viz Předávání
parametrů funkci!