Deklarácie

Meno Name

Meno je hocijaký objekt, funkcia, enumerátor... A nové meno sa do programu zavádza deklaráciou. Meno je viditeľné a platné iba v určitej oblasti - rozsahu platnosti - scope. Každé meno má svoj typ, podľa čoho viem na čo je meno dobré (čo sa s ním dá robiť, a na čo je dobré). Jedno meno môže byť použité vo viacerých blokoch programu a nemusí (ale môže) reprezentovať tú istú entitu. Teda raz to môže byť funkcia, raz premenná, inokedy trieda...
#include "stdio.h"

class a {
        public:
                int y;
        }objekt;

void a(int x){printf("\n%d",x);}

void main(){

int a;
a=objekt.y=10;
::a(a);

char pause=getchar();

			}
Najprv je a pomenovanie pre nový typ triedy. Hneď na to je a menom funkcie. A priamo v maine je a intom. Tieto ačka sa ale nebijú. Meno triedy je úplne nanič, lebo som objekt triedy definoval hneď za ňou. Meno funkcie by už kolidovalo s premennou a, a tak cez ::a pristupujem k funkcii a a obyčajné a je premenná typu int.
*stdio.h je preto v úvodzovkách, lebo je zložitejšie dávať < > znaky do html... celkovo je kód nepodstatný, podstatné je, že sú tam 3 ačka a každé je na dačo iné.

Deklarácie vs. definície Declarations and Definitions

Deklarácia zavádza do programu meno. Ale zatiaľ nevyhradzuje žiadnu pamäť. Definícia je deklarácia, ktorá aj vyhradí pamäť.
Deklarácií, ktoré nie sú definíciami veľmi veľa nie je...
void funkcia(void);			// 1
extern int a;				// 2
typedef int* NovyTyp;			// 3
  • 1.funkcia, ktorá nemá telo, teda len jej prototyp (predpokladá sa že telo niekde bude... ale nemusí ak ju nikde nebudem volať :)
  • 2.externá premenná bez inicializácie (predpokladá sa, že niekde bude definovaná...)
  • 3.definovanie nového typu cez typedef
Definícia je potom skoro všetko ostatné...
int a=10;				// 1
float b;				// 2
void funkcia(void){printf("Ahoj");}	// 3
extern int x=22;			// 4
  • 1.keď som dal ačku 10 tak zjavne mu treba na to pamäť
  • 2.bčku som síce nepriradil nič, ale aj tak mu treba pamať na float mať...
  • 3.funkcia s telom - no telo už zaberá pamäť
  • 4.toto je síce extern, ale keďže je tam aj priradenie tak je to hneď definícia a teda v iných súboroch už definícia nesmie byť. (toto je to isté ako keby tam to extern ani nebolo)
Všetko čo je deklarované musí byť niekde v programe aj definované (funkcia, ktorú nikde nevolám nemusí, však na čo aj keď ju nikde netreba...)

Rozsah platnosti Scope

Scope určuje kde všade je meno použiteľné Rozsahy platnosti sú lokálny local, funkčný function, súborový file a triedny class.
#include "stdio.h"

void funkcia(void){
        int a=1;
        printf("\nLocal a: %d",a);
        }

void main(){
int a=10;
printf("\nLocal a: %d",a);
      {
        int a=5;
        printf("\nLocal a: %d",a);
        funkcia();
        printf("\nLocal a: %d",a);
      }
printf("\nLocal a: %d",a);

char pause=getchar();
            }
ačko má lokálny rozsah platnosti a to hneď viackrát. v rámci funkcie, v rámci mainu a v rámci bloku. Prekladaču je to totiž jedno on vždy keď narazí na riadok int a, tak si zadefinuje nejakú premennú, ktorú používa až do konca bloku, teda až kým nenájde }. Povedané inak lokálna premenná platí iba medzi dvoma {}
void main(){
goto skok;
        int a;printf("%d",a);
skok:
            }
