Skočiť na obsah


Programovanie v jazyku C a C#

Návody pre začiatočníkov a pokročilých



Fórum: Škôlka jazyka C
- - - - -
Fotografia

3. lekcia C (Preprocesor) základná škola


3. Preprocesor jazyka C.
Doteraz sme nevedomky používali najčastejšie používaný príkaz preprocesora #include.
Ale preprocesor má celý rad príkazov, ktoré dodávajú silu jazyku C.
Činnosť preprocesora sa dá zhrnúť do niekoľkých základných bodov:
* Spracuje zdrojový text programu pred použitím prekladača.
* Nekontroluje syntaktickú správnosť prohgramu.
* Prevádza len zámenu textov, napr. identifikátorov konštanz za zodpovedajúce číselné hodnoty.
* Vypúšťa zo zdrojového textu všetky komentáre.
* Prevádza podmienený preklad.
Poznámka:
Riadok, ktorý má byť spracovaný preproxesorom musí začínať znakom # a za ním by nemala
byť medzera.
Zoznam konštrukcií ktoré rozoznáva preprocesor:

* definovanie makra

	 #define meno_makra text rozvoja

* zrušenie definície makra

	 #undef meno_makra

* podmienený preklad textu v závislosti na hodnote konts_výraz

	 #if konst_výraz

	 #elif #else #endif

* vloženie textu zo špecifikovaného súboru v adresári užívateľa

	 #include "filename"

* vloženie textu zo špecifikovaného súboru zo systémového adresára

	 #include <filename>

* podmienený preklad textu v závislosti na tom, či je makro meno_makra definované

alebo nedefinované

	 #ifdef meno_makra

	 #elif #else #endif

* podmienený preklad textu v závislosti na tom, či je makro meno_makra nedefinované

alebo definované

	 #ifndef meno_makra

	 #elif #else #endif

* výpis chybových správ behom procesingu

	 #error Chybová správa

Poznámka:
Okrem uvedených, ktoré sa v praxi používajú často, pozná preprocesor aj:
* direktívu #line
* operátory # a ##
* operátor defined
* preddefinovaná štandartné makrá:
__LINE__ __FILE__ __TIME__ __DATE__ __STDC__
* direktívu #pragma
3.1 Makrá bez parametrov - príkaz define.
Sú známejšie pod názvom symbolické konštanty. Používajú sa veľmi často, pretože zbavujú
program "magických čísiel" tj. najrôznejších konštánt, ktoré sa bez vysvetlenia onjavujú
v programe.
Pravidlá ich písania:
* Názvy symbolických konštánt sa píšu vždy VEĽKÝMI PÍSMENAMI.
* Názov je od hodnoty oddelený aspoň jednou medzerou.
* Za hodnotou by mal byť komentár.
* Nové konštanty môžu využívať už skôr definované konštanty.
* Pokiaľ je hodnota konštanty dlhšia ako riadok, musí byť na konci riadku znak "\", ktorý
sa ale do makra nerozvinie - je to len pomocný znak.

Príklady:

#define MAX			1000		/* max. rozmer pola */

#define PI			 3.14

#define DVE_PI		 (2 * PI)

#define MOD			%

#define AND			&&

#define MENO_SUBORU	"DOPIS.TXT"

#define DLHA_KONSTANTA Toto je dlha konstanta, ktora sa \

					 nevojde do jedneho riadku.

Poznámka:
* Za hodnotou nie je na 99% stredník ;.
* Konštanta môže byť kdekoľvek v programe s výnimkou - nemala by byť súčasťou reťazca
medzi úvodzovkami, pretože tam nebude fungovať.
* Konštanta platí od mista definície až do konca súboru v ktorom bola definovaná.
Príklady:
1) Použitie symbolickej konštanty pre Ludolfove číslo.

#define DVE_PI (2 * 3.14)

main()

{

double r;



printf("Zadaj polomer: ");

scanf("%lf", &r);

printf("Obvod kruhu s polomerom %f je %f\n", r, r * DVE_PI);

}

2) Program prečíta riadok textu a každé malé písmeno zobrazí ako veľké,
ale vytlačí pred ním znak "#".

#include <stdio.h>

#define POSUN		 ('a' - 'A')

#define EOLN		 '\n'

#define PRED_MALE	 '#'

main()

int c;



