NOVINKA - Online rekvalifikační kurz Java programátor. Oblíbená a studenty ověřená rekvalifikace - nyní i online.
NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!

Diskuze: Prototyp funkce

V předchozím kvízu, Online test znalostí C++, jsme si ověřili nabyté zkušenosti z kurzu.

Aktivity
Avatar
Martin Chvátal:22.2.2016 22:35

Ahoj,

nedávno jsem se začal učit C++, s pomocí knihy C++ za 21 dní(vím za 21 dní se to nejde naučit) ovšem vše jsem relativně chápal a vím jak vše funguje relativně až do 5 kapitoly, která je věnována funkcím, základy funkcí jsem pochopil ovšem co nechápu tak prototypy funkcí a celkově jak se funkce dokáže spojit s prototyp a jak to celé komunikuje mezi sebou, proč deklarovat typ dvakrát (v prototypu a ve funkci samotné(argumenty funkce mám na mysli)), a poté volání funkce to mi taky moc nejde do hlavy, děkuji za pomoc :-)

Martin

Odpovědět
22.2.2016 22:35
Nothing is true, everything is permitted.
Avatar
Odpovídá na Martin Chvátal
Patrik Valkovič:22.2.2016 22:41

Deklarace funkce pouze říká, že někde v programu tahle funcke existuje. Neříká kde, ale víš, že existuje. Kompilátor čte soubor od začátku (shora dolů). Kdybys měl tedy funkci, ve které bys volal jinou funkci, která by byla definována až pod ní, tak to neprojde (kompilátor k ní ještě nepřišel). Proto existuje deklarace.

int funkce1(); //hele, bude existovat funkce 1

int funkce2()
{
        return funkce1(); //jasne, sice jeste nevim kde, ale tato funkce nekde bude existovat
}

int funkce1()  //jasny, tady je
{
        return 5;
}

Po kompilaci se na to pustí linker, který nahradí jméno funkce jejím volání. Zjednodušeně řečeno. Na plné pochopení bys musel více prostudovat Assembler.

Nahoru Odpovědět
22.2.2016 22:41
Nikdy neumíme dost na to, abychom se nemohli něco nového naučit.
Avatar
Martin Chvátal:22.2.2016 23:02

no to jsem se tam dočetl taky že je to proto aby kompilátor aby věděl co tam bude, jelikož ano on čte soubor od shora dolů, ovšem furt mi neleze do hlavy toto -

int funkce1(int x, int y);//toto je ten prototyp
int main()
{
        cout << "hodnota souctu - " << funkce1 << endl;
}
int funkce1(int scitanec, int scitanec2)// a zde nechapu jak funguje to
//kdyz nahore v protoypu je x a y tak jak se to preda a jak se to spoji a vznikne s toho scitanec a scitanec2
//kdyz tam nahore jsem mu rekl ze je to prece x a y
{
        return (scitanec + scitanec2);
}

zde samozřejmě chybí ještě požádání o vstup dvou čísel od uživatele ale to tady není podstatný

Nahoru Odpovědět
22.2.2016 23:02
Nothing is true, everything is permitted.
Avatar
Odpovídá na Martin Chvátal
Patrik Valkovič:22.2.2016 23:23