Funkčný rozsah platnosti je to isté čo blokový, ale má drobné rozšírenie. V lokálnom bolo treba pred použitím mena najprv meno definovať. Teda najprv musí byť niekde v bloku int a a až potom môžem do ačka dačo vypísať, alebo niečo čítať.
Meno s funkčným rozsahom platnosti sa dá použiť už aj skôr ako bolo definované. Teda najprv urobím goto skok: aj keď ani neviem kde to ten skok je... Ale je to v pohode, lebo kdesi to návestie bude... Keby som ho zabudol definovať, tak prekladač mi pripomenie. Toto funguje iba na typ návestie label. Nič iné nemôže mať funkčný rozsah platnosti. To je aj celkom pochopiteľné. Ťažko vypíšem napr. premennú, ktorá ešte len bude definovaná, a naopak goto by nemalo až taký zmysel, keby sa s ním dalo skákať len dozadu...
#include "stdio.h"

int a=5;

void funkcia(){printf("\n%d",a);}


void main(){
 printf("\n%d",a);   //vypisem (suborove) a
 funkcia();          //vypisem suborove a
 a=2;                //nastavim nove suborove a
 printf("\n%d",a);   //vypisem (suborove) a
 funkcia();          //vypisem suborove a
 int a=7;            //vyrobim nove lokalne a, suborove a je prekryte
 printf("\n%d",a);   //vypisem (uz lokalne) a
 funkcia();          //vypisem suborove a
 printf("\n%d",::a); //vypisem suborove a ::ma pusti k prekrytemu a

 char pause=getchar();
             }
Meno so súborovým rozsahom platnosti platí v celom súbore od okamihu ako bolo definované. V tomto príklade som ho definoval hneď na začiatku aby ho všetky funkcie a bloky v programe poznali. a je známe teda vo funkcii aj v maine. Ak si však nešikovne (možno zámerne) na nejakom mieste ačko prekryjem novým a (zadefinujem niekde znova meno a, teraz už lokálne) tak smola, už poznám v bloku len toto nové lokálne a. Ak by som sa ale predsa len potreboval dostať k súborovému a, tak je na to operátor rozšírenia platnosti ::
 class Tree {
public:
   typedef Tree * PTREE;
   PTREE  Left;
   PTREE  Right;
   void  *vData;
};

//PTREE pTree;	// chyba
Meno s triednym rozsahom platnosti je známe iba v rámci triedy. Ak bolo definované rovnaké meno predtým inak tak v triede ho novo definované meno prekryje...

Linkovanie Linkage

Toto určuje v podstate to, či je meno známe aj v iných súboroch. Linkovanie sa týka iba mien so súborovým rozsahom platnosti, teda globálnych. (Ono by bolo aj celkom somarina chcieť aby sme nejakú premennú, ktorá existuje len povedzme v rámci bloku a ani len v maine ju nevidieť, videli v inom súbore...)
Linkovanie môže byť interné a externé. Podľa toho či meno je alebo nie je známe aj v iných súboroch. Takže ak má (globálne) meno interné linkovanie, tak môžem toto meno v pohode použiť v inom súbore (ako dačo iné samozrejme), definovať si ho nanovo a pomenovať si s ním niečo. Dokonca ani nemusí pomenovávať rovnakú entitu. V jednom súbore môže byť (globálne) ačko premenná typu int, a v inom súbore môže byť (kľudne aj lokálne :) ačko napríklad trieda.
Ale ak má (globálne) meno externé linkovanie (a to defaultne má...) tak bude problém ak si to isté meno budem chcieť definovať v rôznych súboroch. Dobrá vlastnosť však je, že to bude vo všetkých súboroch jedna a tá istá premenná, a teda čo je externé to je známe vo všetkých súboroch (ak to zadeklarujem samozrejme...) Ak má raz nejaké (globálne) meno externé linkovanie (defaultne má...) tak vo všetkých ostatných súboroch, kde ho plánujem použiť ho treba znova deklarovať (ale už nie definovať - definované už bolo...)

Ukladacia trieda Storage Class