while ((c = getchar() != EOLN) {

	if (c >= 'a' && c <= 'z') {

	 putchar(PRED_MALE);

	 putchar(c - POSUN);

	}

	else

	 putchar(c);

}

}

Poznámka:
Ak budeme chcieť niekedy v budúcnosti tlačiť namiesto znaku "#" napr. znak "*", stačí
len jedna zmena na začiatku programu.
Z definície POSUN je vidieť, že symbolickou konštantou môže byť aj výraz. Len ho treba uzavrieť
do úvodzoviek.
Pozor:
Makro sa nerozvinie, pokiaľ je uzavreté v úvodzovkách, napr.:

#define MENO Libco

printf("Volam sa MENO.");

vytlačí: Volam sa MENO. (nie Libco)
riešenie:

#define MENO "Libco"

printf("Volam sa %s", MENO);

Nová definícia makra najčastejšie prekrýva starú len vtedy, ak je to rovnaká definícia.
To väčšinou nebýva splnené, pretože definovať znovu rovnakú konštantu nemá význam.
V tom prípade je treba starú definíciu najskôr zrušiť direktívou #undef, napr.:

#define POCET 10	/* stara definicia POCET */

#undef POCET		 /* POCET je neplatna */

define POCET 20	/* nova definicia POCET */

Občas sa používa makro ako skrytá časť programu, napr.:

#define ERROR { printf("Chyba v datach \n"); }

Pri použití nesmie byť toto makro ukončené stredníkom:

if (x == 0)

ERROR	 /* tu nie je strednik */

else

y = y / z;

3.2 Makrá s parametrami.
Pri riešení programov sa časato vyskytuje prípad, kedy veľa krát používame nejakú funkciu,
ktorá je veľmi krátka, napr. jednoduchý výpočet nejakej hodnoty. Takúto funkciu napísať
je jednoduché, ale niekedy potom nastáva problém s efektivitou programu.
Teda ak je funkcia veľmi krátka, je niekedy jej využívanie, tj. odovzdanie parametrov,
úschova návratovej adresy, skok do funkcie, návrat z funkcie do miesta volania a výber
použitých parametrov, dlhšie ako samotný užitočný kód funkcie. Toto samozrejme zdržuje
výpočet programu.
Našťastie Céčko umožnuje toto úplne odstrániť, ale nič nie je zadarmo. Takže použitie
makier s parametrami (to je totiž to riešenie) sa zväčší dĺžka programu.
Záleží na programátorovi aby si vybral menšie z dvoch ziel - buď bude program kratší,
ale pomalší, alebo bude dlhší, ale rýchlejší.
Syntax makra s parametrami:

#define meno_makra(arg1, ..., argN) hodnota_makra

Názvy makier s parametrami sa na rozdiel od makier bez parametrov píšu malými písmenami.
Syntax volania makra s parametrami:

meno_makra(arg1, ..., argN)

Príklad:
Makro na test či je znak veľké písmeno:
Napr. v programe na prevod veľkých písmen na malé je volané:

ch = je_velke(ch) ? ch + ('a' - 'A') : ch;

Poznámky:
* Všimnite si, že argument v definícii makra je uzatvorený do zátvoriek.
Ak to nespravíme, je veľká šance vzniku chýb, napr.:

definícia: #define sqr(x) x * x

sa po zavolaní: sqr(f + g)

rozvinie do: f + g * f + g

Správne má byť definícia: #define sqr(x) ((x) * (x)

toto sa rozvinie do: ((f + g) * (f + g))

* Je dobré vždy uvádzať aj vonkajšie zátvorky, pretože napr.:

#define citaj(c) c = getchar()

sa po volaní: if (citaj(c) == 'a')

rozvinie do známej chyby: if (c = getchar() == 'a')

Pozor:
Ak sa objaví argumet v hodnote makra viackrát, vtedy by makro nemalo byť volané s aktuálnym
parametrom. ktorý môže mať vedľajší účinok, napr.:

#define cislo(x) ((x) >= '0' && (x) <= '9')

po volaní: if (cislo(c++)

spôsobí, že premenná c bude inkrementovaná 2 krát, čo nie je správne.

3.2.1 Preddefinované makrá.
Makrá pre zjednodušenie vstupu a výstupu na terminál sú väčšinou definované v knižnici stdio.h
a majú túto podobu:

#define getchar() getc(stdin)

#define putchar() putc(c, stdout)

Dalšia knižnica v ktorej je definovaných množstvo užitočných makier je ctype.h. Makrá v nej
pracujú so znakmi a delia sa na dve skupiny:
1) Makrá na určenie typu znaku.