O to se postará linker. To je o něco složitější záležitost a i já to pořádně nechápu.
Binární kód je rozdělen po bajtech (offset). Budu brát pro příkald čisté C (v C++ probíhá ještě name mangling (https://en.wikipedia.org/…ame_mangling)
Máš tento soubor:

int funkce1(int x,int y);

int funkce2()
{
        return funkce1(5,4);
}

int funkce1(int prvni, int druhy)
{
        return prvni+druhy;
}

Kompiler vygeneruje něco taového (na začátek budu psát offset)

0x0000 funkce1
0x0001 //nemusí tu nutně nic být
0x0002 funkce2
0x0003 //plnění parametru
0x0004 call funkce1
0x0005 //plnění návratové hodnoty
0x0006 return
0x0007 funkce1
0x0008 add [prvni],[druha] //zjednodušeně
0x0009 //naplnění návratové hodnoty
0x000A return

Potom to vezme linker spojí to dohromady

0x0000 //pouze deklarace, takže se to v binárním souboru nezobrazí
0x0001 //nemusí tu nutně nic být
0x0002 funkce2
0x0003 //plnění parametru
0x0004 call 0x0007 //samotné volání funkce
0x0005 //plnění návratové hodnoty
0x0006 return
0x0007 funkce1
0x0008 add [prvni],[druha] //zjednodušeně
0x0009 //naplnění návratové hodnoty
0x000A return

Aspoň tak to chápu já, možná by to chtělo názor někoho, kdo se o to zajímá víc (David Novák)?

Co se týče parametrů jako takových, ty vůbec nemusí být pojmenované, je to pouze informace pro kompiler, kolik má vyhradit místa na zásobníku, klidně můžeš napsat

int funkce1(int,int);

int funkce2()
{
        return funkce1(5,4);
}

int funkce1(int prvni, int druhy)
{
        return prvni+druhy;
}

Dokonce bych řekl, že ty parametry nemusí být explicitně napsané, z ASM by měli jít vytáhnout, ale to ber spíše jako takovou perličku, bez znalosti ASM se to těžko vysvětluje. Moje rada zní: ber to jako je. A trošku programátorsky: funguje to, tak na to nešahej :D

Editováno 22.2.2016 23:24
Nahoru Odpovědět
22.2.2016 23:23
Nikdy neumíme dost na to, abychom se nemohli něco nového naučit.
Avatar
Patrik Valkovič:22.2.2016 23:30

Hmm....kdybych tě tím linkerem popletl, tak to ignoruj :D Prakticky jsi se ptal na něco jiného (odpověď poslední dva paragrafy) :D

Nahoru Odpovědět
22.2.2016 23:30
Nikdy neumíme dost na to, abychom se nemohli něco nového naučit.
Avatar
Martin Chvátal:22.2.2016 23:31

tak to není ani programátorsky ale i v životě dokud to jede aspoň nějak tak do toho nehrab :D klasika :D, no rozhodně když si toho přečtu ještě 100krát tak to možná pochopím jak to faká :D, pravda do prototypu není třeba cpát vůbec žádný název proměnné, rozhodně ti děkuji moc za pomoc :)

Nahoru Odpovědět
22.2.2016 23:31
Nothing is true, everything is permitted.
Avatar
Odpovídá na Martin Chvátal
Patrik Valkovič:22.2.2016 23:37

V deklaraci řekneš, jaká bude návratová hodnota, jaký je název funkce a jaké má parametry. Návratová hodnota a parametry jsou tam kvuli kompileru, aby věděl, kolik má vyhradit místa (například v našem případu to bude 12 bajtů - int má 4b, tedy 4b na návratovou hodnotu a 8b na parametry). Jméno potom pro linker, aby dokázal funkci identifikovat.

Třeba když budeš mít funkce ve dvou různých souborech, tak můžeš zkompilovat pouze jeden (do tzv. obj nebo .o souboru), který už bude binární. Ta funkce se tam bude volat zástupným symbolem (právě tou identifikací). Ale kompiler už bude vědět, kolik má vyhradit místa a víc informací nepotřebuje. Až linker to spojí dohromady - zástupný symbol nahradí skutečnou adresou funkce.

Nahoru Odpovědět
22.2.2016 23:37
Nikdy neumíme dost na to, abychom se nemohli něco nového naučit.
Avatar
Martin Chvátal:22.2.2016 23:44

už mi to začíná lehce docházet :) asi teda :D

Nahoru Odpovědět
22.2.2016 23:44
Nothing is true, everything is permitted.
Avatar
David Novák
Tvůrce
Avatar
Odpovídá na Martin Chvátal
David Novák:23.2.2016 0:13

Je to ještě o něco komplikovanější, než to Patrik popisuje, ale asi není úplně třeba to znát dopodrobna.. Podstatné je, že deklarace se nikde po překladu nevyskytují - je to čistě jen berlička pro programátory :)

To, že deklaruješ funkci, umožní překladači, aby tě kontroloval - jestli předáváš správné argumenty a jestli ta funkce vůbec někde existuje.. Například když použiješ printf bez #include <stdio.h> (tedy překladač nikde nemá deklaraci printf), tak to bude fungovat a jen dostaneš warning, že neví, jak ta funkce vypadá. Najde ji již zmíněný linker, protože je ve standardní knihovně.