Pomenované objekty majú svoju ukladaciu triedu storage class (Preto hovoríme "pomenované" lebo ak pristupujem k premennej iba cez smerník, lebo bola dynamicky alokovaná napr. tak už nie je pomenovaná nijak, a jedine cez ten smerník sa k nej dostanem...)
Storage class definuje ako sú objekty uložené v pamäti. Storage class môže byť automatická automatic a statická static. Automatické objekty existujú v rámci bloku a pamäť majú pridelenú v zásobníku. Keď skončí blok, skončí aj premenná. Nepríjemné je, že ak do automatickej premennej nič nedám tak tam bude nejaký chaos z pamäti.
...
for(int i=0;i!=1000;i++){
int a;}

...
Tiež nepríjemné (aspoň to teda nepríjemne znie :) Je, že ak volám nejaký blok v ktorom je napísané int a, a volám takýto blok (napr. v cykle) 1000x, tak premenná a 1000x vznikne a 1000x zanikne... Pri obyčajnom malom int to asi až tak nevadí, ale ak budem robiť s väčšími objektami tak už to znie naozaj nepríjemne... Všetky lokálne premenné sú automatické. Vznikajú pri definícii a keď skončí blok zanikajú. Ešte aj premenné v rámci cyklov sú automatické. V predošlom príklade teda aj to v oblých zátvorkách je ako keby blok, a teda aj premenná i je automatická. Keďže lokálne premenné "radi zanikajú" treba dávať pozor aby som napr. pomocou goto nepreskočil miesto kde premenná vzniká...
Statické existujú stále. Od kedy sa spustí program, až do kým neskončí. (teda od miesta kde sú definované, pochopiteľne...) Sami sa implicitne inicializujú na nulu svojho typu (int na 0, float na 0.0, char na prázdny znak, pole prvkov na nuly...) a teda neostáva v nich chaos z pamäti... Všetky globálne premenné sú statické. (keby boli automatické tak by to bolo čudné - však boli definované mimo akéhokoľvek bloku...) Ak chcem aby bola lokálna premenná statická, treba jej to nastaviť pomocou príkazu static (lebo defaultne by bola automatická) Nastaviť lokálnej premennej statickú ukladaciu triedu má ten význam, že si bude držať hodnotu aj keď program opustí blok...
#include "stdio.h"

int counter(){
        static int pocitadlo=0;
        return ++pocitadlo;
              }

int main(){
        for (int i=0;i<20;i++)printf("%d ",counter());
 char pause=getchar();
 return 0;
           }
Keďže je pocitadlo statické, tak si pamätá hodnotu aj mimo funkcie, a tá premenná zanikne až keď skončí program.

Špecifikátory ukladacej triedy Storage Class Specifiers

Existujú špecifikátory typu, ukladacej triedy, funkčné špecifikátory a špecifikátor typedef. Čiže keď mám niekde v programe deklaráciu, tak naľavo sú špecifikátory a napravo deklarátory.
static int x;
int main(){

        auto int a;
        register int b;
        extern int c;
        static int d;

return 0;
           }