vracia	názov	 rozsah použitia

--------------------------------------------------------------------

znak	 isalnum	číslice, malé a veľké písmená

znak	 isalpha	malé a veľké písmená

1		 isascii	ASCII znaky (0 až 127)

znak	 iscntrl	Ctrl znaky (1 až 26)

znak	 isdigit	číslice

znak	 islower	malé písmená

1		 isprint	tlačiteľné znaky (32 až 126)

znak	 ispunct	interpunkčné znaky (botka, čiarka, lomítko, ....)

znak	 isspace	biele znaky (medzera, tabulátor, nový riadok, ....)

znak	 isupper	veľké písmená

znak	 isxdigit hexadecimálne číslice ('0' - '9', 'A' - 'F', 'a' - 'f')

1		 isgraph	znak s grafickou podobou (33 až 126)

3) Makrá na konverziu znakov.

tolower	- konverzia na malé písmená

toupper	- konverzia na veľké písmená

toascii	- prevod na ASCII - len najnižších 7 bitov je významových

Poznámka:
Aby sme ich mohli v programe použiť, treba includovať aj knižnicu <ctype.h>
3.3. Vkladanie súborov -- príkaz include.
#include používame vlastne od začiatku (#include <stdio.h>)
Dva tvary používania #include:
1) #include "KONSTANTY.H"
hľadá súbor KONSTANTY.H v rovnakom adresári ako sa nachádza volajúci súbor - program.
Používa sa pre prácu so súbormi, ktoré sme vytvorili my sami.
2) #include <ctype.h>
hladá súbor ctype.h v systémovom adresári.
Používa sa pri práci s už hotovými špaciálnymi súbormi, ktoré sa nazývajú
štandartné hlavičkové súbory.
Poznámka:
Niektorí programátori používajú konvenciu, že mená štandartných hlavičkových súborov
píšu malým písmom a mená hlavičkových súborov, čo sami vytvorili veľkým písmom.
3.3.1 Vkladané súbory.
Súbory vkladané pomocou #include môžu byť ľubovolné textové súbory. Význam má však len
vkladanie zdrojových súborov - s príponou .C.
Druhá, oveľa častejšia možnosť je vkladanie tzv. hlavičkových súborov - s príponou .H.
Vkladanie hlavičkových súborov je veľmi užitočný mechanizmus na to, ako program zložený
z viacerých súborov udržať čitateľný a prehľadný.
Napríklad všetky definície konštánt využívané viacerými súbormi sa uvedú len do jedného
súboru typu .H, ktorý sa pomocou #include pripojí do všetkých súborov, ktoré tieto definície
konštánt potrebujú. To má obrovskú výhodu, te prípadná zmena konštánt sa potom urobí len
v .H súbore a ostatné súbory stačí len preložiť.
Príklad:
Definície konštánt popisujúca pomery na obrazovke sú uložené v tomto súbore:

/*

* OBRAZ.H

*

* Konstanty obrazovky

*

*/

#ifndef OBRAZ

#define OBRAZ

#define RIADKY_OBRAZOVKY	25

#define STLPCE_OBRAZOVKY	80

#endif

/* koniec suboru OBRAZ.H */

Vo všetkých súboroch, ktoré potrebujú pracovať s obrazovkou sa potom použije príkaz:

#include "OBRAZ.H"

3.3.2 Štandartné hlavičkové súbory.
Ak chcete vedieť ako vyzerá (čo obsahuje) napr. súbor stdio.h, dajte si ho v PC vyhľadať
a potom ho otvorte v nejakom textovom editore.
Tieto súbory sú najčastejšie uložené v adresári s názvom INCLUDE (include).
Z mnohých ďalších .h súborov okrem stdio.h spomeniem ešte tri najčastejšie používané:
- ctype.h pre prácu so znakmi
- math.h pre prácu s matematickými funkciami, napr. sinus, cosinus a pod.
- time.h tomu to sa trocu viac povenujeme.
3.3.3 Súbor time.h - meranie času.
Obsahuje popisy mnohých užitočných funkcií pre prácu s časom. Ako príklad môžme uviesť
použitie funkcie clock(), ktorá vrycia počet "tikov" procesora od začiatku spustenia programu.
Teda zistí ako dlho program (alebo jeho časť) bežal.