Abys mohl pochopit princip, tak musíš prvně vědět, že ve skutečnosti neexistuje nic jako "proměnná" nebo "funkce" - pro procesor je to prostě sekvence instrukcí (instrukce je například add - sečti), která leží v paměti. Proměnné jsou jen místa v paměti a přistupuje se k nim pomocí adresy. Když napíšeš int cislo, tak vlastně říkáš, že chceš kus paměti, co bude 4 bajty dlouhý. Ty budeš k tomu místu přistupovat pomocí "cislo" - za to vděčíš překladači. Ve skutečnosti to pak po linkování ale bude jen adresa.

Pokud je program a jeho data vlastně jen souvislý kus paměti, jak tedy fungují cykly, podmínky, funkce apod? Odpověď zní skoky. Procesor prostě skáče z místo na místo v tom kódu. Máš takzvaný vstupní bod, kde program začne. Pak se postupně vykonává (instrukce po instrukci). Pak narazíš například na volání funkce. To je ve skutečnosti jen skok na určitou adresu a vytvoření místa na zásobníku pro lokální proměnné (ty, co jsou deklarovány uvnitř funkce).

No a podobně jako jména proměnných jsou ve skutečnosti jen adresy místa v paměti, tak stejně tak jména funkcí jsou jen adresy. A díky linkování (vytváření finálního spustitelného souboru) vlastně můžeš používat funkce z jiných knihoven - při linkování se tyto symbolická jména nahradí za konkrétní adresy v paměti, kde se nachází kód funkce.

Co se týče dotazu na jména argumentů, tak jak už bylo řečeno, překladač zajímá pouze velikost všech argumentů, aby věděl, kolik místa si má udělat na zásobníku (tam se před voláním funkce typicky vloží její argumenty a funkce k nim pak přistupuje). Pojmenování argumentu je opět pomoc pro programátory - překladač to vidí jako argument1, který je typu int - tedy zabírá 4 bajty a argument2.. a ať už si ho pojmenuješ jakkoli, stejně to bude jedno a to samé místo v paměti.

Jen doufám, že jsem aspoň trochu odpověděl a nemluvil od věci.. Už jsem docela unavený :D

Nahoru Odpovědět
23.2.2016 0:13
Chyba je mezi klávesnicí a židlí.
Avatar
coells
Tvůrce
Avatar
Odpovídá na Martin Chvátal
coells:23.2.2016 9:58

Když si vezmeš programovací jazyky podle toho, jak je složité pro ně napsat kompilátor, tak C++ s přehledem vede, zatím nikdo nevymyslel složitější jazyk z hlediska překladu. S tím souvisí věci jako je dlouhá doba překladu a některé prasárničky, které ještě víc komplikují život. Takže si autoři kdysi dávno řekli, že nejlepší bude, když každý soubor načtou pouze jednou a nesmí se vracet zpět ani přeskakovat dopředu. To je historická past, do které je C a C++ chycené.

Představ si program jako knihu, kterou čteš. Schopnost přelistovat pár stránek dopředu, aby ses podíval na další děj, se nazývá forward reference, většina moderních jazyků to dneska umí, ale C++ ne. Nebo alespoň předstírá, že to neumí, aby si zachovalo historickou tvář, tomu se říká backward compatibility.

Takže čteš knihu a jsi uprostřed děje, kde vystupuje záhadná postava F důležitá pro příběh. Nesmíš přelistovat pár stránek dopředu, aby ses o záhadné F dozvěděl víc. Takže abys věděl alespoň něco, uvedl autor na začátku knihy deklaraci, tím jí představil jménem a postava může v příběhu vystupovat, ostatní postavy se na ní mohou odkazovat a povídat si s ní. Až bude potřebovat vysvětlit, co je postava F zač, uvede autor její definici, kde všechno podstatné vysvětlí.

A to je vlastně všechno, deklarace funkce F říká, že existuje funkce jménem F a uvede způsob, jak si s ní podívat, jak ji zavolat. Na druhou stranu definice funkce už vysvětluje detaily toho, co se děje, když F zavoláš. A zdá se to být (nepochopitelně) komplikované, protože to vychází z historie jazyka a potřeb, které už dneska nejsou aktuální, ale jazyk si je táhne s sebou.

Akceptované řešení
+20 Zkušeností
+2,50 Kč
Řešení problému
 
Nahoru Odpovědět
23.2.2016 9:58
Avatar
Martin Chvátal:23.2.2016 16:09

už mi to začíná docházet lehce víc, časem se to podá :D, děkuju za pomoc :)

Nahoru Odpovědět
23.2.2016 16:09
Nothing is true, everything is permitted.
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 11 zpráv z 11.