auto značí, že premenná a bude automatická. Keďže však všetky lokálne premenné sú defaultne automatické, tak takáto definícia nemá veľký význam...
register značí, že premenná b je registrová. Toto znamená, že by sme boli radi, keby ju prekladač dal priamo do registra, alebo aspoň do cache. Toto je vhodné nastaviť premenným, ktoré sa veľmi často používajú (prístup do registrov procesora je veľmi veľmi rýchly). Ak budem však chcieť nastaviť register objektu u ktorého sa to nedá (napríklad si niekde napíšem &b a teda si pýtam adresu b - no zjavne nezmysel, ak je to v registri tak to nemá adresu... alebo ak je b veľmi veľký objekt a nevôjde do registra...) tak sa kompilátor na celý register vykašle, a uloží si b po svojom.
extern vraví, že objekt c bude externý, a teda známy vo všetkých súboroch. Objekt c je však externý aj defaultne. Táto deklarácia (nie definícia) spôsobuje, že vravím, že chcem používať externe známe c, ktoré je definované niekde inde (pravdepodobne v inom súbore). Riadok extern int c; vraví, že chcem začať od tej chvíle používať premennú c, ktorá bola definovaná niekde inde (v inom súbore...). Bolo by asi rozumné napísať extern int c; so súborovým rozsahom platnosti - aby bola premenná známa v celom súbore, ale ide to aj takto (v rámci bloku). Ak už raz v tomto súbore napíšem extern int c; Tak nikde inde v tomto bloku nemôžem napísať int c; Jednalo by sa totiž o viacnásobnú deklaráciu.
static má iný význam pri globálnej (súborový rozsah platnosti) a iný pri lokálnej premennej. U oboch nastavuje interné linkovanie. U lokálnej premennej to až taký význam nemá. Tie nemajú externé linkovanie (je ich vidieť iba v rámci bloku, teda nie sú viditeľné ani len v rámci súboru, nie to ešte mimo neho). Globálne (súborový rozsah platnosti) premenné majú však defaultne externé linkovanie. A teda ak chcem aby ich nebolo vidieť mimo súboru (snažím sa ich chrániť napríklad) musím ich nastaviť ako static. Ich ukladacia trieda sa nijak nezmení (tá je pri globálnych premenných stále statická).
Pri lokálnych premenných sa však prejavuje práve táto statická ukladacia trieda (linkovanie majú interné, s tým nič nenarobím). Teda ak pri lokálnej premennej použijem špecifikátor static, premenná nadobúda statickú ukladaciu triedu a nezanikne ak vyskočím z bloku. Keby som static neuviedol, tak by mala premenná automatickú ukladaciu triedu a zanikla by pri najbližšom konci bloku...
Povedané stručne - globálnym premenným nastavuje static interné linkovanie, lokálnym statickú ukladaciu triedu. Naopak ukladacia trieda je pri globálnych stále statická a static to nijak neovplyvní. Pri lokálnych je zase linkovanie vždy interné a static to neovplyvní.
mutable je špeciálny typ ukladacej triedy, ktorý dovoľuje modifikovať konštanty (zatiaľ neviem ako :)

Funkčné špecifikátory Function Specifiers

#include "stdio.h"

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

int main(){

printf("\n%d",max(3,6));
printf("\n%d",max(7,6));

char pause=getchar();
return 0;
           }
inline je funkčný špecifikátor, a nastavuje funkcii aby bola inline. Toto znamená, že keď niekde budem volať funkciu max(int, int); tak v skutočnosti ju nebudem volať, ale kompilátor tam skopíruje celé telo funkcie. Toto jednak zrýchli program (keďže netreba funkciu volať, a bude priamo tam kde ju treba) a jednak predĺži (keďže na každé miesto sa vkopíruje celá funkcia). Priamo z tohto vyplýva, že inline je dobré používať iba pri krátkych funkciách, ktoré sa volajú často. Ono aj prekladač v niektorých prípadoch rozhodne, že funkcia nebude inline, aj keď to napíšem. Toto sa stáva, ak chcem aby bola inline funkciou funkcia s cyklami, skokmi - jednoducho príliš dlhá funkcia. Príliš dlhé funkcie nemajú byť prečo inline, a tak to isté si myslí aj prekladač...
Druhým funkčným špecifikátorom je virtual. Toto sa týka virtuálnych funkcií a o tom si popíšem zvlášť, keď tomu budem rozumieť :)

Špecifikátory typu Type Specifiers

int a;
float b;
double c;

const int d=1;
const float e=1.0;
const double f=1.0;

volatile int g;
volatile float h;
volatile double i;