#include <stdio.h>

#include <time.h>

main()

{

clock_t start, end; /* long start, end; */

unsigned int i;



start = clock();



for (i = 0; i < 1000000; i++)

	printf("*");

end = clock();

printf("\nProgram trval: %6.2f sec\n", (end - start) / (double) CLOCKS_PER_SEC);

}

3.4 Oddelený preklad súborov - I.
Ak je program rozdelený na viac súborov, ktoré sa pomocou #include vložia do jedného súboru,
vznikne po preklade jediný .OBJ súbor.
Oddelený preklad znamená, že sa každý súbor preloží zvlášť. Vznikne teda niekoľko .OBJ súborov,
ktoré sa spoja do jedného programu až pomocou zostavovacieho programu (linkeru).
Na prvý pohľad sa to zdá zbytočne zložité, ale v praxi je to jediný spôsob, ako rozumne zvládnuť
prácu s veľkými súbormi.
Moderné kompilátory tento spôsob podporujú. Totiž, čím je prekladaný súbor väčší, tým väčšie
sú na kompilátor kladené požiadavky na rôzne administratívne informácie, napr. tabuľky
identifikátorov, atď.
To môže viesť až k stavu, keď sa pri preklade obsadí veškerá operačná pamäť a preklad sa
v horšom prípade neúspešne skončí, v lepšom extrémne spomalí.
Spomalenie je spôsobené tzv. swapovaním, teda stavom, keď je procesor nútený odkladať
momentálne nepotrebné úseky pamäti na disk.
Príklad:
Máme 3 súbory S1.c, S2.c a S3.c, kde funkcia main() je v S1.c.
1) Vkladanie súborov

------	------	------

| S1.c | | S2.c | | S3.c |

------	------	------

|		 |		 |

--------------------------

|	 |	#include "S1.c" |

| S.c |	#include "S2.c" |

|	 |	#include "S3.c" |

--------------------------

			 |

			 |

		 -------

		 | S.OBJ |

		 -------	

zostavenie: link s

2) Oddelený preklad

------	 ------	------

| S1.c | | S2.c | | S3.c |

------	 ------	------

|		 |		 |

|		 |		 |

-------- -------- --------

| S1.OBJ || S2.OBJ || S3.OBJ |

-------- -------- --------

zostavenie: link s1, s2, s3