const volatile int j=1;
volatile const float k=1.0;
Základné špecifikátory typu ako int, double, float sú jasné... Každý z nich ale môže byť doplnený špecifikátorom const, alebo volatile. Príp. oboma. const vraví o tom, že premenná bude konštantná a teda sa nebude dať meniť počas behu programu. Je dôležité, že hneď pri deklarácii musí byť aj definovaná. volatile určuje, že si neželám aby bola premenná v rámci optimalizácie nejak presúvaná v pamäti. Toto je dôležité ak exstuje spôsob ako meniť túto premennú aj inak ako v programe (Napríklad zvonka nejakým vstup z vonkajšieho hardware. Ak by program uloženie premennej nejak optimalizoval a nebola by počas celého programu tam kde mala, tak by vonkajší proces zapisoval hodnoty niekde úplne inde.) Teda, čo je volatile s tým program nehýbe... const volatile je aj konštantné aj sa s tým nehýbe (a tak vonkajší proces môže vždy načítať hodnotu premennej a nemusí sa báť, že by ju nenašiel na tom mieste kde má byť. Zapisovať do nej (do konštanty) ale nemôže...
Rozdiel oproti direktíve #define je ten, že pri #define sa jednalo iba o makro. Teda čo bolo za #define sa skopírovalo do programu na miesto kde bola "konštanta" použitá. Pritom to vôbec nemusela byť ani konštanta, mohol to byť hocijaký text... Pri konštantných premenných sa už jedná o naozajstné premenné. Akurát, že nemôžu byť menené (to je dosť čudné, keď je to premenná a nedá sa meniť... :)))
#include "stdio.h"

#define sedmicka 7
#define koniec char pause=getchar();return 0;}

int main(){

int a=sedmicka;
const int b=sedmicka;
a=b;
//b=a;    //CHYBA!

koniec
sedmicka je len makro. Všade v programe kde je napísané sedmicka sa tento text nahradí znakom 7. Rovnaké je to s makrom koniec. Všade kde je v programe napísané koniec, nahradí sa to reťazcom char pause=getchar();return 0;}. b už je naozajstná premenná, má svoju adresu kde je uložená, má svoj rozsah platnosti, ukladaciu triedu... Akurát je konštantná. Ale inak je to plnohodnotný objekt. Je logické, že keď je konštantná tak ju nikde v programe nemôžem meniť. Raz ju nadefinujem, a tým to končí. Od vtedy z nej iba čítam.
#include "stdio.h"

int main(){

int a=1;
int *b;
          b=&a;  //keby som to nedal nemozem vypisat *b
                 //b aj a su odteraz na rovnakej adrese
                 //teraz je aj a aj *b 1
printf("\na=%d *b=%d",a,*b);
          *b=2;  //teraz je aj a aj *b 2
printf("\na=%d *b=%d",a,*b);
const int c=2;   //c je 2 a uz sa to nikdy nezmeni
int const d=3;   //d je 3 a uz sa to nikdy nezmeni
const int* e=&c; //*e je teraz 2, je to ok, c bola konstanta
//e=&a;e=b;      //sice nehlasi chybu ale sposobi to problem...
                 //e ocakava adresu na konstantny int
                 //ale ani a ani *b nie su konstanty...
//*e=4;          //chyba, nemozno modifikovat konstantu
int* const f=&a; //toto je konstantny smernik
                 //moze smerovat na konstantu aj nekonstantu
                 //podstatne je ze musi stale smerovat na to iste
//f=&a;f=b;      //chyba, nemozem zmenit na co smeruje...
*f=5;            //ale mozem zmenit obsah toho na co smeruje
                 //obsah toho na co smeruje nastavim na 5
                 //ale pozor jeho adresa bola adresa a
                 //tym padom aj v a bude 5
                 //*b je na tej adrese ako a... aj *b je 5
printf("\na=%d *b=%d *f=%d",a,*b,*f);
const int* const g=&d;
                 //g bude konstantny smernik na konstantu
//g=&a;g=b;      //chyba, nemozem menit kam smeruje...
//*g=6;          //chyba, nemozem menit ani obsah...
printf("\na=%d *b=%d c=%d d=%d *e=%d *f=%d *g=%d",a,*b,c,d,*e,*f,*g);

const int* h=&a; //urobme smernik na konstanu
                 //ale nastavme nech smeruje na nekonstantu
a=7;             //aj ked ma smerovat na kostantu a sa da menit...
printf("\na=%d *h=%d",a,*h);
//*h=8;          //ale aj ked je a nekonstanta, nemozno ju takto menit

char pause=getchar();
return 0;
}
Konštantné môžu byť aj smerníky. Vtedy môžem meniť obsah toho na čo ukazujú ale nemôžem meniť smerník samotný... Toto je prípad smerníka f
Takisto môžu existovať smerníky, ktoré smerujú na konštantu. Môžem meniť to na čo smerujú, ale vždy sa to bude považovať za konštantu. Toto je prípad smerníka e.
Môže existovať smerník, ktorý má smerovať na konštantu, ale ja mu priradím adresu nekonštanty (prípad h). Potom síce môžem túto nekonštantu meniť, ale nemôžem meniť *h. Smerník h totiž predpokladá, že jeho obsahom je konštanta...
Nakoniec existuje konštantný smerník na konštantu. Prípad g. S takýmto smerníkom potom nemožno robiť dokopy nič... :)
#include "stdio.h"

 void modify (int* a, int const* b){
        *a=10;
        //*b=10;
        }

int main(int argc, char* argv[])
{
int x=20;
int y=20;

modify (&x,&y);
printf("\nx=%d y=%d",x,y);

char pause=getchar();
        return 0;
}
Smerník na konštantu môže byť dobrý napríklad na to, keď nechem aby funkcia za žiadnych okolností modifikovala, to čo jej pošlem. a sa dá modifikovať ľahko, ale pri b to už nefunguje. b je skutočne len na čítanie...
#include "stdio.h"

 void modify (int &a, int const &b){
        a=10;
        //b=10;
        }

int main(int argc, char* argv[])
{
int x=20;
int y=20;

modify (x,y);
printf("\nx=%d y=%d",x,y);

char pause=getchar();
        return 0;
}
No a presne toto isté platí aj pre referencie.

Typ enum enum

#include "stdio.h"

int main(int argc, char* argv[])
{
enum farba {red,green,blue=10,magenta,cyan,yellow=12};
farba a;
a=green;
printf("\ngreen is %d",a);
a=cyan;
printf("\ncyan is %d",a);
a=yellow;
printf("\nyellow is %d",a);
a=4;
printf("\n??? is %d",a);
int b=red;
char pause=getchar();
        return 0;
}
enum je špeciálny typ kde sa jeho možné prvky vymenujú. V skutočnosti sú to len celé čísla, akurát si vytvorím aliasy. Môžem priradiť aliasom aj vlastné hodnoty. Ak uvediem niekde aj hodnotu a ďalej v zozname nie, ďalšie hodnoty sú číslované od tohto čísla. Teda green je 1, blue je 10, magenta 11, cyan 12 atď...
Je možné priradiť aj číselnú hodnotu, ktorú nereprezentuje žiaden alias, ale na čo by to bolo dobré neviem... Niektoré aliasy môžu reprezentovať tú istú hodnotu (cyan a yellow) ale opäť netuším na čo by to bolo... Alias sa dá priradiť aj int premennej, vtedy sa priradí samozrejme hodnota, ktorú alias reprezentuje.
...
enum farba {red,green,blue=10,magenta,cyan,yellow=12};
farba a;
int farba;
enum farba x;
...
Ak si názov typu náhodou prekryjem premennou s rovnakým menom, dostanem sa k nemu naspäť tak, že uvediem jeho plné meno... V tomto príklade enum farba

Typedef a pokročilé deklarácie

#include "stdio.h"
#include "typeinfo.h"