Poznámka:
* Pri vkladaní súborov sa všetky tri inkludujú do súboru S.c (ten môže obsahovať len tri príkazy
#include a nič viac), ktorý sa preloží a vznikne jeden .OBJ súbor a ten sa samostatne
zostaví - zlinkuje.
Nevýhodou tohto spôsobu je, že pri zmene v hociktorom súbore (S1.c, S2.c alebo S3.c) sa musia
celkom zbytočne aj ďalšie dva súbory, čo môže byť niekedy časovo veľmi náročné.
* Pri oddelenom preklade sa každý súbor samostatne preloží do .OBJ súboru. Výhodou je, že sa pri
opakovanom preklade (najčastejšie pri ladení) prekladá len súbor, ktorý bol skutočne zmenený.
Tento spôsob sa odporúča používať, pretože nás núti rozdeliť problém na viac menších častí.
To síce zpočiatku zaberie určité množstvo času, ale pri ladení veľkého programu sa to
mnohonásobne vráti.
3.5 Podmienený preklad
V mnohých prípadoch sa zložitejšie programy píšu tak, že obsahujú ladiace časti.
To sú najčastejšie pomocné výpisy, ktoré majú uľahčiť ladenie. Tieto časti sa do programov
dávajú, aj keď máme k dispozícii výkonný debuger.
Je dobrým zvykom počítať už pri návrhu programu s tým, že ho bude nutné ladiť a už pri návrhu
programu tieto časti do programu zaraďovať.
Po odladení však nastáva typický problém - ako tieto časti (už nepotrebné) z programu odstrániť.
Najjednoduchšie je ich vymazať, ale pritom sa môže stať, že zmažeme aj dúležité záležitosti
a tým program zneschopníme.
Céčko našťastie počíta aj stýmto problémom, takže pomocou príkazov preprocesora môžeme určiť,
ktoré časti programu sa majú prekladať podmienene. To znamená, že všetky ladiace časti
už pri vytváraní programu označíme ako podmienene prekladané a pri ladení ich prekladáme,
ale po odladení už nie.
Ladiace časti sú tak trvalou súčasťou zdrojového programu, ale sú zároveň voliteľnou súčasťou.
Preprocesor teda na jediný príkaz všetky tieto časti vypustí sám, ale budú k dispozícií do
budúcnosti. V prípade potreby ich jedným príkazom zase do programu zaradíme.
Podmienený preklad je riadený základnými podmienkami:
3.5.1 Riadenie prekladu hodnotou konštantného výrazu

#if konstantny_vyraz

	časť_1

#else

	časť_2

#endif

Prekladač bude túto časť programu spracovávať tak, že ak je konstantny_vyraz rovný 0 (FALSE),
prekladá sa len časť_2, v opačnom prípade (nenulová hodnota TRUE), len časť_1.
Poznámky:
* Časť:

#else

	časť_2

môžu byť vynechané, jednoduchšie je to takto:

#if 0

	časť programu, ktorá má byť vynechaná

#endif

* Často sa podmienený preklad používa pri vývoji vic platformových programov, napr.:

#if PCAT

#include <conio.h>	/* consol input/output */

#else

#include <stdio.h>	/* standard input/output */

#endif

Ak budeme používať program na PC/AT, stačí predradiť príkaz:
#define PCAT 1
N ostatných PC príkaz:
#define PCAT 0
* V praxi je možné rozšíriť pôsobnosť tohto príkazu využitím direktív
#elif a #error a operátoru defined.
3.5.2 Riadenie prekladu definíciou makra
Podmienený preklad riadený hodnotou kenštantného výrazu je mocný nástroj, ale oveľa častejšie
sa používa jeho jednoduchšia verzia, ktorá je závislá len na tom, či je konštanta definovaná
alebo nie.
Pri použití:

#if PCAT

#include <conio.h>	/* consol input/output */

#else

#include <stdio.h>	/* standard input/output */

#endif

je zmena v tom, že ak budeme používať PC/AT stačí príkaz:

#define PCAT	/* prázdny, ale definovaný */

Na ostatných PC príkaz zmeníme:

#undef PCAT	/* zrušená definícia makra PCAT */

alebo jednoduchšie, symbolickú konštantu PCAT vôbec nedefinujeme.
Poznámky:
* K dispozícii je ešte jeden príkaz, ktorý je vlastne len negáciou predchodzieho, teda je
riadený tým, že symbolická konštanta nebola definovaná.
Príklad:

#ifndef PCAT

#include <stdio.h>	/* standard input/output */

#else

#include <conio.h>	/* consol input/output */

#endif

Po príkaze: #undef PCAT sa prevedie: #include <stdio.h>
Po príkaze: #define PCAT sa prevedie: #include <conio.h>
3.5.3 Operátor defined
Test existencie definície symbolickej konštanty pomocou direktív #ifdef alebo #ifndef
umožňuje zisťovať existenciu len jedného symbolu (mena konštanty). Aby bolo možné vytvárať
pri podmienenej kompilácii logické výrazy z viac symbolov, je nutné použiť operátor defined.
Spôsob použitia operátoru ukazujú nasledujúce tri ekvivalentné príkazy:

pre direktívu #ifdef		pre direktívu #ifndef

#ifdef TEST				 #ifndef TEST

#if defined TEST			#if !defined TEST

#if defined(TEST)		 #if !defined(TEST)

3.5.4 Direktívy #elif a #error
Možnosti všetkých uvedených typov podmienenej kompilácie rozširuje direktíva #elif, ktorá
má analogický význam ako príkaz else-if v podmienenom príkaze.
Ďalšia direktíva #error umožňuje výpis chybových správa už behom fázy preprocessingu.
Ľubovoľný text, ktorý nasleduje za príkazom #error je vypísaný na štandartné zariadenie
(najčastejšie obrazovku) a kompilácia je ukončená chybou. Táto direktíva sa typicky
používa na kontrolu hodnôt symbolických konštánt ovplivňujúcich podmienenú kompiláciu.
Príklad na vysvetlenie použitia direktív #elif a #error a tiež operátora defined:

#if defined(ZAKLADNY) && defined(DEBUG)

#define VERZIA_LADENIA 1

#elif defined(STREDNY) && defined(DEBUG)

define VERZIA_LADENIA 2

#elif !defined(DEBUG)

#error Pozor, ladiacu verziu nie je mozne pripravit!

#else

#define VERZIA_LADENIA 3

#endif	

Časté chyby:

#define A = 1				pred 1 nemá byť =

#define PI 3.14;			 za 3.14 nemá byť stredník

#define inc(X) x + 1		 má byť ((x) + 1)

#define inc (x) ((x) + 1)	medzi inc a (x) nesmie byť medzera

Čo je dobré si uvedomiť:
* Všetky parametre v definícii makra s parametrami by mali byť uzatvorené do zátvoriek.
* Vyhýbajte sa možnosti vzniku vedľajších efektov pri vyhodnocovaní parametrov makra.
* Každú použitú konštantu definujte ako symbolickú a to hneď na začiatku programu.
Zlepšuje to prenositeľnosť programu a jeho čitateľnosť.
* Použite makrá s parametrami na skrytie dlhých, opakujúcich sa a komplikovaných výrazov.
Významne to zlepšuje čitateľnosť programu.
* Používajte podmienenú kompiláciu pre vynechanie ladiacich častí programu.
* Ak prekladač hlási nejakú chybu, na ktorú nemôžte prísť, je vhodné skontrolovať súbor
po spracovaní preprocesorom. Je možné, že chyba bude v rozvoji niektorého makra.

Úlohy:

1) Napíšte program, ktorý spočíta N prvých prirodzených čísiel a vypíše napr.:
Sucet prvych 5 cisiel je 15.
N definujte ako symbolickú konštantu.
Spoiler