int main(int argc, char* argv[])
{
typedef int* TSmernikInt;
// TSmernikInt je ukazovatel na int
typedef void(*TFunction)(TSmernikInt);
// TFunction je ukazovatel na funkciu ktorej vstupom je TSmernikInt
// teda smernik na fukciu do ktorej vstupuje smernik na int
// a vystupuje void
typedef TFunction (*TArrayPointer)[];
// TArrayPointer je ukazovatel na pole hore spominanych funkcii
typedef const char (*THlavnyTyp)(TArrayPointer);

printf("\n TSmernikInt je: %s",typeid(TSmernikInt).name());
printf("\n TFunction je: %s",typeid(TFunction).name());
printf("\n TArrayPointer je: %s",typeid(TArrayPointer).name());
printf("\n THlavnyTyp je: %s",typeid(THlavnyTyp).name());

char pause=getchar();
        return 0;
}
typedef je špecifikátor pomocou, ktorého si môžem navoliť nový typ. Vytvára vlastne alias na nejaký iný typ. Dá sa to dobre využiť pri definícii komplikovaných typov.
THlavnyTyp teda bude ukazovateľ na funkciu, ktorá vracia konštantný char, a vstupom do nej je smerník na pole smerníkov na funkcie, z ktorých každá vracia void, a jej vstupným parametrom je smerník na int.
Aspoň teda myslím... :)

typedef je výborná pomôcka, ale dá sa to spraviť celé aj ručne.
Najprv si spravme ukazovateľ na niečo.
... *HlavnyTyp
Čiže HlavnyTyp je ukazovateľom na niečo. Fajn. Má to byť ukazovateľ na funkciu...
... (*HlavnyTyp)(...)
Ok, už ukazuje na funkciu. Teraz o tej funkcii vieme že vracia konštantný char...
const char (*HlavnyTyp)(...)
A vstupom tej funkcie je smerník na niečo...
const char (*HlavnyTyp)(... *)
Je to smerník na pole čohosi...
const char (*HlavnyTyp)(... (*)[])
...pole smerníkov na čosi...
const char (*HlavnyTyp)(... *(*)[])
...smerníkov na funkcie...
const char (*HlavnyTyp)(...(*(*)[])(...))
...z ktorých každá vracia void...
const char (*HlavnyTyp)(void(*(*)[])(...))
...a jej vstupom je smerník na čosi...
const char (*HlavnyTyp)(void(*(*)[])(...*))
...konkrétne smerník na int
const char (*HlavnyTyp)(void(*(*)[])(int*))
Hotovo, keď si dám vypísať typeid takejto premennej skutočne zodpovedá tomu čo som spravil predtým cez typedef...

Toto isté treba vedieť urobiť aj naspäť :) Takže to skúsme...
const char (*HlavnyTyp)(void(*(*)[])(int*))
... *HlavnyTyp
Z tohto hneď vidím, že HlavnyTyp je smerník.
... (*HlavnyTyp)(...)
Z takejto štruktúry vidím, že je to zjavne smerník na funkciu.
const char (*HlavnyTyp)(...)
Je to teda smerník na funkciu, ktorá vracia konštantný char.
const char (*HlavnyTyp)( void(*(*)[])(int*) )
Analýzou tohto vidím, že vstupom do funkcie je void(*(*)[])(int*)
Zanalyzujme teda, čo to je...
Keďže v strede je (*) tak je to smerník na dačo.
Hneď vedľa vidieť ...(*)[], a teda je to smerník na pole čohosi, konkrétne toho čo bude na mieste ...
Na mieste ... však nebol konkrétny typ, ale zas len smerník - ach jaj... :) Teda je to smerník na pole smerníkov na čosi.
...(*(*)[])(...)
Z takejto štruktúry vidieť, že to čosi sú funkcie. Je to teda smerník na pole smerníkov na funkcie.
void(*(*)[])(int*)
Zistiť z tohto, že sú to funkcie do ktorých vstupuje smerník na int a ich výstupom je void, už nie je žiaden problém. Je to teda smerník na pole smerníkov na funkcie so vstupným parametrom smerník na int, a výstupom void.
Vráťme sa k pôvodnému. const char (*HlavnyTyp)( void(*(*)[])(int*) )
Celé je to teda smerník na funkciu, ktorá vracia konštantný char a jej vstupom je void(*(*)[])(int*).
Celou vetou: Smerník na funkciu, ktorá vracia konštantný char a jej vstupom je smerník na pole smerníkov na funkcie so vstupným parametrom smerník na int, a výstupom void.

Smerník na pole vs. pole smerníkov

Tak túto poznámku si jednoducho musím napísať, lebo mi to robilo obrovský problém rozlišovať zápis týchto dvoch. Pritom je to celkom jednoduché. Len treba radšej písať zátvorky.
Začnime so smerníkom na pole. Smerník na niečo by vyzeral takto:
NIECO (*MENO);
Ak by to bol teda napríklad smerník na int vyzeral by
int (*MENO);.
No a dosaďme za NIECO pole...
NIECOHO ((*MENO)[]);
Za NIECOHO teraz stačí dosadiť čoho. A potom smerník na pole 20 intov vyzerá takto:
int ((*MENO)[20]);

Pri poli smerníkov začíname inak. Deklarácia poľa vyzerá takto:
TYP (MENO[]);
Pole 20 intov napríklad sa deklaruje takto - za TYP dosadím int. Dostávam:
int (MENO[20]);
Ak dosadím za typ smerník, tak pole smerníkov vyzerá takto:
NA_CO *(MENO[])
Za NA_CO treba dosadiť na čo jednotlivé ukazovatele ukazujú... Ak budem chcieť teda pole 20 ukazovateľov na char dostávam:
char *(MENO[20]);

Deklarácie (zložitejších) funkcií

Všeobecne to vyzerá takto:
TYP_VYSTUPU MENO (TYPY VSTUPOV);
A teda jednoduchá deklarácia funkcie, ktorej vstupom sú dva int a výstupom je char vyzerá takto:
char MENO(int a, int b);
Fajn povedzme teraz, že výstupom bude smerník na char a vstupmi dva smerníky na int
char* MENO(int *a,int *b);
Resp. ak rozpíšem zátvorky (v takto jednoduchom prípade netreba) tak to vyzerá:
char (*(MENO))(int (*a), int (*b));
Povedzme však, že vstupom je smerník na funkciu B, pričom funkcia B je deklarovaná takto:
int B(int a);
Smerník na funkciu typu B by vyzeral takto:
int (*SmernikNaB)(int a);
Potom funkcia, ktorej vstupom je smerník na funkciu typu B, a výstupom je char vyzerá takto...
char (MENO)(int (*SmernikNaB)(int a));
Povedzme však, že aj výstupom funkcie je smerník na funkciu typu B
Začnime zľahka... Povedzme si, že výstupom je nejaký smerník...
... (*(MENO)(int (*SmernikNaB)(int a)));
Na miesto bodiek teraz treba napísať, smerník na čo to je... Keďže je to smerník na funkciu, tak to bude vyzerať takto:
... (*(MENO)(int (*SmernikNaB)(int a))) (...);
Namiesto bodiek teraz už len napíšeme, čo sú vstupy a výstupy tej funkcie na ktorú oný smerník ukazuje...
int (*(MENO)(int (*SmernikNaB)(int a))) (int a);

V prípade, že funkciu iba deklarujeme, a jej telo nebude nasledovať, tak netreba vypisovať aj mená premenných a stačí použiť abstraktné deklarátory. Funkcia, ktorej vstupmi budú dva int a výstupom bude void potom môže byť deklarovaná takto:
void FUNKCIA(int, int);
No a funkcia, ktorú som deklaroval hore, môže byť deklarovaná (ak sa jedná iba o deklaráciu...) takto:
int (*(MENO)(int (*)(int))) (int);

Inicializácie premenných

...
int a;
int b(10);
int *c=new int(20);
...
Tak toto je úplne jasné, trošku neznáme mi bolo akurát, že existuje aj ten druhý spôsob. Tento sa používa pri objektovom programovaní (vraj :) Tiež keď si vytvorím smerník a chcem ho hneď na niečo nasmerovať, tak si dynamicky vytvorím miesto v pamäti a práve pomocou týchto zátvoriek si ho hneď aj naplním. Keď sa totiž napíše len int *c; tak je to pomerne nebezpečné, lebo ten smerník nesmeruje nikam...

(c) Wray 2006