2) Čítajte znaky z klávesnice až do EOLN, ktoré sa zadefinujete. Po skončení čítania
vypíšte počet zadaných čísiel. Ďalej odpíšte len zadané malé písmená, ktoré preveďte
na veľké. Využite makrá isdigit(), islower() a toupper() zo súboru ctype.h.
Spoiler

3) Napíšte makro na_tretiu(x), ktoré bude počítať tretiu mocninu. Vyskúšajte ho na výrazoch:
na_tretiu(3)
na_tretiu(i)
na_tretiu(2 + 3)
na_tretiu(i * j + 1)
Spoiler

4) Definujte makro je_velke( c ), ktoré vráti 0 ak znak nie je veľké písmeno a 1,
ak je veľké písmeno.
Spoiler

5) Definujte makro moze_tlacit( c ), ktoré zistí, ži je znak tlačiteľný
(od ASCII hodnoty 32 do 126). Pomocou makra vytlačte ACSII tabuľku.
Spoiler

6) Definujte makro citaj_int(i), ktoré číta z klávesnice celé číslo. Makro sa musí dať
použiť vo výraze:
if ((j = citaj_int(k)) == 0)
Nápoveda: Využite operátor čiarka.
Spoiler

7) Predchádzajúci príklad rozdelte do dvoch súborov MAKRO.C a HLAVNY.C a pomocou príkazu
#include zaistite správne chovanie programu.

MAKRO.C
Spoiler

HLAVNY.C
Spoiler

8) Napíšte program, ktorý bude čítať 10 celých čísiel. Čísla bude čítať buď zo súboru
CISLA.TXT, kde bude každé číslo na novom riadku, alebo z klávesnice. Pri obidvoch
spôsoboch sa bude na obrazovke vypisovať text:
Zadaj 1. cislo :
Určite počet párnych čísiel.
Spoiler
  • 0

Popis: Preprocesor

- makrá bez parametrov (symbolické konštanty)
- makrá s parametrami
- preddefinované makrá
- vkladanie súborov - príkaz include
- štandartné hlavičkové súbory
- oddelený preklad súborov - I
- podmienený preklad
- riadenie prekladu hodnotou konštantného výrazu
- riadenie prekladu definíciou makra
- operátor defined
- direktívy #elif a #error
- úlohy k lekcii
Poznámky: Ak chceš, aby ti niekto úlohy skontroloval, zabal ich do zip-u a zašli na e-mail:
libcosenior zavináč gmail botka com

Poprípade tam môžeš konzultovať, čo ti nie je jasné.
Odkazy:


0 Komentárov


Najlepšie lekcie


Najnovšie pridané lekcie


Najnovšie komentáre


Najviac komentované lekcie


Najviac zobrazené lekcie


Náhodné lekcie


Na tejto stránke bolo užívateľ(ov) za posledných 30 minút

členov, návštevníkov