Bu∩ pozdraven poutnφku, kdo v dobrΘm p°ichßzφÜ.
M∙₧ete p°isp∞t na
humanitßrnφ pomoc lidem, kde je hladomor, pokud kliknete na reklamu sponzor∙.
Tφm, ₧e kliknete, p°isp∞je sponzor na dodßvku potravin.
http://www.thehungersite.org/
Dßle na odkaz DONATE FREE FOOD.
M∙₧ete takΘ zve°ejnit tento server i na sv²ch WWW strßnkßch. D∞kuji.
Sta₧enφ tΘto strßnky pro lokßlnφ prohlφ₧enφ (i s p°φklady) v archivu ZIP (ze dne 7.1.2000) - Φasem jsou odstra≥ovßny p°eklepy, dopl≥ovßno podle p°ipomφnek Φtenß°∙, dφvejte se proto po aktußlnφ verzi na http://www-troja.fjfi.cvut.cz/~sokolovs/CPP.HTM
Moje poznßmka: Vlastnφ zkoumßnφ jazyka C++, kdy₧ jsem byl jeÜt∞ zaΦßteΦnφk... Stßhn∞te si archiv.
Poznßmka: Cφlem tΘto html strßnky je podat zßkladnφ "kurs" o programovßnφ v C++, proto₧e n∞kterß literatura je p°φliÜ drahß a takΘ velice rychle zestßrne.
Poznßmka: Informace a v²klad je tedy veden podle p°ednßÜek Programovßnφ v C++ na FJFI (to je Fakulta Jadernß a Fyzikßln∞ In₧en²rskß - to je zde, kde si nynφ prohlφ₧φte tuto WWW strßnku), pat°φcφ pod ╚VUT. P°ednßÜky p°ednßÜφ ing. M.Virius, Csc.
Äßdost: Pokud uvidφte jakoukoliv chybu, dejte mi prosφm v∞d∞t. ChybiΦka toti₧ pravd∞podobn∞ vznikla p°episem progrßmk∙ do WWW strßnky, na p°ednßÜce se chyby vyskytujφ jen z°φdka.
Poznßmka: OvÜem ₧ßdn² text se nevyrovnß p°ednßÜkßm Programovßnφ v C++ v Trojanov∞ 13 a praktick²ch ukßzkßch p°edvßd∞n²ch na poΦφtaΦi s mo₧nostφ promφtßnφ obrazu na tabuli Φi st∞nu.OvÜem p°edßÜky jsou nenahraditelnΘ, jeliko₧ se za₧ije n∞kdy i legrace, kdy₧ poΦφtaΦ Φi projektor nepracuje, tak jak by m∞l...
DoporuΦenß literatura: Nßzvy literatury ani jmΘna autor∙ - bez zßruky, formßt: nakladatelstvφ - autor - nßzev dφla
Naopak nedoporuΦenß literatura: Obsahuje toti₧ tolik chyb, ₧e kdo se z nφ bude uΦit, ...
Obsah:
DalÜφ (druhß Φßst) tΘto WWW strßnky
Nelze se nezasmßt :-) Programovacφ jazyk C se jmenuje C, proto₧e p°ed nφm byl programovacφ jazyk B (zkratka od B CPL - central programming language, pou₧φvßn pro Unix).
Poznßmka: C++ se oznaΦuje C++, proto₧e je nßsledovnφkem jazyka C a symbor ++ znamenß operßtor (viz dßle), kter² znamenß nßslednφka nebo dalÜφ mo₧nost (u cel²ch Φφsel znamenß zv∞tÜenφ o jedniΦku).
Co tvo°φ dneÜnφ jazyk C++?
1,2,3 = neobjektovΘ C++
1 a₧ 8 = dneÜnφ C++
èablony: Pou₧φvajφ se, pokud naznßme p°edem typ, jako univerzßlnφ konstrukce pro r∙znΘ typy prom∞nn²ch, ale pro stejn² algoritmus (nap°. zßsobnφk - pro uklßdßnφ r∙zn²ch typ∙ prom∞nn²ch - v₧dy jsou operace toto₧nΘ, pouze se liÜφ typem prom∞nnΘ se kterou se pracuje; t°φd∞nφ - zde takΘ m∙₧eme naprogramovat stejn² algoritmus, kter² m∙₧eme pou₧φt pro t°φd∞nφ r∙zn²m typ∙ prom∞nn²ch, kde je definovßn operßtor porovnßvßnφ).
Vyjφmky: Pou₧φvßjφ se v p°φpad∞, ₧e je n∞jakß Φßst programu (nap°. funkce) p°eruÜena n∞jakou chybou, tak₧e je mo₧nΘ potom zadat, jak se bude na nφ reagovat resp. kam se mß p°edat °φzenφ, jeliko₧ funkce se nem∙₧e normßln∞ ukonΦit, ale musφ dßt v∞d∞t tomu, kdo ji zavolal, nap°. tφm, ₧e se tato vyjφmka n∞jak oÜet°φ - n∞kam se skoΦφ.
Historie:
Asi roku 1972 navrhli Ritchie a T(h)omson jazyk C.
Ritchie + Kerningham - Programming language C
Roku 1990 - poprvΘ navr₧ena norma ANSI C
C++ - poprvΘ navrhl a definoval Bjarne Stroustrup
R∙znΘ C FRONT 1, 1.1, 2.0, 2.1
P°ekladaΦe od r∙zn²ch firem: Borland, MS (nenφ MakroSoft, ale Microsoft), Watcom, GNU
Borland - Turbo C++, pro Dos verze: 1.0, 2.0, 3.0, 3.1
Borland - Verze 3.1 je nejlepÜφ verze C++ pro DOS
Borland - DalÜφ verze jsou pro Wokna (sluÜn∞ °eΦeno MS Windows, kde MS nenφ MakroSoft), ale Microsoft - verze: 4.01, a₧ 4.5, 5.0
MS verze: 2.0 je vlastn∞ C, 7.0 je C++ (asi jako Borland 3.1), dßle jsou verze pro Wokna oznaΦenß Visual C++ 1.0 (pro Windows 3.1),2.0 (u₧ umφ p°elo₧it i jako 32bit. aplikaci), ... 5.0 (pro Win95)
Watcom verze: 10.5 a 11, OPTIMA++, p°eklad pro r∙znΘ platformy, DOS, Win, OS2, i pro Novell, POWER++
Poznßmka: Hlavnφ program je funkce, kterß v DOSu vracφ Φφslo, kterΘ odpovφdß chybovΘmu k≤du DOSu - prom∞nnß DOSu ERRORLEVEL. Proto je dobrΘ definovat hlavnφ program jako funkci main() vracejφcφ chybov² k≤d. Chybov² k≤d se zde vracφ p°φkazem return, za kter²m se napφÜe vracenß hodnota. Lze napsat i bez hodnoty za p°φkazem ruturn, v takovΘm p°φpad∞ pouze ukonΦφ program (pokud je p°φkaz return ve funkci main), ale nelze s urΦitostφ °φci jak² bude chybov² k≤d (v∞tÜinou 0, ale nelze se na to spolehnout).
Varovßnφ a poznßmka: Programovacφ jazyk C++ rozliÜuje velkß a malß pφsmena. P°ednßÜky jsou uskuteΦnovßny na p°ekladaΦi Borland C++ 3.1, p°ekladaΦi pro DOS. V jin²ch p°ekladaΦφch m∙₧e b²t situace trochu odliÜnß. Tak₧e, pokud mφsto main() napφÜete nap°. Main(), kompilßtor ohlßsφ chybu, jako by funkce neexistovala. P°i startu programu se p°edß °φzenφ procedu°e main(). Procedure main() smφ b²t maximßln∞ jedna. Aby program odstartovan, musφ b²t prßv∞ jedna, nesmφ jich b²t tedy vφc a musφ existovat alespo≥ 1 funkce main(). Zßvorky () jsou d∙le₧itΘ, nebo¥ tak dßte p°ekladaΦi najevo, ₧e definujete funkci a ne pouze prom∞nnou.
Poznßmka: Za v∞tÜinou deklaracemi se pφÜe st°ednφk. Na vyjφmky Vßs upozornφ tato WWW strßnka nebo chyba p°i p°ekladu nebo Üpatnß funkΦnost programu.
VÜeobecnΘ poznßmky - zdvojovßnφ \\: Dßle se nesmφ Üet°it znaky \, pokud je v nßzvu adresß°ovΘ cesty, proto₧e znak \ C++ bere jako specißlnφ znak, tak₧e je nutnΘ ho zdvojit, potom bude brßt \\ skuteΦn∞ jako \. DoporuΦujeme si vyzkouÜet p°i prßci se soubory (viz dßle), kdy₧ v adresß°ovΘ cest∞ pou₧ijete mφsto \\ pouze jeden znak \ (Φastß chyby zaΦßteΦnφk∙), abyste se vyvyrovali p°φpadn²ch budoucφch nadßvek, proΦ ten program nem∙₧e najφt adresß° Φi soubor.
Poznßmka: Dßle je dobrΘ neÜet°it zßvorkami () nap°. u maker s parametry (viz dßle). Dßle je dobrΘ neÜet°it znakem = p°i porovnßvßnφ (zde je nutnΘ psßt ==), proto₧e jedno = se pou₧φvß pro p°i°azenφ, == pro porovnßvßnφ (je to n∞kdy chyby t∞ch, kte°φ znajφ Pascal, ne₧ si zvyknou na C++).
ZAKLADNI.CPP: Zßkladnφ program v C++ resp.C
Popis zßkladnφho programu ZAKLADNI.CPP:
Nynφ progrßmek zkomplikujeme a naprogramujeme v²poΦet faktorialu z celΘho p°irozenΘho Φφsla.
FAKTORIA.CPP: Zde je program na v²poΦet faktorißlu, jak jsem slφbil.
Popis programu na v²poΦet faktorißlu FAKTORIA.CPP:
P°φklad na pou₧itφ operßtor∙ ++ pro zv²Üenφ prom∞nnΘ a -- pro snφ₧enφ demonstruje TEST.CPP
Popis programu TEST.CPP:
Uvedeme si, co to je projekt. P°edem se omlouvßm, pokud by definice nevystihovala to, co skuteΦn∞ projekt je. Pokusφm se to naznaΦit v nßsledujφcφ poznßmce.
Definice: Projekt je urΦitß skupina menÜφch programk∙, kterΘ dßvajφ dohromady jeden velk² program.
Poznßmka: Ji₧ mohou b²t p°elo₧eny nebo se p°elo₧φ vÜechny najednou. Situace se s v²hodou pou₧φvß u velk²ch program∙, na kterΘm pracuje n∞kolik programßtor∙, kte°φ se dohodnou na n∞jakΘm rozhranφ a ka₧d² naprogramuje urΦitou Φßst programu (nap°. je to soubor, kter² je p°ekladaΦem p°elo₧en do souboru s koncovkou obj) a potom (po naprogramovßnφ hlavnφho programu), v C++ je to funkce main() se linkerem spojφ vechny soubory do jednoho spustitelnΘho programu (s koncovkou exe nebo com).
Zadßnφ projektu: V menu Project (verze Borland C++ 3.1) zvolφme polo₧ku Open project... Potom v dialogu zadßme nebo vybereme jmΘno projektu (s koncovkou prj), pokud soubor neexistuje, potom se vytvo°φ. V stejnΘm menu polo₧kou Close project m∙₧eme project zav°φt a polo₧kou Add item... m∙₧eme vybrat soubory, pat°φcφ do naÜeho projektu.
Poznßmka: Pokud te∩ budeme n∞co p°eklßdat, potom se bude p°eklßdat projekt. V projektu mohou b²t ji₧ p°elo₧enΘ programy *.obj (nap°. z C, C++, Assembleru). Aby to vÜechno Ülo spojit linkerem, musφ b²t prßv∞ jedna procedura main(). Pozor! Jak jsme ji₧ zd∙raznili, C++ rozliÜuje malß a velkß pφsmena, tak proto se mo₧nß stane, ₧e n∞kterou funkci linker nenalezne, proto₧e jsou rozliÜovßny malΘ a velkΘ pφsmena. Krom∞ toho C++ komolφ jmΘna vÜech funkcφ, tak₧e n∞kdy pou₧φvat n∞co co vytvo°ilo C++ je dosti obtφ₧nΘ, ale prakticky se ukazuje, ₧e to n∞kdy jde... Linker v C++ asi bez problΘm∙ sestavφ obj soubory, kterΘ vytvo°il kompilßtor C++.
V²hody projekt∙: Ka₧dou Φßst programu lze p°elo₧it zvlßÜ¥, v jin² okam₧ik, jin²m programßtorem. Pokud nenφ k dispozici k obj souboru jeho zdrojov² program, m∞l by b²t k dispozici alesp≥ hlaviΦkov² soubor, obsahujφcφ ty funkce resp. i prom∞nnΘ, typy apod., co lze pou₧φvat v ostatnφch programech. V takovΘm p°φpad∞ do objektu vlo₧φte obj soubor a program, ve kterΘm budete chtφt pou₧φvat slu₧by obj souboru. Do toho programu pomocφ include naΦtete z hlaviΦkovΘho souboru hlaviΦky funkcφ, pokud je po vlo₧enφ do projektu linker neuvidφ a bude hlßsit chyby. Pou₧itφ projekt∙ vÜeobecn∞ urychluje p°eklad, proto₧e se n∞kterΘ Φßsti programu ji₧ nemusφ p°eklßdat.
Nev²hody projekt∙: N∞kdy hlßsφ zbyteΦnΘ chyby, kterΘ by se nehlßsily, kdyby se vÜechno dalo do jedinΘho souboru (v tomto p°φpad∞ lze dost dob°e zjistit, co je deklarovßno dvakrßt, ne₧ prohledßvßnφ n∞kolika soubor∙ - doporuΦuji pou₧φvat program Grep pro vyhledßvßnφ textov²ch Φßstech ve zvolen²ch souborech). Toto platφ hlavn∞ v p°φpad∞ sestavovßnφ, pokud linker najde n∞co dvakrßt. U projektu se zastavφme jeÜt∞ n∞kdy podrobn∞, ukß₧eme si to na p°φkladech. Ale zatφm si musφme vysv∞t n∞co jinΘho.
Poznßmka: Zatφm to nemß znamenat v²uku objektovΘho programovßnφ v C++, ale nßsledujφcφ program ma n∞co °φci o mo₧nostech C++ a v²hodßch (resp. nav²hodßch) pou₧φvßnφ objekt∙, kterΘ budou patrnΘ z programu.
Poznßmka: Prßce s komplexnφmi Φφsly je mo₧nΘ naprogramovat i neobjektov∞, je mo₧nΘ naprogramovat i v Pascalu, zde ovÜem p°ichßzφme ji₧ o mo₧nost psßt prßci s komplexnφmi Φφsly pomocφ operacφ +,-,*,/ jako v matematice. C++ ji₧ umo₧≥uje tzv. p°etφ₧enφ operßtor∙, mezi nimi₧ pat°φ +,-,*,/ (ale i jinΘ), co₧ umo₧≥uje p°i°adit novou funkci, kterß bude "poΦφtat" sΦφtßnφ, odΦφtßnφ, nßsobenφ, d∞lenφ.
Poznßmka: Nynφ budu doslovn∞ citovat v p°ednßÜky v C++ (ing. M.Virius, CSc.): "Mo₧nost p°etφ₧enφ operßtor∙ m∙₧e zdrojov² text programu zp°ehlednit, ale takΘ m∙₧e znep°ehlednit (pokud se p°edefinuje operßtor + nap°. pro nßsobenφ, - pro sΦφtßnφ apod)."
Poznßmka: Lze p°edefinovat operßtor v²stupu do v²stupnφho proudu >> (pou₧φvß se u cout), ale takΘ nßm zatφm neznßm² operßtor << pro Φtenφ ze vstupnφho proudu (pou₧φvß se u cin - Φtenφ z p°esm∞rovatelnΘho vstupu a ulo₧enφ do zadanΘ prom∞nnΘ). Tφmto zp∙sobem ukß₧eme p°edefinovßnφ t∞chto operßtor∙ pro prßci s komplexnφmi Φφsly.
Pou₧itφ prßce s komplexnφmi Φφsly a zlomky. Hlavnφ program, kter² to vÜechno pou₧φvß.
Nezapom∞≥te si stßhnout hlaviΦkovΘ soubory ZLOMKY.H a COMPLEX.H !!! Bez nich progrßmek ·sp∞Ün∞ nep°elo₧φte.
Moje poznßmka - jak to p°elo₧it? V C++ n∞kdy b²vß ·sp∞Ün∞ p°elo₧en² program zßzrak. Proto nelze s jistotou °φci, ₧e po dodr₧enφ tohoto postupu a podmφnek, program p∙jde p°elo₧it. Ukß₧eme si to v Borland C++ 3.1:
Popis programu:
POUZITI.CPP je hlavnφ program (tedy obsahuje funkci main()), pou₧φvß prßci s komplexnφmi Φφsly COMPLEX.CPP (to je n∞co jako jednotka - unit v Pascalu, m∙₧e b²t p°elo₧ena do complex.obj souboru a p°ilo₧en hlaviΦkov² soubor complex.h nebo kompletnφ zdrojov² text vÜetn∞ hlaviΦek i implementace funkcφ a vÜech dat). COMPLEX.CPP pracuje se zlomky, pou₧φvß tedy ZLOMKY.CPP (u zde bychom mohli p°elo₧it do ZLOMKY.OBJ a dodat pouze hlaviΦkov² soubor ZLOMKY.H, tak₧e nemusφme dßt kompletnφ zdrojov² text vΦetn∞ implementace t∞l funkcφ a dat, ostatnφ programßto°i by tomu stejn∞ nerozum∞li :-) nebo by se sna₧ili tomu porozum∞t).
DalÜφ povφdßnφ o objektech bude v kapitole Objektov∞ orientovanΘ programovßnφ. Zde se blφ₧e seznßmφme s prßci, deklaracφ objekt∙ (vlastn∞ je to vid∞t z uveden²ch p°φklad∙). Lze vlastn∞ pou₧φt struct nebo class
Pou₧itφ p°istupov²ch prßv:
soukromΘ - private - jako soukromΘ se v∞tÜinou definujφ atributy (tedy data objektu - t°φdy) a n∞kterΘ metody, kterΘ je zakßzßno pou₧φvat mimo objekt. SoukromΘ je vÜechno to, k Φemu je zakßzßn p°φstup z vn∞jÜku.
p°ßtelskΘ - friend - specifikuje nßzev funkce, kterß m∙₧e pracovat se soukrom²mi prvky objektu.
ve°ejnΘ - public - lze pou₧φvat i z vn∞jÜku. VhodnΘ je to u metod, aby se daly pou₧φt i z jin²ch Φßstφ program∙. NedoporuΦuje se definovat atributy jako ve°ejnΘ, proto₧e ty data m∙₧e kdokoliv kdykoliv m∞nit. Pro prßci s atributy - daty t°φdy jsou urΦeny metody, aby kontrolovaly p°φstup a zm∞nu sv²ch dat, takto lze zabrßnit necht∞nnΘ zm∞n∞ dat nap°. na nesmyslnou hodnotu.
Konstruktor a destruktor
konstruktor se v₧dy jmenuje stejn∞ jako nßzev objektu a m∙₧e mφt parametry, nic nevracφ
destuktor se v₧dy jmenuje ~jmeno kde jmeno je jmΘno objektu, nesmφ mφt parametry, nic nevracφ
konstruktor se volß deklaracφ p°φsluÜnΘho objektu, stejn∞ jako u deklarace normßlnφ prom∞nnΘ - zde se takΘ volß standartnφ konstruktor, kter² vytvo°φ prom∞nnou p°φp. nastavφ n∞jakou hodnotu
destruktor se volß p°i zßniku instance
pokud se u funkce neuvede ₧ßdn² typ prom∞nnΘ, pouze jejφ nßzev, p°edpoklßdß p°ekladaΦ prom∞nnou typu int
Pascal: |
C++: |
V²znam p°φkazu: |
write;writeln |
cout;printf |
Tisk na obrazovku (standartnφ v²stupnφ za°φzenφ) |
read;readln |
cin;scanf |
╚tenφ z klßvesnice (standartnφ vstupnφ za°φzenφ) |
begin p°φkaz resp. p°φkazyend; |
{ p°φkaz resp. p°φkazy} |
Slo₧en² p°φkaz, kter² obsahuje 1 nebo vφce p°φkaz∙, resp. blok p°φkaz∙ |
exit |
return |
UkonΦenφ aktußlnφho bloku (v hlavnφm programu znamenß konec programu) |
halt |
exit |
UkonΦenφ programu - v₧dy |
if podmφnka then p°φkaz1else p°φkaz2 |
if (podmφnka)p°φkaz1 else p°φkaz2 |
Pokud je spln∞na podmφnka, provede se p°φkaz1, jinak p°φkaz2 |
case v²raz of k1: p°φkaz1 k2: p°φkaz2 kn: p°φkazn else p°φkaz0 end; |
switch (v²raz) { case k1: {p°φkaz1;break;} case k2: {p°φkaz2;break;} case kn: {p°φkazn;break;} default: {p°φkaz0;break;} } |
V∞tvenφ podle uveden²ch mo₧nostφ k1,k2,...,kn. V p°φpad∞, ₧e v²raz nabyde jednΘ z t∞chto mo₧nostφ, potom se provede p°φkaz za dvojteΦkou. Pokud v²raz nenabyde ₧ßdnΘ hodnoty, potom, pokud je uvedeno else resp. default, potom se provede nßsledujφcφ p°φkaz za else resp. default |
while podmφnka do begin áá p°φkazy end; |
while (podmφnka) { á p°φkazy } |
jednß se cyklus, tj. opakujφ se p°φkazy mezi begin a end resp mezi { a }, tak dlouho, dokud je spln∞na podmφnka. Ti, kte°φ znajφ Pascal, pro ty to nenφ nic novΘho. |
Dßle existuje cyklus do { p°φkazy } while (podmφnka), kter² by p°i znegovanΘ podmφnce odpovφdat cyklu repeat p°φkazy end; Tento cyklus (p°φkazy mezi repeat a until) se v Pascalu provßd∞l dokud nenφ spln∞na podmφnka, ale cykluv v C++ do while se provßdφ p°i spln∞nΘ podmφnce, tak dlouho, dokud je podmφnka spln∞na.
Poznßmka: Pozor! Nezapome≥te v p°φkazu v∞tvenφ v C++ p°φkaz break, kter² je velmi d∙le₧it². ProΦ? Zkuste si to sami vyzkouÜet, co se stane... To si pak lΘpe zapamatujete a vyvarujete se p°φpadnΘ chyby, ne₧ kdy₧ si te∩ budete sna₧it zapamatovat d∙vod. V p°φpad∞, ₧e si nebudete v∞d∞t rady, m∙₧ete mi poslat E-MAIL a jß Vßm zp∞t poÜlu vysv∞tlenφ.
Poznßmka: Ti, co navÜt∞vovali na FJFI kurs Pascalu, jist∞ umφ vyvolat nßpov∞du (a tak si mohou snadno obejφt i bez tΘto WWW strßnky), naprogramovat pr∙m∞rn∞ slo₧it² program, odhadnout jeho slo₧itost (Φasovou i pam∞¥ovou - viz Zßklady algoritmizace - ing. M.Virius, CSc.) a jist∞ Φφst anglicky psan² help v Turbo Vision.
P°φkaz printf se pou₧φvß pro v²stup dat na standartnφ v²stupnφ za°φzenφ (monitor). P°ikaz scanf se pou₧φvß pro vstup dat ze standartnφho vstupnφho za°φzenφ (klßvesnice). Jako data m∙₧e b²t u printf i °et∞zec, kter² se mß vytisknout, u obou (printf i scanf) m∙₧e b²t prom∞nnß, v tom p°φpad∞ s vypφÜe jejφ obsah resp. do nφ se ulo₧φ to co se p°eΦte z klßvesnice. Funkce fungujφ skoro stejn∞ jako write a read v Pascalu, s tφm rozdφlem, ₧e zde navφc m∙₧e b²t tzv. formßtovacφ °et∞zec.
Ve formßtovacφm °et∞zci m∙₧e b²t (ten se zapisuje do "....."), parametry se odd∞lujφ Φßrkou, tak jak bychom to m∞li znßt z Pascalu.
Od°ßdkovßnφ - vlo₧φme \n - nov² °ßdek
Upozor≥ujeme, ₧e se zde pou₧φvß v²pistka (viz dßle), tak₧e poΦet ani typ parametr∙ se nekontroluje, co₧ m∙₧eme mφt nep°edvφdatelnΘ d∙sledky pokud programßtor nevφ co chce nebo pokud napφÜe Üpatn² typ (jde o to, ₧e p°i zadßnφ %f poΦφtaΦ oΦekßvß nßzev prom∞nnΘ typu double, ale pokud zadßme nap°. celoΦφselnou, bude bu∩ Φßst pam∞ti p°epsßna (u scanf) nebo vypsßn nesmysl (u printf)
Poznßmka: Pokud zvolφme v option a zaÜkrtneme polφΦko precompile headers, potom si kompilßtor vytvo°φ soubory *.SYM, do kterΘ si (jak u₧ anglick² nßzev napovφdß) uklßdß hlaviΦky funkcφ, co₧ mß v²hodu, ₧e to urychlφ p°eklad, ale na druhou stranu to zabφrß mφsto na disku. Uklßdß se to adresß°e nastavenΘho jako output directory.
P°eklad programu: P°eklad programu probφhß pobφhß zleva doprava na °ßdku, potom se skoΦφ na dalÜφ °ßdek a₧ do konce souboru, pop°. se naΦφtajφ jinΘ soubory, na kterΘ se pou₧ije kompilßtor stejn² postup, pokud se jednß o zdrov² text nebo hlaviΦkov² soubor.
Poznßmka - token: Token je polo₧ka, na kterΘ se kompilßtor sna₧φ rozlo₧it text. Vstupem je tedy soubor s textem, ze kterΘho se za normßlnφch okolnostφ odstanφ p°ebyteΦnΘ mezery (pokud to nenφ °et∞zec) a hledß se v₧dy nejdelÜφ polo₧ka, kterß mß smysl p°i postupu zleva doprava (tak v∞tÜina programßtor∙ takΘ pφÜe a zezhora dol∙).
Druhy token - polo₧ek v C++:
V²znßm jednotliv²ch nßzv∙ by m∞l Φtenß° znßt z Pascalu nebo z jinΘho programovacφho jazyka.
Pod identifikßtorem m∙₧e b²t jmΘno procedury resp. funkce (v C++ jsou stejn∞ vÜechno funkce) nebo jmΘno prom∞nnΘ Φi slou₧it∞jÜφ datovΘ struktury.
Konstanta m∙₧e b²t Φφselnß (celß, reßlnß, potom v r∙zn²ch soustavßch - desφtkovß, Üestnßctkovß a osmiΦkovß). A co dvojkovß? (To jsem se dovolil zeptat...) P°ednßÜejφcφ mi °ekl, ₧e C++ standartn∞ neumo₧≥uje dvojkovou soustavu, ale ₧e bych si to musel naprogramovat, k Φemu se snad dostanu a napφÜu to zde, pokud se to ovÜem nauΦφm...
╪et∞zec je vlastn∞ konstanta, je to ovÜem spφÜe pole znak∙ a v C++ se °et∞zec v₧dy kontvertuje na adresu ukazujφcφ na 1. prvek. Narozdφl od Pascalu je zde vyjφmka: Prvnφ znak v °et∞zci neznamenß jeho dΘlku, ale 1. znak je skuteΦn∞ prvnφ znak °et∞zce. ╪et∞zec se ukonΦuje znakem s ASCII k≤dem 0. Pokud normßln∞ °et∞zce zapisujeme - p°i vstupu a v²stupu a ve v∞tÜin∞ p°i°azovacφch p°φkaz∙, poΦφtaΦ ji za nßs nakonec v∞tÜinou p°idß, pokud je mu jasnΘ, jak je °et∞zec dlouh².
KlφΦovΘ slovo se v∞tÜinou v Borland C++ zobrazuje bφle, aby se odliÜilo od jin²ch tokens. KlφΦov²m slovem je standartnφ p°φkaz v C resp. C++. Nap°φklad v∞tvenφ, cyklus, podmφnka, apod.
Komentß° se zapisuje za znaky // V tomto p°φpad∞ se bere komentß° a₧ do konce °ßdky. Nebo druh² druh je /* kter² platφ a₧ do */
Operßtor je v∞tÜinou n∞co jako matematickß operace (ne fuknce), nap°. matematickΘ: + - * / ( ) pro porovnßvßnφ: < > <= >= != == = zv²Üenφ resp. snφ₧enφ o 1: ++ -- kombinace p°iΦφtßnφ, odΦφtßnφ, nßsobenφ a d∞lenφ, pokud je v²stup a 1. operand stejnß prom∞nnß: += -= *= /= a dalÜφ specißlnφ nap°. p°i prßci s objekty. N∞kterΘ operßtory lze p°etφ₧it a p°edefinovat jejich v²znam pro sv∙j vlastnφ typ. Tak je mo₧nΘ zapsat sΦφtßnφ komplexnφch Φφsel pomocφ operßtoru +, jak jsme si na zaΦßtku ukßzali v p°φkladu komplexnφch Φφsel. Operßtory + - mohou b²t binßrnφ (dva operßtory) - p°i sΦφtßnφ resp. odΦφtßnφ nebo unßrnφ - jako znamΘnko Φφsla.
V C++ je vlastn∞ program posloupnost deklaracφ. Nebo¥ i volßnφ funkce je vlastn∞ deklarace, kterß se p°elo₧φ jako skok nebo volßnφ p°eruÜenφ.
╚φslo v desφtkovΘ soustav∞ se zapisuje zcela normßln∞ a p°irozen∞: 123, -123, +123, 5.123, 5E+11, 5e+11
╚φslo v ÜestnßctkovΘ soustav∞ se zapisuje jako 0xΦφslo, kde Φφslo se sklßdß z Φφslic 0123456789ABCDEF
╚φslo v osmiΦkovΘ soustav∞ se zapisuje jako 0Φφslo, co₧ m∙₧e u zaΦßteΦnφk∙ b²t docela problematickΘ, nebo¥ se to dß splΘst s Φφslem v desφtkovΘ soustav∞. Pouze pokud na zaΦßtku uvedeme 0Φφslo, bere se to jako v osmiΦkovΘ soustav∞
╚φslo 0.125 je brßno jako Φφslo v desφtkovΘ soustav∞ (ne v osmiΦkovΘ - to je vyjφmka) nebo¥ si nedokß₧u p°edstavit blßzna, kter² by si p°ßl poΦφtat v osmiΦkovΘ resp. ÜestnßctkovΘ soustav∞ s desetinn²mi Φφsly.
Konstanty mohou b²t podle dΘlky t∞chto typ∙:
V p°φpad∞ jinΘ konstanty se p°evßdφ konstanta na 1 z t∞chto 6 pro∞nn²ch v p°φpad∞ Φφsla. V p°φpad∞ °et∞zce Φi znaku z∙stane °et∞zec °et∞zcem a znak znakem.
N∞kdy je nutnΘ p°edepsat, zda se mß Φφslo brßt jako urΦit² typ: 0U - znamenß 0 jako unsigned, 0L znamenß long a 0LU unsigned long
Znak se zapisuje 'a'
╪et∞zec se zapisuje "text"
Specißlnφ ascii znak lze zapsat:
a&b bitovß operace and - logick² souΦin
a|b bitovß operace or - logick² souΦet
a^b bitovß operace xor - nonekvivalence
~a bitovß negace
a<<1 bitov² posun o 1 bit doleva (Φφslo urΦuje o kolik bit∙ se Φφslo posune)
a>>1 bitov² posun o 1 bit doprava (Φφslo urΦuje o kolik bit∙ se Φφslo posune)
! logickß negace: !a - pokud je prom∞nnß nenulovß, vracφ nulu a pokud a je nulovß, vracφ 1(v Pascalu byla prom∞nnß Bolean, kterß mohla mφt pouze hodnoty 0 a 1, ale ostatnφ hodnoty byly nep°φstupnΘ, zde v C++ je i 2,3 -jakßkoliv nenulovß hodnota brßna jako logickß 1)
&& logickß konjunkce (and) - logick² souΦin
|| logickΘ or - logick² souΦet
Poznßmka: Nezam∞≥ovat || s | a && s |. Zatφmco || a && sjou logickΘ or a and (pou₧itφ v podmφnce), | a & jsou bitovΘ or a and (prßce s vφce bity jako Φφsly v jednΘ prom∞nnΘ - nap°. maskovßnφ, analogickΘ instrukcφm OR a AND v Assembleru).
% operace modulo (viz mod v Pascalu), tedy zbytek po celoΦφselnΘm d∞lenφ
/ operace d∞lenφ (stejnΘ jako v Pascalu), Φφslo se zaokrouhluje (pokud je pou₧ito pro celoΦφselnΘ prom∞nnΘ je to analogickΘ div v Pascalu)
+ - * ( ) normßlnφ sΦφtßnφ, odΦφtßnφ (binßrnφ se dv∞my prom∞nn²mi se znakem uprost°ed - vÜe normßlnφ) nebo znamΘnko ΦφselnΘ prom∞nnΘ (unßrnφ - p°ed prom∞nnou), * je v₧dy nßsobenφ (asi nemß logick² smysl unßrnφ *???) a je v₧dy binßrnφ, / je d∞lenφ (takΘ asi nemß smysl unßrnφ /???)
ReßlnΘ prom∞nnΘ:float (4B) <= double (8B) <= long double (10B) Rozsahy prom∞nn²ch zßle₧φ na OS a verzi programovacφho jazyka C, C++. doble = long float
P°etypovßnφ: Jednß se o um∞lou zm∞nu typu prom∞nn²ch. Normßln∞ se provßd∞jφ automatickΘ konverze prom∞nn²ch v p°i°azenφ, pokud jsou nßzvy prom∞nn²ch r∙zn²ch typ∙, ale n∞kdy nenφ mo₧nß. Pokud ovÜem nutn∞ chceme °φci, ₧e prom∞nnß x musφ b²t nutn∞ typu int, zapφÜeme (int)x nebo int(x) (d∙vodem m∙₧e b²t nap°. chybnß funkce programu nebo chyba p°i programu).
Rozsahy jednotliv²ch prom∞nn²ch jsou:
shord <= int <= long
unsignedshor <= unsigned <= unsigned int
Znakovß prom∞nnß se deklaruje char, unsigned char, signed char, kde unsigned char je znak bez znamΘnka 0..255 a signed char je znak se znamΘnkem -128..+127 Nezapomn∞l jsem se zeptat, jak² logick² smysl mßjφ znamΘnkovΘ a neznamΘnkovΘ znaky. Bylo mi °eΦeno, ₧e C++ chce u₧ivateli umo₧nit vyu₧φt vÜech schopnostφ operaΦnφho systΘmu, tak₧e kdy₧ to umo₧≥uje operaΦnφ systΘm, C++ nechce u₧ivatele o tuto mo₧nost obφrat a tedy to zßle₧φ jen na u₧ivateli, co s tφm bude d∞lat. A jeÜti jsem byl varovßn, ₧e se s tφm dß nad∞lat p∞kn² nepo°ßdek...
Rozsahy jednotliv²ch prom∞nn²ch int a short zßvisφ na DOSu a zda pou₧φvßme 16-ti resp. 32-ti bitov² p°ekladaΦ C++. Na 16-ti bit. v normßlnφm DOSu je int = short -32767 .. +32767 u znamΘnkovΘ signed int a 0..65535 u neznamΘnkovΘho unsigned int. V p°φpad∞ long celoΦφseln²ch 32-ti bitov²ch je rozsah -2mld .. +2mld resp. 0 .. 4mld, mld jsou miliardy.
Operßtory: Zopakujeme si: < (menÜφ) > (v∞tÜφ) <= (menÜφ nebo rovno) >= (v∞tÜφ nebo rovno) = (p°i°azenφ) == (testovßnφ rovnosti) != (nerovnost), + (sΦφtßnφ nebo znamΘnko Φφsla), - (odΦφtßnφ nebo znamΘnko Φφsla), * (nßsobenφ), / (d∞lenφ), && (logickΘ and), || (logickΘ or), ! (logickß negace). Existuje pr² a₧ 55 operßtor∙ s 16 r∙zn²mi prioritami. Pro ty, kte°φ neznajφ jejich prioritu, je lepÜφ, kdy₧ budete pou₧φvat zßvorky () - ty majφ nejvyÜÜφ a tedy majφ p°ednost p°ed ostatnφmi. Proto se zßvorkami neÜet°ete.
Konverze: Pokud uvedeme r∙znΘ prom∞nnΘ, hledß se (pokud je to ovÜem logickΘ) spoleΦn² typ. char->int, short->int nebo unsigned. Po p°evedenφ se objevujφ jen typy prom∞nn²ch: int, unsigned, long, unsigned long, float, double, long doule. VÜe to jsou ΦφselnΘ prom∞nnΘ, rozsahy najdete poΦet Byt∙, kterΘ zabφrajφ v pam∞ti, najdete v nßpov∞d∞, proto₧e se to liÜφ verzφ p°ekladaΦe a takΘ OS, pod kter²m mßte C, C++. V²ΦtovΘ typy a bitovΘ pole se takΘ konvertujφ na celoΦφselnΘ prom∞nnΘ (int - bez zßruky, zßle₧φ to na verzi a OS).
int -1 se v DOSu (BC 3.1) bere jako 16-ti bitovΘ a zapφÜe se jako FFFFH
long -1 se zapφÜe jako FFFFFFFFH
Pozor: Pokud je to jen mo₧nΘ, je dobrΘ spolu nemφchat znamΘnkovΘ a neznamΘnkovΘ znaky. Pozor mßme definovanΘ funkce void f(char) a void f(int). Pokud napφÜeme f(c) (kde c je znak), volß se funkce f(char), ale pokud napφÜeme f(+c), volß se potom druhß funkce f(int).
Rozdφly u znakov²ch prom∞nn²ch mezi C a C++: Konstanta - znak nap°. 'Φ' se v C bere jako int (C nejspφÜ char neznß), ale v C++ se bere skuteΦn∞ jako char. Z toho vypl²vß nebezpeΦφ - viz dalÜφ poznßmka.
Poznßmka: Funkce getchar() z hlaviΦkovΘho souboru <stdio.h> vracφ int. To p°edstavuje n∞kdy nebezpeΦφ, pokud uklßdßme v²stup do prom∞nnΘ jinΘho typu. Podobn∞ i vÜechny funkce v C++, kterΘ provßd∞jφ Φtenφ znaku, vracφ int!
MatematickΘ funkce: <math.h> Informace o funkcφch lze najφt v nßpov∞d∞.
DemonstarΦnφ progrßmek pracujφcφ s funkcemi.
Poznßmka pro studenty FJFI - ╚VUT:
V Trojance na sφti (pro zaΦßteΦnφky, kte°φ by si p°ßli studovat na FJFI uvßdφm, ₧e je to server katedry matematiky) jsou p°φklady vÜeobecn∞ p°φstupnΘ vÜem, kte°φ majφ na serveru katedry matematiky u₧ivatelskΘ konto. Ing. M. Virius zde mß ve°ejn∞ p°φstupnΘ p°φklady program∙ v C++ a v Pascalu. Prvßcφ, kte°φ mohou b²t v Trojance na sφti maximßln∞ 10 minut (to¥ novΘ opat°enφ katedry matematiky, aby prvßci netrßvili Φas na poΦφtaΦi a mφsto toho se v∞novali matematice) uvφtajφ radu, jak se globßln∞ napφchnout - tedy p°ipojit na server katedry matematiky a tak si prohlΘdnout p°φklady v C++ resp. Pascalu:
Server katedry matematiky: ftp://tjn.fjfi.cvut.cz
Username: jmΘno.prvaci. resp. jmΘno.studenti. nezapomφnejte teΦku na konci! Bez nφ budete jako odpojeni od serveru katedry matematiky.
Password: vaÜe heslo
P°i°adφ vßm implicitn∞ jako pracovnφ adresß° VßÜ home adresß°, kter² mßte na kated°e matematiky.
Potom budete asi dlouho hledat adresß° p°ednßÜejφcφho ing. M. Virius, CSc., proto₧e Φasto se p°emis¥uje, ·pravami na serveru.
Podle novΘ informace by to m∞lo b²t asi zde: KM1\DATA:\HOME\M64\VIR\VYUKA. Po nalogovßnφ na tjn.fjfi.cvut.cz se ocitnete asi n∞kde v adresß°i KM1\DATA:\n∞jak² adresß°... V p°φpad∞ lokßlnφho p°ilogovßnφ m∙₧ete pou₧φt p°φkaz map. Jinak pomocφ FTP se tam taky snad n∞jak dostanete.
Uvnit° bloku C nepoklßdß deklaraci za p°φkaz.
{
deklarace;
p°φkazy;
}
V C++ je deklarace = p°φkaz. Lze tedy psßt skoro libovoln∞, deklarace mezi p°φkazy, pouze musφ b²t zachovßno pravidlo: Nejd°φv deklarovat, potom pou₧φt. P°ekladaΦ postupuje naprosto logicky - tedy zezhora dol∙. To snad nenφ neobvyklΘ...? V∞tÜina programßtor∙ pφÜe program takΘ zezhora dol∙. DalÜφ pravidlo znφ: Deklarovat prom∞nnou a₧ kdy₧ ji pou₧iju. Respektovanφ tohoto pravidla zabrßnφ necht∞nΘmu pou₧itφ p°ed svou deklaracφ. Prom∞nnß je znßme ve svΘm bloku { } od mφsta deklarace dol∙. Jak jsme ji₧ upozornili, p°echodem p°ed } toho bloku, ve kterΘm je definovanß, prom∞nnß p°estane existovat. Prom∞nnΘ typu static se nezniΦφ p°i v²skoku z bloku ani p°i p°echodu }.
Napis N("Ahoj"); v tomto p°φpad∞ se vytvo°φ prom∞nnß N typu Napis a zavolß se konstruktor t°φdy Napis (nebo se pou₧ije standartnφ, pokud takov² neexistuje) a inicializuje se. Ud∞lajφ se p°φpadnΘ operace s °et∞zcem, to zßle₧φ na implementaci konstruktoru t°φdy Napis.
if (x>0)
int i;
else
double i;
!!!Poz≤r!!!V tomto p°φpad∞ se vytvo°φ prom∞nnß (v naÜem p°φpad∞ "i") typu i r∙znΘho typu podle hodnoty jinΘ prom∞nnΘ (v naÜem p°φpad∞ "x"). Prom∞nnß "i" bude (jak jsme °ekli) existovat od svΘ deklarace a₧ dokonce do konce bloku. OvÜem pozor, zkuste si to p°elo₧it a zjistφte, ₧e to nefunguje tak jak bychom si p°ßli... Nebo¥ tento zßpis je ekvivalentnφ zßpisu:
if (x>0)
{int i;}
else
{double i;}
Otßzka - proΦ? Z druhΘho zßpisu je z°ejmΘ, proΦ to tak je. Prom∞nnß p°estane existovat p°echodem p°es } v tom bloku, ve kterΘm je definovanß. Vlastn∞ m∞ nenapadß, jak za°φdit, aby se deklarovala prom∞nnß r∙znΘho typu podle hodnoty n∞jakΘ prom∞nnΘ, jejφ₧ hodnota nenφ znßma v dob∞ p°ekladu. Je to tak dob°e proto, ₧e p°ekladaΦ nevφ jakΘho typu by byla a tedy by za podmφnkou nemohl rozhodnout, jakΘho je prom∞nnß typu a nev∞d∞l by, jak to p°elo₧it, proto₧e pro ka₧d² typ prom∞nnΘ vypadß p°eklad trochu jinak.
NesmyslnΘ p°φkazy: VÜechno, za Φφm se napφÜe st°ednφk je p°φkaz. 3 je Φφslo, pokud se ovÜem za nφ napφÜe st°ednφk 3; je to p°φkaz. Ale je zajφmavΘ, ₧e p°ekladaΦ to p°elo₧φ ani nevypφÜe warning, ikdy₧ je to logick² nesmysl. Nevφm, co to ve skuteΦnosti ud∞lß, asi se to p°elo₧φ jako prßzdn² p°φkaz. Pokud ovÜem mφsto 3; napφÜeme 3(); ohlßsφ chybu "Call of nonfunction" a nenφ d∙vod se mu divit. TakΘ m∙₧ete napsat samotn² st°ednφk na °ßdku, to by se potom m∞lo p°elo₧it jako prßzdn² p°φkaz. Podobn∞ je nesmysln²m p°φkazem nap°. sin(x) samotnΘ na °ßdku, mß to sice smysl, pouze v²stup funkce se zahazuje. Mß to smysl nap°. u funkcφ getch(), pokud nßs nezßjφmß, jakß klßvesa byl stisknuta, ale pouze chceme naprogramovßnφ Φekßnφ na stisk libovolnΘ klßvesy.
Pozor: Z Pascalu jsme zvyklφ psßt volßnφ procedur bez parametr∙ nap°. ClrScr; nebo ReadKey; , ale zde v C++ je to nebezpeΦnΘ, p°ekladaΦ mß n∞jak² v²raz (viz nßÜ p°φklad s 3; v p°edchozφm odstavci) a p°elo₧φ se to jako prßzdn² p°φkaz. TakovΘ chyby se dost Üpatn∞ hledajφ, snad s vyjφmkou funkce v C++ nosound();, pokud zde zapomeneme (), p°elo₧φ se to jako prßzdn² p°φkaz, nevypne se zvuk z reproduktoru a programßtorovi dojde, co se stalo... :-) , ₧e za nosound zapomn∞l zßvorky a tedy se funkce nosound nezavolala, proto₧e se p°elo₧ila jako prßzdn² p°φkaz.
S blokem jsme se ji₧ seznßmili, ale pro ·plnost si zopakujeme:
{
[p°φkazy]OPT OPT = optional = volitelnΘ
}
P°φkazy a deklarace jsou nepovinnΘ, blok m∙₧e b²t prßzdn² ze stejnΘho d∙vodu, jako ₧e p°φkaz m∙₧e b²t prßzdn². Blok toti₧ odpovφdß slo₧enΘmu p°φkazu. Blok je t°eba volit v p°φpad∞ cykl∙, v∞tvenφ a podmφnky, funkcφ a hlavnφho programu, kde se vÜude oΦekßvß 1 p°φkaz. Pokud tedy chceme mφt vφce p°φkaz∙, potom je musφme logicky dßt do bloku a vytvo°it slo₧en² p°φkaz. Slo₧en² p°ikaz je ji₧ jednφm p°φkazem, kter² u₧ v podmφnce a cyklu m∙₧e b²t. { } odpovφdß v Pascalu p°φkaz∙m begin a end.
Bloky lze (stejn∞ jako v Pascalu) do sebe vno°ovat. Lze samoz°ejm∞ dßvat ka₧d² jednotliv² p°φkaz do bloku, lze psßt {{{}}} nebo {{{p°φkaz}}}, proΦ ne, kdy₧ to programßtora bavφ :-)
Moje poznßmka: Je nutnΘ mφt stejn² poΦet { jako }. To je ovÜem logickΘ. NeÜet°ete ovÜem zßvorkami, jak kulat²mi ( ) ve v²razech a podmφnkßch, tak i slo₧en²mi { } v blocφch. Pozor v cyklech a podmφnkßch se oΦekßvß 1 p°φkaz, pokud zde chceme uvΘst vφce p°φkaz∙ a zapomeneme na to dßt p°φkazy do { }, potom doplatφme na svou Üetrnost a chybu. (V t∞le funkce musφ v₧dy b²t { }.) P°ipomenu svou poznßmku. Cht∞l jsem si naprogramovat makro swap na p°ehazovßnφ dvou Φφseln²ch prom∞nn²ch. P°ednßÜejφcφ ing. M.Virius, CSc. mi °ekl, ₧e to lze ud∞lat bez deklarace lokßlnφ prom∞nnΘ, pouze pou₧itφm operacφ sΦφtßnφ a odΦφtßnφ? Napadne vßs to? (D∙vod proΦ to zde uvßdφm je ten, ₧e to souvisφ se Üet°enφm { } za podmφnkami resp. cykly.)
Moje poznßmka: ╪eÜenφ mΘho problΘmu: Makro swap bude vypadat #define swap(x,y) x=x+y;y=x-y;x=x-y; Makro swap je vlastn∞ jako nahrazenφ swap(x,y) t∞mi t°emi p°φkazy v ka₧dΘm mφst∞ programu. ZkuÜenφ programßto°i se asi u₧ usmφvajφ, co se mi asi stalo... ╚asem jsem swap pou₧φval v BublinkovΘm t°φd∞nφ pole a zapomn∞l jsem, ₧e swap je makro a ne funkce (procedura), tak₧e jsem napsal if (a[i]>a[j]) swap(a[i],a[j]); kde pole a bylo ΦφselnΘ a mohl jsem tudφ₧ pou₧φt svΘ makro. V p°φpad∞ funkce by to fungovalo normßln∞, tak jak to mß, zavolalo by se funkce swap a p°ehodila by prom∞nnΘ a bylo by vÜe v po°ßdku. Ale zapomn∞l jsem, ₧e je to makro, tak₧e se to ve skuteΦnosti p°elo₧φ jako:
if (a[i]>a[j]) a[i]=a[i]+a[j];
a[j]=a[i]-a[j];a[i]=a[i]-a[j];
Moje poznßmka - vysv∞tlenφ: Podmφnka if tedy, pokud je spln∞na, provede pouze 1. p°φkaz a pokraΦuje se provedenφm zbyl²ch dvou p°φkaz∙ (to by jeÜt∞ Ülo...), ale v p°φpad∞ nespln∞nφ podmφnky se 1. p°φkaz neprovede (tak je podmφnka if definovanß), ale zbylΘ dva p°φkazy se provedou v₧dy a zniΦφ tak obsah pole... :=(
Moje poznßmka - °eÜenφ: Vzpomeneme si na poznßmku, ₧e nebudeme v podmφnkßch Üet°it slo₧en²mi zßvorkami a pou₧itφ toho makra napφÜeme takto: if (a[i]>a[j]) {swap(a[i],a[j])} Takto to u₧ bude zaruΦen∞ fungovat. V p°φpad∞, ₧e swap nenφ makro, ale funkce (procedura), potom by byl zde blok zbyteΦn².
Moje poznßmka:Progrßmek s makrem SWAP.CPP
Poznßmka: P°ilo₧en² progrßmek demonstruje p°φstup ke globßlnφ prom∞nnΘ pou₧itφm ::i a takΘ vno°ovßnφ prom∞nn²ch v r∙zn²ch blocφch a jejich zastφn∞nφ.
Poznßmka - prßzdn² p°φkaz: Prßzdn² p°φkaz je st°ednφk; samotn² nebo blok { }
Poznßmka - podmφn∞n² p°φkaz: Podmφn∞n² p°φkaz - if(podmφnka) p°φkaz1 [else p°φkaz2]opt. Je takΘ mo₧nΘ je do sebe vno°ovat, proto₧e podmφn∞n² p°φkaz je takΘ p°φkaz a bloky je takΘ mo₧nΘ do sebe vno°ovat.
if (i>0) x=7; else x=11;
je sprßvn∞, zatφmco:
if (i>0) {x=7;}; else x=11;
je chybn∞, proto₧e st°ednφk za {x=7;}; je p°φkaz, kter² ovÜem nem∙₧e zde b²t, jestli₧e nßsleduje else.
Poznßmka - rozdφl = a ==:V podmφnce m∙₧eme napsat x=7, co₧ je mo₧nΘ, proto₧e x=7 p°i°adφ do prom∞nnΘ x hodnotu 7 a jako v²stup dß p°i°azenφ hodnotu 7, co₧ lze testovat v podmφnce. 7 je ovÜem nenulovß, tak₧e se p°φkaz za podmφnkou v₧dy provede. Kdyby ovÜem byla v²sledkem p°i°azenφ 0, tak se p°φkaz za podmφnkou neprovede a provede se p°φkaz za else, pokud if obsahuje else. Ale v∞tÜinou mß programßtor na mysli x==7, tedy test, zda je x rovno hodnot∞ 7 a pokud ano, provede se p°φkaz za podmφnkou Proto kdy₧ napφÜeme v podmφnce x=7 mφsto x==7, p°ekladaΦ program p°elo₧φ, ale vydß varovßnφ warning, podle kterΘho jsme upozorn∞ni, zda jsme to mysleli tak, jak jsme to napsali.
Poznßmka - ne·plnΘ vyhodnocovßnφ: Pokud napφÜeme nap°. if (x>0 && sqrt(x)<10) f(); potom se vyhodnotφ prvnφ podmφnka (x>0) nespln∞na a nßsleduje logick² souΦin (and - a souΦasn∞ - &&, logick² souΦin 0 s jak²mkoliv v²razem je v₧dy 0), proto se dalÜφ podmφnky ji₧ netestujφ. Analogicky by to bylo, pokud by byla 1. spln∞na a byl zde logickΘ souΦet (or - nebo - ||, logick² souΦet 1 s jak²mkoliv v²razem je v₧dy 1), proto se dalÜφ podmφnky ji₧ netestujφ. V Pascalu se toto dß nastavit, Pascal asi rßd d∞lß zbyteΦnΘ operace nebo p°ekladaΦ nechce generovat lepÜφ k≤d, pokud to nenφ nezbytn∞ nutnΘ, v Pascalu se to dß nastavit v Compiler Options - Complete Boolean Evaluation. V BC C++ 3.1 jsem podobnΘ nastavenφ neuvid∞l (mo₧nß ₧e tam n∞kde je...???) a p°ekladaΦ automaticky pou₧φvß ne·plnΘ vyhodnocovßnφ, tak₧e Üet°φ operace, pokud u₧ je v²sledek jasn², ji₧ dßle netestuje ostatnφ podmφnky. V naÜem p°φpad∞ se ji₧ nebude volat funkce sqrt (druhß odmocnina) pro zßpornΘ Φφsla.
Poznßmka - podmφn∞n² v²raz: Pod podmφn∞n²m v²razem chßpeme ?:, bli₧Üφ ·daje v nßpov∞d∞. Vysv∞tlenφ: Nech¥ napφÜeme nap°. a?b:c potom se testuje "a", je-li podmφnka spln∞na (pravda - True), potom v²sledkem v²razu "b", je-li podmφnka nespln∞na (le₧ - False), potom je v²sledkem v²razu "c".
P°φklad: Naprogramujeme hledßnφ maxima dvou Φφsel:
if (x>y) max=x; else max=y;
nebo pou₧itφm podmφn∞nΘho v²razu:
max=(x>y)?x:y
Pro programßtora v Pascalu bude asi p°ehledn∞jÜφ 1. mo₧nost, avÜak programßtor v C++ rad∞ji uvidφ 2. zßpis. Je k dispozici demonstraΦnφ progrßmek pro maximum a minimum dvou Φφsel naprogramovan²ch dv∞mi metodami.
Poznßmka - po°adaΦ: Pod po°adaΦem rozumφme, operßtor - Φßrka , kter² mß v²znam jako zahozenφ v²stupu z prvnφ funkce. Nech¥ f(x) a g(x) jsou funkce (ne matematickΘ, ale n∞jakΘ v C resp. C++). Potom v²raz zapsan² v C++ takto: f(x),g(x) zavolß prvnφ funkci f(x), v²stup zahodφ a zavolß druhou funkci g(x) a v²sledkem f(x),g(x) bude vracenß hodnota funkce g(x).
prom∞nnß=(f(x),g(x))
je ekvivalentnφ zßpisu:
f(x);prom∞nnß=g(x);
Charakteristick²mi vlastnostmi operacφ (operßtor∙) je priorita a asociativita. Ukß₧eme si sm∞ry p°i°azovßnφ a sm∞ry operacφ nap°. sΦφtßnφ. Zßvorky majφ nejvyÜÜφ prioritu a tedy co je v zßvorkßch, to se vyhodnotφ nejd°φve, nakonec se vyhodnotφ to, co mß nejni₧Üφ prioritu.
P°φklad Φ.1: x=y=z=7; zde se postupuje zprava doleva, tj. nejd°φve se p°i°adφ sedmiΦka do z, potom se p°ekopφruje obsah prom∞nnΘ z do y a nakonec se p°ekopφruje obsah prom∞nnΘ y do x. Je to tedy ekvivalentnφ zßpisu: x=(y=(z=7)));
P°φklad Φ.2: V²raz a+b+c obsahuje vÜechny operace stejnΘ priority, proto se postupuje zleva doprava. P°ekladaΦ to p°elo₧φ asi jako: (a+b)+c
P°φklad Φ.3:
int x,y;
double z,c;
c=x=y=z=3.14
Bude v²sledkem v prom∞nnΘ 3 ne vÜak 3.14. D∙vodem je, ₧e zde (aΦ je tento p°φklad nelogicky a nesmysln², ale to te∩ nechßme stranou, d∙le₧itΘ je si vysv∞tlit, proΦ to tak je, jak to je...). Budeme postupovat postupovat podle 1. p°φkladu, tedy zprava doleva. Prom∞nnß "z" je double, tedy reßlnΘ Φφsla, tedy se do prom∞nnΘ z p°i°adφ skuteΦn∞ 3.14. Dob°e, v "z" u₧ mßme hodnotu 3.14. Nynφ chceme p°i°azenφ y=z, podφvßme se, ₧e "y" je v naÜem p°φpad∞ int, tedy celoΦφselnß prom∞nnß. Na pravΘ stran∞ "z" je reßlnΘ Φφslo. Provede se tedy konverze z double na int tak, ₧e se usekne desetinnß Φßst a p°ekladaΦ provede jeÜt∞ dalÜφ operace a p°esuny, kterΘ nejsou te∩ a₧ tak moc d∙le₧itΘ. Po useknutφ desetinnΘ Φßsti mßme v y ji₧ 3. Nynφ mßme provΘst p°i°azenφ x=y, ob∞ prom∞nnΘ jsou int, tak₧e je to bez problΘm∙, vÜe je jednoduchΘ, v x tedy mßme 3. A poslednφm p°i°azenφm je c=x, zjistφme, ₧e c je double a x je int, tak₧e se provede konverze z int na double. Celß Φφsla ovÜem nemajφ desetinnou Φßst, proto bude v "c" nulovß desetinnß Φßst a tedy v "c" skuteΦn∞ bude po p°i°azenφ 3.
╖ int x,y=11;
╖ while(x==y) ;
V tomto p°φpad∞, tak dlouho dokud je spln∞na podmφnka, provßdφ je p°φkaz, kter² je za podmφnkou while. Je to klasick² Pascalovsk² cyklus while, akorßt, ₧e zde chybφ klφΦovΘ slovo "do" a podmφnka se zßsadn∞ pφÜe do zßvorky, nebo¥ zßvorky urΦujφ zaΦßtek "(" a konec podmφnky ")".
╖ v²raz1;
╖ while(v²raz2) {
╖ áp°φkaz;
╖ áv²raz3;
}
Z tohoto ekvivalentnφho zßpisu je z°ejmΘ, ₧e "v²raz1" je inicializace, tedy zde nastavφme prom∞nnou cyklu na poΦßteΦnφ hodnotu (m∙₧eme mφt cyklus cel²ch, reßln²ch a takΘ i komplexnφch Φφsel - pokud n∞kdo nalezne pou₧itφ). Prob∞hne p°ed vlastnφm cyklem. "v²raz2" je podmφnka opakovßnφ, opakuje se p°i spln∞nφ podmφnky. "v²raz3" je vlastn∞ Φßst, kterß se provßdφ v ka₧dΘm kroku. Zpravidla se sem dßvß p°φkaz pro zv∞tÜenφ (snφ₧enφ) o 1 Φi p°φkaz pro dalÜφ mo₧nou hodnotu. "p°φkaz" je normßlnφ p°φkaz, kter² se mß provßd∞t, m∙₧e zde b²t takΘ slo₧en² p°φkaz, proto₧e mßlokdy staΦφ jeden p°φklad v cyklu. P°φklad je v nßsledujφcφ kapitole o poli.
P°φklad: Vypln∞nφ pole jedniΦkami
int a[10]; //var a:array[0..9] of integer;
for (int i=0; i<10; i++) a[i]=1; //for i:=0 to 9 do a[i]:=1;
Deklarace pole a pou₧itφ je analogickΘ Pascalu. Deklarace je stejnß jako deklarace prom∞nnΘ, pouze s tφm rozdφlem, ₧e se za nßzvem prom∞nnΘ napφÜe [index].
Pozor: Pole mß prvnφ prvek v₧dy s indexem 0. V₧dy je poΦet prvk∙ tolik, kolik je Φφslo v hranatΘ zßvorce. Deklarace int a[10]; vyhradφ v pam∞ti 10 prom∞nn²ch typu int, index prvnφho je 0 (jak jsem ji₧ napsal) a index poslednφho je 9. Kdy₧ to programßtor nevφ a spolßhß na PascalovskΘ hlßÜenφ Range Check Error, potom je v C++ nemile p°ekvapen, ₧e se mu p°episujφ obsahy jin²ch prom∞nn²ch, nebo ₧e p°episuje Φßst k≤du nebo ₧e se mu d∞jφ podivnΘ v∞ci. TakovΘ hlßÜenφ se v C++ neobjevuje, je na programßtorovi, aby si to ohlφdal. Proto, kdy₧ to programßtor nevφ, potom mu zpravidla program nefunguje tak, jak by m∞l... Ani nenφ v C++ mo₧nΘ nastavit, aby se hlßÜenφ Range Check Error, aby se kontrolovaly meze. Jedin²m mo₧nostφ °eÜenφ je p°edefinovat operßtor indexovßnφ [ ], co₧ se snad takΘ n∞kdy nauΦφte.
Poznßmka: Je zde ud∞lßn progrßmek pro obrßcenφ pole od poslednφho k prvnφmu.
Poznßmka - p°φkaz break: P°φkaz break provede ukonΦenφ cyklu a skok na dalÜφ p°φkazy za cyklem. V Borland Pascalu 7.0 je ji₧ p°φkaz break implementovßn, tak₧e i v Pascalu ho m∙₧ete ji₧ pou₧φvat. StarÜφ verze Pascalu ho jeÜt∞ neumφ. DemonstaΦnφ program pro break.
Poznßmka - p°φkaz continue: P°φkaz continue p°eskoΦφ zbytek t∞la cyklu a skoΦφ na podmφnku opakovßnφ. Cyklus se narozdφl od break neukonΦφ, ale p°eskoΦφ se ne konec cyklu a pokraΦuje skokem na podmφnku opakovßnφ cyklu.DemonstraΦnφ program pro continue.
Tento p°φkaz je urΦen k v∞tvenφ chodu programu na varianty podle hodnoty v²razu v podmφnce. Je to obdoba PascalovskΘho case of, ale programßtor, kter² znal Pascal, bude trochu nemile p°ekvapen, ₧e bude muset dßvat za jednotlivΘ mo₧nosti break a takΘ ₧e nelze pou₧φt v jednotliv²ch mo₧nostech interval (10..25: p°φkaz), C++ toti₧ po skonΦenφ tΘ v∞tve toti₧ nedß skok do spoleΦnΘ Φßsti, ale pokraΦuje se dßl, tak₧e pokud je za nφ dalÜφ v∞tev, skoΦφ se do nφ. Blφ₧e to ukß₧e p°ilo₧en² p°φklad, je ukßzßno, co se stane v p°φpad∞, ₧e zapomenete ve v∞tvi p°φkaz break;.
Deklarace:
switch(v²raz)
{
case hodnota1: p°φkaz1;
case hodnota2: p°φkaz2;
...
case hodnotan: p°φkazn;
defaulta: p°φkaz;
}
P°φkazy 1-n v∞tÜinou jsou slou₧enΘ, tak₧e je tam blok { } a poslednφm v bloku je p°φkaz break; , aby nßsledoval skok do spoleΦnΘ nerozv∞tvenΘ Φßsti za koncem switch. P°φkaz break je nutn² proto, ₧e se podle hodnoty sice skoΦφ na p°φsluÜnou v∞tev, ovÜem C++ ji₧ nevlo₧φ skok do spoleΦnΘ Φßsti, tak₧e se nßsleduje pokraΦovßnφm do dalÜφ v∞tve (u poslednφ nßm to nevadφ).
Poznßmka: P°φkaz break lze pou₧φt v cyklech a ve switch, zatφmco continue lze pou₧φt pouze v cyklech.
Poznßmka: Pokud si p°ejeme jedene stejn² p°φkaz pou₧φt pro vφce mo₧nostφ case ve switch, m∙₧eme to ud∞lat nap°. takto:
case 'a':
case 'A': cout << "Ahoj" << endl;
Je to domonstrovßno v ji₧ ukßzanΘm p°φkladu.
Poznßmka - v C++ neexistuje Pascalovsk² p°φkaz with: V Pascalu existuje p°φkaz with pro prßci se strukturami a objekty a tady v C++ neexistuje.
U p°ekladaΦ∙ Watcom je mo₧nΘ vlo₧it instrukce Assembleru takto: asm(°et∞zec), asm("mov AX,10")
U p°ekladaΦ∙ Borland se Assembler vklßdß takto: asm { instrukce Assembleru }
Sprßvn∞:
asm {
instrukce
}
TakΘ sprßvn∞ (m∙₧e to b²t se st°ednφkem, ale takΘ nemusφ BC 3.1):
asm MOV AX,0
èpatn∞:
asm
{
instrukce
}
!!!Pozor!!! ProΦ je to Üpatn∞? Pokud existuje i mo₧nost s asm na jednom °ßdku, oΦekßvß bu∩ jednu instrukci v Assembleru (ale to je sm∞ÜnΘ, co ud∞lß 1 instrukce Assembleru) nebo blok { }. Pokud tam nic nenapφÜeme, p°elo₧φ to jako prßzdn² p°φkaz a uvidφ na dalÜφm °ßdku blok, tak to je slo₧en² p°φkaz, ale u₧ p°edpoklßdß, ₧e je to slo₧en² p°φkaz v C resp. C++, kde u₧ neznß instrukce v Assemleru, tak₧e ohlßsφ chybu p°i p°ekladu.
Poznßmka pro specialisty: AssemblerovskΘ instrukce se b∞₧n∞ vklßdajφ do programovΘho - code segmentu. Lze je vklßdat i do dat - data segmentu, co₧ b∞₧nφ u₧ivatelΘ asi nepou₧ijφ, ale n∞kdy se to hodφ u samomodifikujφcφch se program∙. Ale to jsou dost nebezpeΦnΘ v∞ci a m∙₧e to ud∞lat tolik zmatk∙, ₧e to m∙₧e mφt podobnΘ d∙sledky, jako kdy₧ p°epφÜeme Φßst pam∞ti nap°. p°i p°ekroΦenφ mezφ pole a zapsßnφ tam n∞jakΘ hodnoty.
P°φkaz return hodnota mß smysl pouze u funkcφ, ale jeliko₧ v C++ jsou vÜechno funkce, tak to musφme up°esnit, ₧e je to u fukncφ, kter² majφ n∞jak² v²stup. P°φkaz return hodnota tedy nemß smysl u funkcφ vracejφcφ void - tedy ty, kterΘ majφ prßzdn² v²stup. U funkce vracejφcφ void (funkce bez v²stupu), je mo₧nΘ pou₧φt pouze return; - tedy bez vracenΘ hodnoty. A u funkcφ, kterΘ majφ neprßzdn² v²stup, nelze pou₧φt return; ale pouze return hodnota;.
P°φkaz return okam₧it∞ ukonΦuje funkci a p°edß se °φzenφ za p°φkaz, kter² vyvolal tu danou funkci.
V hlavnφm programu int main() { } se pou₧φvß v DOSu pro vracenφ chybovΘho k≤du DOSu - prom∞nnß ERRORLEVEL. Proto je tedy dobrΘ, definovat hlavnφ program jako funkce vracejφcφ n∞jakou hodnotu. Jak je to s vracenφm hodnoty z hlavnφho programu na Unixu, to si netroufßm °φcφ.
DalÜφ povφdßnφ o p°φkazu return je zde.
Deklarace: enum [jmΘno] { co bude obsahovat}
P°φkady:
enum tyden {pondeli, utery, streda, ctvrtek, patek, sobota, nedele};
enum mesice {leen, unor, brezen, duben, kveten, cerven, cervenec, srpen, zari, rijen, listopad, prosinec};
enum Bible {Stary_Zakon, Novy_Zakon};
V C++ jsou v²ΦtovΘ typy zvlßÜtnφ typ, v C je to celoΦφseln² typ. Z toho vypl²vß, ₧e v C lze napsat p°i°azenφ x=7; (x je prom∞nnß v²ΦtovΘho typu), ale v C++ ne. Za poslednφ polo₧kou ve v²ΦtovΘm typu m∙₧e b²t Φßrka, p°ekladaΦ neohlßsφ chybu.
Pomocφ typedef lze tvo°it synonyma nap°.:
typedef enum dny {po,ut,st,ct,pa,so,ne} Dny;
Programßto°i trßpφcφse aplikacemi pro Windows asi znajφ pojem handle. N∞kde v p°ekladaΦi je napsßno n∞co jako:
typedef int handle; je to tedy alternativnφ typ pro int, nov² typ - nßzev. Mß to tu v²hodu, ₧e pokud chceme zm∞nit rozsah prom∞nnΘ typu handle, staΦφ pouze zm∞nit int na v∞tÜφ Φi menÜφ typ a znovu p°elo₧it. Sice to znep°ehled≥uje v²pis programu, ₧e hned na prvnφ pohled nenφ vid∞t, jakΘho typu je prom∞nnß hande (n∞kdy to nelze ani poznat), ale lze snadno modifikovat a nenφ t°eba zdlouhav∞ prohledßvat, kde jsem hande pou₧il, abych zm∞nil jejφ typ.
Poznßmka: Pro v²ΦtovΘ typy lze p°etφ₧it operßtory <<, >>. To kdyby si n∞kdo p°ßl mφsto Φφsla v²stup rovnou nßzev.
Moje poznßmka: N∞kde si jeÜt∞ pamatuje na to, ₧e jsem si definoval makro swap pro prohazovßnφ obsahu dvou prom∞nn²ch a na (skoro zaΦßteΦnick²) problΘm, kter² z toho vyplynul.
Moje definice - makro: Makro je n∞co, co se rozvine v °et∞zec a odpovφdß to jako Replace ve zdrojovΘm textu. Zdrojov² text se ovÜem nem∞nφ.
Moje poznßmka: Jednß se vlastn∞ o nahrazenφ n∞Φeho neΦφm, co d∞lß p°ekladaΦ p°ed vlastnφ kompilacφ. Nemßm rßd definice, vy snad takΘ ne, proto hned p°ejdeme k p°φklad∙m. V Assembleru (u₧ na ATARI 800 XL byl makroassembler), co₧ je tvo°enφ skupiny instrukcφ, kterΘ se n∞jak oznaΦφ (nejlΘpe podle funkce) a tak p°i ka₧dΘm uvedenφ jmΘna makra vlo₧il strojov² k≤d p°elo₧enΘho makra do mφsta volßnφ. Je to jako podprogram, avÜak se mφsto skoku do podprogramu vlo₧φ t∞lo makra. Zde v C resp. C++ je to podobnΘ. Zdrojov² text se nem∞nφ, pouze to umo₧≥uje vytvo°it k≤d, kter² nemusφ mφt skoky (je sice delÜφ), ale lze ho libovoln∞ posunovat v pam∞ti, ani₧ by se museli m∞nit adresy, kam se skßΦe, proto₧e p°elo₧en² strojov² k≤d odpovφdajφcφ makru se v₧dy vlo₧φ na mφsto volßnφ. V C++ makro m∙₧eme pou₧φt k definovßnφ konstanty, symbolu, Φi jako skupina p°φkaz∙, kterΘ se vlo₧φ na mφst∞ volßnφ.
Deklarace makra: Z definice makra jste mo₧nß pochopili, ₧e se bude n∞co nahrazovat n∞Φφm.
Poznßmka: ╪et∞zec makra se bere a₧ do konce °ßdky. NepφÜe se nakonci °ßdky st°ednφk, nebo¥
#define nßzev_makra °et∞zec
P°φklad Φ.1: Makro jako konstanta: #define N 1000 P°ed vlastnφm p°ekladem nahradφ vÜude kde je N Φφslem 1000, tisφc zde bude jako °et∞zec (ne v binßrnφm tvaru) jako ve zdrojovΘm textu.
P°φklad Φ.2: Makro jako procedura pro p°ehazovßnφ Φφseln²ch prom∞nn²ch: #define swap(x,y) {x=x+y;y=x-y;x=x-y;} V ka₧dΘm pou₧itφ makra se vlo₧φ tyto p°φkazy. Tak se dosßhne p°ehozenφ dvou prom∞nn²ch.
P°φklad Φ.3: Makro jako funkce pro vrßcenφ maxima dvou Φφsel: #define max(x,y) (((x)>(y))?(x):(y)) Je to v²raz (kter² jsme si uvedli v kapitole o podmφn∞nΘm v²razu), pouze s trochu vφce zßvorkami. Poznßte, ₧e u maker se zßvorkami nevyplßcφ Üet°it. Hodn∞ zßvorek je zßrukou ·sp∞chu. ProΦ je dobrΘ dßvat u maker hodn∞ zßvorek, poznßte z p°φkladu 4.
P°φklad Φ.4: Makro jako funkce pro vrßcenφ druhΘ mocniny Φφsel: #define sqr(x) x*x; VyzkouÜejte si, co se stane a zjistφte, ₧e to n∞kdy nefunguje... PokraΦovßnφ v p°φkladu. M∙₧ete se mrknout sem na p°φklad.
Poznßmka: Nastavenφ Üφ°ky v²stupu u cout se provede manipulßtorem setw(n), kde n je poΦet mφst a lze ho nalΘzt v hlaviΦkovΘm souboru <iomanip.h>
Poznßmka - podmφn∞n² p°eklad: Jednß se mo₧nost p°eklßdat Φi nep°eklßdat Φßsti zdrojovΘho textu v zßvislosti na existenci n∞jakΘho makra.
Deklarace:
#if podmφnka
.....
#else
.....
#endif
Podmφnkou m∙₧e b²t existence makra, potom je podmφnka takovßto:
#if defined RET
.....
#endif
Podmφnkou musφ b²t takovß, kterou je mo₧no vyhodnotit v dob∞ p°ekladu. Za define se nepφÜe st°ednφk. #ifdef = #if defined.
Trik Φ.1: Odstran∞nφ Φßsti textu:
#if 0
...
#endif
Trik Φ.2:Existuje trik, pokud pot°ebujeme odladit Φßst programu samostatn∞, pokud se sklßdß z vφce soubor∙ a pot°ebujeme mφt alespo≥ n∞jakou funkci main, abychom mohli podprogram odstartovat. ╪eÜenφ:
#defined LADIM
.....
#ifdef LADIM
int main()
{
á ....
}
#endif
Pokud tedy jeÜt∞ ladφme, nechßme existovat makro LADIM. Pokud u₧ to bude odlad∞no, zruÜφme makro LADIM vymazßnφm °ßdky #define LADIM. Potom u₧ nebude makro existovat, tak₧e se nebude ani p°eklßdat funkce main().
Trik Φ.3: <assert.h> je pou₧itelnΘ p°i lad∞nφ. Chceme nap°. mφt jistotu, aby nap°. x bylo v∞tÜφ ne₧ y. Potom pou₧ijeme hlaviΦkov² soubor <assert.h> a vlo₧φme do programu makro assert(x>y);. Pokud je podmφnka spln∞na, potom se pokraΦuje, jinak se vypφÜe chybovΘ hlßÜenφ. M∙₧e se to hodit nap°. pro test, zda je alokovanß pam∞¥. Pokud mßme program ji₧ odlad∞n (co₧ je asi nejmilejÜφ chvφle pro programßtora), potom nemusφme vyhledßvat pou₧itß makra assert, ale staΦφ p°idat: #define NDEBUG, kterΘ zp∙sobφ, ₧e makro assert se rozvine v prßzdn² °et∞zec, tak₧e u₧ formßln∞ nebude vadit a zdrojov² text se stejn∞ moc neukazuje...
Poznßmka - zruÜenφ makra: #undef jmΘno
# if !defined = #ifndef
#if defined = #ifdef
#else if = #elif
Ukazatel definujeme tak, ₧e p°ed prom∞nnou napφÜeme *.
P°φklady:
Pole a ukazatele: Ka₧d² ukazatel se poklßdß za ukazatel na zaΦßtek pole. U polφ (jak u₧ jsme °ekli) nelze volit dolnφ mez, ta je 0. int P[10]; vytvo°φ pole P[0],P[1],...,P[9]. Pokud napφÜeme const N=10; potom se bere jako konstanta typu int. Potom m∙₧eme zapsat:
const N=10;
int P[n],i,*ui; //deklarace pole int∙, prom∞nnß i typu int,ui je ukazatel na int
for (i=0;i<N;i++) P[i]=i; //vypln∞nφ pole Φφsly od 0 do N
Rozdφly polφ v C,C++ proti Pascalu:
Dynamickß alokace pam∞ti:
Do mφsta, kam ukazuje ui se ulo₧φ 11 p°φkazem *ui=11;
Lze dereferencovat - lze aplikovat indexovßnφ ui[5]=7; lze tedy pomocφ ukazatele pracovat s polem.
Borland C++ 3.1 mß hezkou pom∙cku pro kontrolu pam∞ti, kterß se jmenuje inspektor a vyvolß se stiskem ALT + F4 nebo z menu Debug Inspect. Do okΘnka napφÜeme nßzev prom∞nnΘ, kterou si chceme prohlΘdnout. Narozdφl od Turbo-Debuggeru umo₧≥uje prohlΘdnout si dynamicky alokovanΘ pole, co₧ se hodφ nap°. v seznamech, kde umo₧≥uje si prohlφ₧et i jednotlivΘ prvky v seznamu, ani₧ bychom definovali n∞jakou funkci, kterß by seznam zobrazovala. M∙₧eme zobrazit i prvek pole zadßnφm ui[4]. ui=p; To by mohlo mφt nßsledek, ₧e byste moli pova₧ovat pole za ukazatel na jeho prvnφ prvek za jedno a totΘ₧. To ovÜem nenφ totΘ₧, viz nßsledujφcφ vyjφmky:
Vyjφmky, kdy nenφ toto₧n² ukazatel na 1. prvek pole a pole:
3. int ui = &P; //P je pole
4. int uui[10]*; //ukazatel na pole o dΘlce 10 prvk∙ int
5. int *uui=P; //lze
Co to je adresovß aritmetika? Nech¥ tedy mßme nap°φklad pole a n∞jak² ukazatel, kter² ukazuje n∞kam v poli. Nynφ m∙₧eme p°istupovat k dalÜφm prvk∙m pole tak, ₧e m∙₧eme pohybovat s tφmto ukazatelem po poli.
int *ui; //bude pou₧it jako ukazatel n∞kam do pole int∙
ui+1 bude potom ukazovat na dalÜφ prvek pole
ui+5 bude potom ukazovat o 5 prvk∙ dßle
PodporovanΘ operace: Jsou tedy definovanΘ operace + - pro posun v poli. To ovÜem nenφ vÜechno. Lze porovnßvat ukazatele ui==uj a takΘ testovat nerovnost ui!=uj. Je mo₧nΘ pou₧φvat pou₧φvat operace < > <= >= Ukazatele lze sΦφtat, odΦφtat. OdΦφtßnφm lze zjistit poΦet prvk∙ mezi nimi. Nulov² ukazatel (obdoba PascalovskΘho NIL) mß zde v C++ hodnotu 0 (n∞kdy se definuje konstanta NILL). Ukazatel lze testovat v podmφnce if(ui) ui<>True, ui=0 False.
Progrßmek na vypln∞nφ pole:
Progrßmek na vypln∞nφ pole pou₧itφm adresovΘ aritmetiky, popis je souΦßstφ progrßmku.
Progrßmek na kopφrovßnφ 2 °et∞zc∙:
Nejprve si zkusφme vysv∞tlit, co se stane pokud definujeme dva °et∞zce:
char *c = "AHOJ";
char *d = c;
Potom se vyhradφ v pam∞ti text, na kter² bude ukazovat ukazatel c. Pokud nynφ definujeme ukazatel d, tak mφsto vyhrazenφ novΘho prostoru se pouze ukazatel d vezme a bude ukazovat na stejn² °et∞zec. Kdo mi neuv∞°φ, m∙₧e si zkusit zm∞nit Φßst °et∞zce c, zm∞nφ se i d a opaΦn∞. V p°ilo₧enΘm p°φkladu je vÜechno naprogramovßno a je naprogramovßna a vysv∞tlena funkce strcpy pro kopφrovßnφ °et∞zc∙. Je naprogramovßna klasicky C++, tak₧e programßtor∙m v C++ to m∙₧e p°ipadat jako magickß formulace, ale ₧ßdnΘ magie neexistuje (vÜechno to jsou podvody), tak p°iklßdßm vysv∞tlenφ.
Zde na obrßzku uvidφte, co se stalo v p°φpad∞, ₧e budeme chtφt kopφrovat °et∞zce pouze p°i°azovacφm p°φkazem. Vlastn∞ se stane pouze to, ₧e se zm∞nφ ukazatel na °et∞zec a bude ukazovat jinam.
Zde u °et∞zcov²ch operacφ je ·mluva psßt cφl jako prvnφ parametr nebo jako v²stup funkce.
Inicializace polφ: Asi jste to ji₧ vid∞li v p°φkladu, nicmΘn∞ Φlov∞k je podle svΘ definice tvorem zapomφnajφcφm, proto to zopakujeme. P°φklad: int y[10]={1,2,5,88,} Pokud neuvedeme vÜechny Φφsla, potom by se m∞l zbytek doplnit nulami. Nemusφme takΘ poΦφtat poΦet prvk∙ napsat y[]={prvky pole}, v tom p°φpad∞ si je poΦφtaΦ spoΦφtß a deklaruje nejmenÜφ pole obsahujφcφ vÜechny prvky.
ZjiÜt∞nφ velikosti polφ: Pou₧ijeme sizeof(pole). V naÜem p°φpad∞ m∙₧eme nap°. napsat: cout << sizeof(y) << endl; - vypφÜe se 8 - co₧ jsou 4.int a int zabφrß 2B.
Prßce s dynamickou pam∞tφ: Pojem dynamickß pam∞¥ znamenß pam∞¥, ve kterΘ m∙₧eme za b∞hu programu vytvß°et novΘ prom∞nnΘ, pole a data, je ji mo₧nΘ takΘ alokovat. Tyto prom∞nnΘ se neruÜφ p°i v²skoku z prom∞nnΘ, n∞kdy se ani neuvolnφ p°i ukonΦenφ programu, ale za normßlnφch okolnostφ by je m∞l programovacφ jazyk Φi operaΦnφ systΘm vrßtit a uvolnit, pokud tak neud∞lß nepo°ßdn² programßtor. Spolu s dynamickou pam∞tφ se objevuje pojem halda, co₧ je jin² nßzev pro tu oblast pam∞ti, kde lze z°izovat novΘ prom∞nnΘ a ruÜit je. Prom∞nnΘ tedy mohou b²t:
P°id∞lenφ dynamickΘ pam∞ti:
Odstran∞nφ - uvoln∞nφ dynamickΘ pam∞ti:
Vφcerozm∞rnß pole: p°φklad: double matice[3][5]; matice 3x5. V C existujφ pouze jednorozm∞rnß pole, co₧ ovÜem nevadφ, pokud prvkem pole m∙₧e b²t zase pole. Tak₧e dvourozm∞rnΘ pole je vlastn∞ pole, jeho₧ prvky jsou pole. Tak₧e v naÜem p°φpad∞ to bude pole [0],[1],[2], kterΘ obsahujφ prom∞nnΘ typu double [0],[1],[2],[3],[4].
Jak vφme, v∞tÜinou se pole konvertuje na ukazatel na 1. prvek (a₧ na t∞ch n∞kolik vyjφmek). Nynφ si ukß₧eme, jak je mo₧nΘ p°istupovat k prvk∙m pole bez pou₧itφ indexovßnφ, nap°. pomocφ adresovΘ aritmetiky.
P°φklad: Cht∞li bychom se dostat a zφskat obsah p°φpadn∞ zm∞nit prvek mat[1][1] pomocφ adresovΘ aritmetiky. Prvnφ indexovßnφ m∙₧e b²t *(mat+1), co₧ bude ukazovat na zaΦßtek mat[1], tedy na zaΦßtek prvku pole - jeho₧ prvkem je pole, tak₧e se posuneme o dΘlku prvku, kter²m je pole, 2. indexovßnφm potom se posuneme v tom danΘm poli, nynφ se posuneme o dΘlku double a v²sledek je potom *(*(mat+1)+1).
P°φklady: Deklarujeme matici:
Nulovßnφ pole: for (int i=0;i<3;i++) for (int j=0;j<5;j++) mat[i][j]=0;
Poznßmka: Deklaraci 5 ukazatel∙ na int provedeme: int (*up)[5]; Budeme chtφt pomocφ t∞chto ukazatel∙ naprogramovat vypln∞nφ pole Φφsly jeÜt∞ jinou metodou, nap°. pomocφ adresovΘ aritmetiky.
for (int (*up)[5]=mat; up<&mat[3]; u++) for (int *ui = *up; ui<&up[5]; ui++) *ui=11;
Mßme zde vlastn∞ definovanΘ jakΘsi p°φstupovΘ vektory, co₧ je vlastn∞ ukazatel na ukazatel.
P°φklad, implementujφcφ ukßzku na p°edchozφm obrßzku:
int **m;
typedef int *ukint;
m=new(int*)[5];
m=new uknit[5];
for (int i=0;i<5;i++) m[i]=newint[10];
m je ukazatel na pole ukazatel∙ na int, co₧ je ukazatel na pole, na kterΘm lze op∞t pou₧φt indexovßnφ. Lze tedy adresovat nap°. m[i][j]=11;m[3][3]=111;
Rozdφl mezi p°edchozφm p°φkladem a maticφ vytvo°enou pomocφ: double mat[10][15] je ₧e v p°φkladu je pole jednak alokovanΘ dynamicky a zpravidla nenφ ani za sebou v pam∞ti, obecn∞ nelze polohu alokovanΘ prom∞nnΘ p°edpoklßdat, proto se u new vracφ ukazatel, proto₧e potom by nikdo nev∞d∞l, kde se ta danß vytvo°enß prom∞nnß vlastn∞ nachßzφ. Zatφmco deklaracφ double mat[10][15] se vytvo°φ pole, kterΘ nenφ alokovanΘ dynamicky a je to oblast 150 int∙ v pam∞ti za sebou. M∙₧ete se podφlat do Turbo Debuggeru a v∞tÜinou to tak bude. V²hodou dynamicky alokovanΘho pole je, ₧e lze vytvo°it nap°φklad troj·helnφkovou matici, ani₧ by to n∞jak zbyteΦn∞ zabφralo pam∞¥, ale staticky lze vytvo°it pole a pole polφ, kterΘ jsou obdΘlnφkovΘ. Pro ulo₧enφ troj·helnφkovΘ matice by se v tom p°φpad∞ pl²tvalo pam∞tφ.
Pozor: Operßtor new nemusφ obecn∞ usp∞t, je proto nutnΘ ov∞°it, zda je vracen² ukazatel r∙zn² od nuly, v takovΘm p°φpad∞, byla alokace pam∞ti ·sp∞Ünß a adresa alokovanΘ prom∞nnΘ je platnß. V p°φpad∞, ₧e ukazatel je nula, v tom p°φpad∞ je adresa neplatnß a alokace se nezda°ila (nejspφÜ na nedostatek pam∞ti, proto₧e ji n∞jak² nepo°ßdn² programßr zapom∞l uvolnit operßtorem delete.
Uvoln∞nφ pam∞ti: K uvoln∞nφ pam∞ti slou₧φ operator delete, za kter² se napφÜe ukazatel prom∞nnΘ, kterß se mß uvolnit. Toto mφsto ruÜenΘ prom∞nnΘ bude OS nebo programovacφm jazykem oznaΦeno za volnΘ, tak₧e m∙₧e b²t pou₧ito pro dalÜφ alokaci. Proto, pokud zapomenete uvolnit p°id∞lenou pam∞t anevrßtφ ji sßm OS po ukonΦenφ VaÜeho nepo°ßdnΘho programu, potom to zabra≥uje prßci dalÜφch program∙, kterΘ pot°ebujφ hodn∞ pam∞ti (dnes to nenφ nic neobyklΘho). M∙₧e to dojφt k zaseknutφ poΦφtaΦe, a¥ u₧ s vypsßnφm hlßÜenφ typu Out of memory, system halted, apod. a zaseknutφ.
Borland C++ mß takovou malou ochranu proti program∙m od programßtor∙, kte°φ p°episujφ svß vlastnφ data nebo data jinde v pam∞ti. N∞kde nazaΦßtku u₧ivatelskΘho programu v pam∞ti je jak²si °et∞zec Borland C++ International (nebo tak n∞jak), kter² pokud se poniΦφ (p°ekladaΦ ani editor ani ₧ßdn² jin² program firmy Borland ho nep°episuje), proto se po skonΦenφ u₧ivatelskΘho programu testuje, zda je neponiΦenφ. V p°φpad∞, ₧e je poniΦen², potom ho nejspφÜ poniΦil u₧ivatelsk² program chybn²m adresovßnφm a vypφÜe se hlßÜenφ: "Null pointer assigment". Za normßlnφch okolnostφ byste se to asi nedozv∞d∞li a programßtor by mohl na chybu nep°iÜel. ProblΘm je v∞tÜinou, ₧e pracujete s n∞jak²m ukazatelem, kter² mß nulovou hodnotu a d∞lßte s nic n∞co, co p°epsalo zaΦßtek vaÜeho programu, proto₧e ukazatel mß nulovou hodnotu. DoporuΦuji v tom p°φpad∞ poΦφtaΦ resetovat, kdovφ co se jeÜt∞ poniΦilo...
Reßln² m≤d v DOSu: Adresa se udßvß jako segment a offset, ob∞ to jsou 16-ti bitovΘ Φφsla. Segmentovß Φßst adresy urΦuje adresu segmentu, kter² se pou₧ije k adresovßnφ (CS - code segment, DS - data segment, ES - extra segment, SS - stack segment) a offset je posun v∙Φi zaΦßtku segmentu. DΘlka segmentu je 64 kB. Fyzickß adresa v pam∞ti se urΦφ jako segment*16+offset. TakovΘto uspo°ßdßnφ je zastaralΘ a ji₧ se moc nepou₧φvß. Adresa je tedy segment:offset, v C++ lze ji zapsat jako 0x16AB:FF00. 0x???? znaΦφ Φφslo v ÜestnßctkovΘ soustav∞, ale to jsme si asi ji₧ uvßd∞li v konstantßch a pokud ne, tak u₧ to te∩ vφte.
Pam∞¥ovΘ modely v C++: V Borland C++ 3.1 je v option - code generation uvedeno n∞kolik pam∞¥ov²ch model∙. N∞kterΘ (kterΘ jsem pochopil) si popφÜeme.
Poznßmka: Po p°elo₧enφ programu a spuÜt∞nφ programu obsahuje v∞tÜina program∙ tyto Φßsti:
Tiny model: Je to model, ve kterΘm jsou data a k≤d v jednom segmentu, tedy do 64 kB. Programßto°i na ATARI 800 XL/XE a ostatnφch 8-mi bitov²ch poΦφtaΦφch si museli s tφmto omezenφm vystaΦit. Tento model se hodφ pro vytvß°enφ .com soubor∙ v DOSu. Pokud je program p°elo₧en v tomto modelu, potom pou₧φvß zßsobnφk DOSu (nemß vlastnφ). VÜechny ukazatele jsou blφzkΘ, registry CS=DS=ES
Small model: VÜechny ukazetele jsou blφzkΘ, i ty s new, zßsobnφk (stack) je v₧dy max. 64 kB. U₧ mß sv∙j vlastnφ.
Large model: VÜechny ukazatele jsou vzdßlenΘ.
Bli₧Üφ informace lze zφskat v nßpov∞d∞ (klßvesou F1). Ale to v∞tÜina programßtor∙ vφ, proto₧e v∞tÜinou lovφ nßzvy p°φkaz∙ a syntaxe, rozsahy Φφsel apod.
V malΘm modelu se dß definovat vzdßlen² ukazatel pomocφ far, _far, __far. Obdobn∞ blφzk² ukazatel se deklaruje near, _near, __near. V malΘm modelu dost dob°e nefunguje adresovß aritmetika, nebo¥ po p°iΦenφ 1 k 65535 nedostaneme 65536 (jak spoΦφtß dobr² matematik), ale nulu (Φemu₧ se matematik divφ :-( ), proto₧e je adresa 16-ti bitovß, tak₧e v∞tÜφ Φφslo ne₧ 65535 tu jaksi nejde zobrazit. Tak₧e poku ukazuje ukazatel na konec (n∞kem kolem 65534) a mßme ukazatel int near *f; a napφÜeme f++; dostaneme se na zaΦßtek segmentu, co₧ je nemilΘ a pokud tam n∞co zapφÜeme, nelze s urΦitostφ °φci, co jsme vlastn∞ p°epsali a zniΦili.
Upozorn∞nφ - definice huge ukazatel∙: Z definice adresy segment:offset je jasnΘ, ₧e adresa nenφ jednoznaΦnß. Z toho vypl²vß, ₧e tedy obecn∞ dost dob°e nefunguje, proto nenφ dobrΘ se v adresovΘ aritmetice spolΘhat na operßtory = < >. Proto se definovaly ukazatele huge, kterΘ jsou definovanΘ tak, ₧e pro ofset platφ: 0<=ofset<16. Potom je takto definovan² ukazatel ji₧ jednoznaΦn² a lze je porovnßvat a i rovnost je skuteΦn∞ jednoznaΦnß. Tak₧e znßme ji₧ t°i druhy ukazatel∙: far, near, huge. Tedy je dobrΘ pou₧φvat implicitnφ ukazatele, tj. nespecifikovat typ ukazatele (implicitn∞ v malΘm modelu jsou blφzkΘ (near) ukazatele, v velkΘm modelu jsou vzdßlenΘ (far) ukazatele. Pro porovnßvßnφ je dobrΘ pou₧φvat huge ukazatele.
Dereference *0: Toto nebezpeΦφ hrozφ hlavn∞ v malΘm modelu. Je to vlastn∞ prßce s nulov²m ukazatelem (ukazujφcφm a obsahujφcφm hodnotu 0). V tomto p°φpad∞ pracujeme s adresou DS:0. Proto je na zaΦßtku DS (data segmentu) nßpis Borland International, kterß slou₧φ pro kontrolu, zda tam n∞kdo n∞co nezapsal a tφm nßpis poruÜil. Toto umo₧nφ programßtor∙m zjistit, zda urΦit∞ zapsali n∞co pomocφ nulovΘho ukazatele a pokud ano, objevφ se hlßÜenφ: Null pointer assigment.
Poznßmka: Na OS Win95,NT se pracuje v chrßn∞nΘm re₧imu, kde OS i dokonce procesor hlφdß u₧ivatelsk² program, zda nßhodou nep°istupuje mimo data svΘho vlastnφho programu, pokud to zjistφ, objevφ se asi okno p°es celou obrazovku a program je ukonΦen bez ulo₧enφ dat, co₧ u₧ivatele nepot∞Üφ. N∞kdy se hlßÜenφ objevujφ dost Φasu, proto je dobrΘ vÜechno ve Win dost Φasto uklßdat, nebo¥ Win se hroutφ Φast∞ji, ne₧ programy v DOSu, nap°. kdy₧ odstartujete velkΘ mno₧stvφ program∙. V chrßn∞nΘm re₧imu je tedy zßkßzano Φφst a zapisovat mimo data svΘho programu, Φφst ani zapisovat do k≤du svΘho vlastnφho programu. Virtußlnφ pam∞¥ je pam∞¥, kterß se uchovßvß na disku a slou₧φ pro zv∞tÜenφ u₧ivatelskΘ pam∞ti, proto₧e Win a programy pro Win jsou "velcφ ₧routi" pam∞ti. Adresa v chrßn∞nΘm re₧imu je tepu: selektor:offset, selektor je 2B index v tabulce adres, je to obdoba segmentu, pouze zde je to index do tabulky s adresami segment∙ a ne adresa vlastnφho segmentu jako v reßlnΘm m≤du. Offset je 4B a je to posun v∙Φi zaΦßtku segmentu stejn∞ jako v reßlnΘm m≤du, pouze zde je 2x v∞tÜφ. V chrßn∞nΘm m≤du jsou vÜechny ukazatele blφzkΘ, pracuje se pouze s offsety
Pro studenty FJFI: Po nalogovßnφ na server katedry matematiky (lokßln∞ nebo globßln∞ na adrese tjn.fjfi.cvut.cz) si m∙₧ete namapovat nebo p°ejφt do adresß°e: KM1\DATA:\HOME\VIR\VYUKA\CPP\#ZAKL\01 a₧ 05, kde jsou n∞kterΘ z progrßmk∙ uvßd∞n²ch na p°ednßÜkßch Programovßnφ v C++ na FJFI - ╚VUT.
void * je ukazatel bez domΘnovΘho typu, je to obdova typu pointer z Pascalu.
P°φklad: Nech¥ mßme ukazatel v definovan² void *v; a p°ßli bychom si definovat funkci F, kterß bude podle hodnoty p°edanΘ jako parametr zm∞nφ n∞jak prom∞nnou libovolnΘho typu. Na prom∞nnou bude ukazovat ukazatel v: void F(void *v, int i) {if (i==1) {*(int*)v=10;} else {*(double*)v=3.14;}} P°φkazem - p°etypovßnφm (double*) zφskßme z neurΦitΘho ukazatele void * ukazatel ukazujφcφ na double, kter² pak referencφ *(double*)v=3.14 m∙₧eme zapsat do prom∞nnΘ tuto hodnotu. Obdobn∞ lze p°etypovat ukazatel void * na skoro libovoln² ukazatel.
Poznßmka - modifikßtor seg: Modifikßtor seg, _seg, __seg lze pou₧φt pro zφskßnφ segmetovΘ Φßsti ukazatele (adresy). P°φklad: int __seg*x; bude obsahovat segmentovou Φßst ukazatele x.
Lze p°φÜφt k segmentovΘmu ukazateli p°iΦφst blφzkß a zφskat tak vzdßlen². Na globßlnφ prom∞nnΘ ukazuje DS, proto₧e globßlnφ prom∞nnΘ jsou v data segmentu. Na lokßlnφ prom∞nnΘ ukazuje SS, proto₧e lokßlnφ prom∞nnΘ jsou v zßsobnφku (stack segment). N∞kterΘ prom∞nnΘ mohou b²t i v CS (code segment), ikdy₧ to neb²vß tak ΦastΘ, v∞tÜinou jsou to konstanty v instrukcφch. Pokud chceme pou₧φvat a implicitn∞ specifikovat p°φsluÜn² segment, ve kterΘm se prom∞nnß mß nachßzet, m∙₧eme pou₧φt modifikßtory _es,_ds,_cs,_ss, ale lepÜφ je se jim vyhnout.
Makro MK_FP: Makro MK_FP "make far pointer" jak u₧ napovφdß anglick² v²znam zkratky, ud∞lß vzdßlen² ukazatel ze dvou ukazetel∙ A:B. OpaΦn∞ lze zφskat ze vzdßlenΘho ukazatelele segmentovou a offsetovou Φßst makry FP_SEG, FP_OFF, fp je far pointer. Umo₧nφ rozklad vzdßlenΘho ukazatele.
Te∩, kdy₧ u₧ umφme r∙znΘ typy ukazatel∙ a m∙₧eme si tedy ukßzat prßci s obrazovou pam∞tφ na PC v textovΘm re₧imu. P°φm² p°φstup do videopam∞ti zrychluje prßci s obrazovkou, ne₧ zajiÜ¥ujφ pomalΘ slu₧by OS. V²hoda slu₧eb OS je univerzßlnost a tedy zaruΦenß funkΦnost na r∙zn²ch typech grafick²ch karet, nev²hodou je pomalost.
PopφÜeme si obrazovou pam∞¥ v textovΘm re₧imu 0, poΦßteΦnφ adresa je 0xB800:0 a je rozm∞r∙ 80x25 znak∙, ka₧d² znak je tvo°en 2B, prvnφm je ASCII k≤d a druh²m je barva. Byte barva je urΦena pro barvu pφsma (4 nejni₧Üφ bity), 3 bity pro barvu pozadφ znaku a 1 nejvyÜÜφ bit pro urΦenφ, zda znak blikß. V chrßn∞nΘm re₧imu je dobrΘ definovat zvlßÜtnφ selektor ukazujφcφ na videopam∞¥, zde se ovÜem n∞kdy m∙₧e OS brßnit zßpisu, proto v∞tÜinou zde nenφ ani prßvo zapisovat n∞kam jinak, ne₧ program m∙₧e. Jedinou mo₧nostφ je bu∩ pou₧φt sly₧by OS pro zßpis do videopam∞ti nebo si videopam∞¥ p°isvojit. V reßlnΘm a chrßn∞nΘm re₧imu lze pou₧φt nep°φmΘ adresovßnφ, kterΘ programßto°i v Assembleru znajφ, ostatnφ si nemesφ d∞lat moc starosti.
Grafick² re₧im zßvisφ na rozliÜenφ (podle toho je velikost pam∞ti) a poΦtu barev. Logicky si m∙₧eme odvodit, jak budeme adresovßnφ provßd∞t, rad∞ji pou₧ijeme reßln² m≤d, kdy nejsme omezovßni s tφm, kam m∙₧eme zapisovat a kam ne.PoΦßteΦnφ adresa v∞tÜinou b²vß na adresa 0xA000:0, ale nelze se na to spolehnout. Adresace se liÜφ podle maximßlnφho poΦtu barev, kterΘ je mo₧nΘ souΦasn∞ na obrazovce zobrazit. U 2 barevnΘho re₧imu p°ipadß na 1B videopam∞ti 8 pixel∙. Podobn∞ u 4 barevnΘho re₧imu p°ipadß na 1B videopam∞ti 4 pixely. Podobn∞ u 16 barevnΘho re₧imu p°ipadß na 1B videopam∞ti 2 pixely. Podobn∞ u 256 barevnΘho re₧imu p°ipadß na 1B videopam∞ti 1 pixel. VφcebarevnΘ re₧imy v∞tÜinou pou₧φvajφ pro 1 pixel 3B, kde je potom mo₧no zobrazit 16777216 barev. To ale v∞tÜinou lidskΘ oko ani nenφ schopnΘ rozliÜit, proto se asi nejvφce pou₧φvajφ 16-ti barevnΘ re₧imy (proto₧e standartnφ grafickß knihovna graph.h v C++ umo₧≥uje prßci s max. 16-ti barvami, pro vφce barev po₧aduje jin² bgi soubor. 256 barevbnΘ re₧imy se pou₧φvajφ nejlΘpe, proto₧e 1 bod - pixel je souΦasn∞ 1 B, tak₧e to umo₧≥uje velice rychlou prßci pomocφ videopam∞ti. P°iΦem₧ nejlepÜφ je asi re₧im 320x200 a 256 barev, kter² se vejde do 1 segmentu, co₧ umo₧≥uje rychlou prßci v Pascalu, C++, Assembleru, je tedy mo₧nΘ programovat rychlΘ zobrazenφ, co₧ dokazujφ programßto°i her.
Pro alokovßnφ pam∞ti v C a C++ lze pou₧φt funkci malloc, jejφ₧ prototyp lze nalΘzt v hlaviΦkov²ch souborech alloc.h a stdlib.h. Vracφ void *. Syntaxe: void * malloc(velikost);
V C lze napsat:
int *ui;
void main() {
ui=malloc(10*sizeof(int));
}
Ale v C++ nelze tento zßpis p°elo₧it, proto₧e C++ neumφ konvertovat (nebo je to ochrana proti chybßm), tak₧e je nutnΘ napsat:
int *ui;
void main() {
ui=(int*)malloc(10*sizeof(int));
}
Po alokovßnφ pam∞ti je tam smetφ. Dynamicky alokovanß pama¥ se nenuluje! ui[5]=11; zapφÜe do 6. prvku pole hodnotu 11 (v C, C++ jsou pole indexovanΘ od 0).
Poznßmka p°ednßÜejφcφho: Cht∞l jsem pou₧φt ji₧ naprogramovanou knihovnu s Fourierovou transformacφ, kterß byla p°evzata z jinΘho programovacφho jazyka (asi Fortrana). Ale n∞jak to nefungovalo a tak jsem hledal v Φem je chyba. Po n∞kolika hodinßch jsem chybu objevil a zjistilo se, ₧e pr² n∞jakß pod°adnß pracovnφ sφla p°episujφcφ programy z Fortranu do C nev∞d∞la, ₧e v C se indexujφ pole od 0, tak₧e tφm byl i Üpatn² v²sledek.
Moje poznßmka: Proto je dobrΘ si dßvat pozor na vÜechny to, co pou₧φvßte. V∞tÜina programßtor∙ nechce n∞co programovat a rad∞ji pou₧ije cizφ dφlo, ovÜem p°episem ani v∞tÜinou ani neotestuje, zda to funguje, proto₧e si asi myslφ, ₧e v tom nelze ud∞lat chybu. Dom²Ülivost se nevyplßcφ. To znßm takΘ ze zkouÜek na FJFI, kde m∙₧ete b²t n∞kdy, kdy₧ riskujete i vyhozenφ, tak₧e n∞kdy je lepÜφ si nechat napsat horÜφ znßmku (3) nebo riskovat na lepÜφ znßmku, ale zde op∞t hrozφ nebezpeÜφ, ₧e m∙₧ete (obΦas se takΘ stane) n∞co, s Φφm si nevφte rady. Ale n∞kte°φ zkouÜejφcφ jsou rßdi, kdy₧ nemusφ dßt vÜem jenom 3 a kdy₧ mohou dßt i lepÜφ znßmku. Ostatn∞ to je jen na Vßs. V∞tÜinou je ovÜem jednoduchΘ dostat dvojku, na jedniΦku byste asi m∞li um∞t i d∙kazy v∞t a vφce rozum∞t tematice. ZkouÜejφcφ na zkouÜkßch ostatn∞ poznß, jak Vßs to nauΦil a takΘ vy poznßte co umφte a neumφte. V∞tÜina zkouÜejφcφch je ovÜem dobrß, ₧e Vßm nechce dokazovat, co neumφte a tak Vßm to na zkouÜce vysv∞tlφ, tak₧e nenφ se vlastn∞ Φeho bßt.
ZruÜenφ dynamicky alokovanΘ pam∞ti: Provede se funkcφ free. Syntaxe free(ukazatel).
Opakovßnφ: Pam∞¥ lze dynamicky alokovat bu∩ pomocφ new a ruÜit pomocφ delete nebo pomocφ malloc a ruÜit pomocφ free.
V C se pou₧φvß calloc, c znamenß programovacφ jazyk C, kterß p∙vodn∞ vracela char *, proto₧e neexistoval void *, kde jako parametry jsou poΦet polo₧ek a velikost jednΘ polo₧ky v B. VÜechno si lze zjistit v nßpov∞d∞, stiskem Ctrl+F1, po napsßnφ calloc v editoru a umφst∞nφm kurzoru. Nebo Shift+F1 v indexu.
Rozd∞luje se jeÜt∞ alokace ze vzdßlenΘ haldy: far malloc a far free. V₧dy se tu pou₧φvß vzdßlen² ukazatel.
ZjiÜt∞nφ velikosti volnΘ pam∞ti v hald∞: Pou₧ije se funkce coreleft. Viz nßpov∞da.
Kopφrovßnφ dat: Pou₧ijeme bu∩ funkce movedata nebo movemem. LiÜφ se typem parametr∙, kterΘ lze zjistit v nßpov∞d∞, movemem se umφ vypo°ßdat se situacφ, kdy se kopφrovanΘ pole p°ekr²vajφ.
P°φklad ukazatele na objekty. Zkusφme s pomocφ ukazatele vyvolat metodu vypis().
Class X{int x,y; public: vypis();};
X *ux;
(* ux).vypis();
ux->vypis();
teΦka mß vyÜÜφ prioritu ne₧ *, proto bylo nutnΘ napsat (* ux).
struct - zßznam bez variatnφ Φßsti
unie - zßznam pouze s variatnφ Φßstφ
Poznßmka k hlaviΦkov²m soubor∙m: N∞kdy je problΘm, aby se hlaviΦkov² soubor nenatahoval vφcekrßt do pam∞ti, jeliko₧ by to mohlo zp∙sobit, ₧e n∞kterΘ t∞lo fuknce by bylo uvedeno vφcekrßt, co₧ zp∙sobφ chybu p°i p°ekladu: ╪eÜenφ:
#ifndef _TYP_H_
#define _TYP_H_
typedef int T;
#endif
Pokud nenφ makro TYP_H_ definovßno, potom se vytvo°φ a deklaruje se n∞jak² typ nebo se vlo₧φ jinß deklarace Φi hlaviΦkov² soubor. Vytvo°enφm makra je u₧ °eΦeno, ₧e to bylo ji₧ definovßno, tak₧e pokud je stejnß deklarace v jinΘm hlaviΦkovΘm souboru, makro ji₧ existuje a deklarace nebo nata₧enφ souboru bude p°eskoΦeno.
Konstruktor: Konstruktor provßdφ inicializaci objektu. Mßme objekt T:
class T {T(int d=0);};
T::T(int d) { };
V objektu T mßme konstruktor T, m∙₧eme ho tedy volat s parametrem typu int nebo takΘ bez parametr∙.
Seznam: PopφÜeme si p°φklad seznamu. SEZNAM.CPP (na serveru katedry
matematiky) obsahuuje implementaci seznamu, SEZNAM.H - obsahuje hlaviΦky funkcφ
pracujφcφch se seznamem. Je zde definovßn ukazatel "hlava", kter²
ukazuje na zaΦßtek seznamu. Seznam je definovßn jako seznam bez zarß₧ky, viz
zßklady algoritmizace (1.roΦnφk na FJFI).
N∞kolik poznßmek k progrßmku. << mß vyÜÜφ prioritu ne₧ ?:, proto se musφ
uzßvorkovßvat.
Seznam je pou₧it v programu na vypl≥ovßnφ plochy uzav°enΘho mnoho·helnφku,
zadanΘho jeho vrcholy.
Koho progrßmek zajφmß, m∙₧e si stßhnout archiv zde.
Program je dost komentovan², pro pochopenφ principu vypl≥ovßnφ mnoho·helnφku
dopuruΦuji navÜt∞vovat program poΦφtaΦovß grafika - ing. Milota. FJFI pro 4.
roΦnφk Tvorba software (ale p°ednßÜka je voln∞ p°φstupnß pro ostatnφ zßjemce...).
Funkce p°edßvanß jako parametr funkce: Rovn∞₧ se musφ zapsat void (*uf) (int,int) kv∙li priorit∞., co₧ je vlastn∞ ukazatel na funkci, kterß nic nevracφ a mß 2 parametry. Lze tak p°edat funkci jako param∞tr. Je potom mo₧nΘ p°i°azenφ uf=g; g je n∞jakß funkce, kterß nic nevracφ a majφcφ dva parametry int. Funkci je potom mo₧no volat dv∞ma zp∙soby: uf(10,15); funguje pouze v C++, (*uf)(10,15); funguje i v C i v C++. DalÜφ p°φklady definice: int * f() nebo int(*f())[5] tedy vracφ ukazatel na pole kv∙li priorit∞. Funkce m∙₧e vracet referenci, co₧ je vlastn∞ skoro jako vracenφ prom∞nnΘ odkazem.
DalÜφ povφdßnφ o funkcφch nßsleduje v kapitole o funkcφch.
Rekurzφ naz²vßme volßnφ funkce, pokud volß sama sebe. Rekurzi znßme z Pascalu a hezkΘ pou₧itφ (ikdy₧ ne moc inteligentnφ) je definice faktorißlu. Z matematickΘ anal²zy nebo diskrΘtnφ matematiky znßme posloupnosti zadanΘ rekurentn∞, tak₧e pokud chceme spoΦφtat n∞co zadanΘho rekurentn∞, musφme spoΦφtat vÜechny p°edchozφ Φleny posloupnosti. Pokud chceme tedy spoΦφtat faktorißl n!, potom musφme znßt faktorißl (n-1)! a vynßsobit ho n (podle definice), ale pokud ten neznßme (co₧ zpravidla neznßme), potom musφme spoΦφtat faktorißl (n-2)! a vynßsobit ho n-1, tak zφskßme (n-1)! a po vynßsobenφ n zφskßme n!. Tφmto zp∙sobem lze odvodit, ₧e n! se skuteΦn∞ rovnß 1.2....n. Ale jak jsme si ji₧ uvedli na zaΦßtku p°φklad faktorißlu, je jasnΘ, ₧e n∞kdy se poda°φ odstranit rekurzi, n∞kdy je to tak obtφ₧nΘ, ₧e v²pis programu by se dost zkomplikoval.
Rekurentnφ volßnφ fuknce: Nech¥ mßme funkci int f(int a, int b) { int c; } V tomto p°φpad∞ a,b,c neexistujφ, dokud se funkce nezavolß, co₧ je stejnΘ jako v Pascalu. Pokud si p°ejeme, aby c existovala po°ßd (a nebyla znovu vytvß°ena a ruÜena), deklarujeme ji jako static: static int c; Potom bude prom∞nnß c existovat po celou dobu b∞hu programu. OvÜem nenφ k nφ mo₧n² p°φstup vn∞ funkce, ve kterΘ je definovßna. Pozor - zrada: P°i rekurentnφm volßnφ se p°ema₧e, nebo¥ se znovu nevytvß°φ, jak jsme si °ekli. Proto je nutnΘ zachßzet s nφ opatrn∞.
Pam∞¥ovß t°φda auto je jeÜt∞ pro n∞kterΘ neznßmß. Nynφ u₧ nebude, proto₧e jφ vysv∞tlφm. P°ekladaΦ si u t°φdy auto t°φdu doplnφ sßm.
Pam∞¥ovß t°φda static je u₧ trochu znßmß, pou₧φvß se ve funkcφch, pokud si nep°ejeme, aby prom∞nnß p°estala existovat po opuÜt∞nφ funkce. Po dalÜφm volßnφ funkce se prom∞nnß ji₧ nevytvo°φ, proto₧e existuje od minulΘho volßnφ a pokud jeÜt∞ funkce nebyla volßna a prom∞nnß se nevytvo°ila, tak se vytvo°φ. Prom∞nnß p°esto nenφ znßma mimo t∞lo funkce.
Pam∞¥ovß t°φda extern se pou₧φvß pro deklarovßnφ prom∞nnΘ, kterß mß b²t pou₧itß, aby byla vid∞t mezi soubory. Mßm na mysli nap°φklad prom∞nnou, kterß se nachßzφ n∞kde v ji₧ p°elo₧enΘm obj souboru, co₧ lze pou₧φt v projektu, kdy dßme k dispozici pouze hlaviΦkov² soubor a p°elo₧en² obj soubor. Pokud tedy mß b²t n∞jakß prom∞nnß vid∞t z p°elo₧enΘho obj souboru, potom se zpravidla pou₧φvß deklarace extern tak, aby se prom∞nnß dala pou₧φvat.
Pam∞¥ovß t°φda register je pouze doporuΦenφ pro p°ekladaΦ, ₧e by mohl dßt prom∞nnou do registru. Je jasnΘ, ₧e je to pouze doporuΦenφ, proto₧e n∞jakΘ obrovskΘ pole nelze narvat do malΘho registru. Proto je to pouze doporuΦenφ pro p°ekladaΦ, ₧e by to mohl ud∞lat. Ale ve v∞tÜin∞ p°φpad∙ to poznß sßm p°ekladaΦ a narve jφ do registru sßm, aby se urychlilo provßd∞nφ program∙.
V Optimalization Options lze nastavit:
Rekapitulace: Pam∞¥ovß t°φda auto m∙₧e b²t lokßlnφ prom∞nnß nebo pro parametr funkce. Prom∞nnß static m∙₧e b²t globßlnφ nebo na ·rovni souboru, potom je vid∞t pouze v tom souboru. Deklaracφ prom∞nnß extern je °eΦeno, ₧e prom∞nnß e definovanß jinde (nejspφÜ v n∞jakΘm obj souboru). Prom∞nnß register je rada: Zkus vlo₧it do registru.
Je mo₧nΘ definovat i externφ funkci: extern int f(); V n∞kter²ch situacφch lze extern vynechat, ale v∞tÜinou je dobrΘ napsat extern, potom se zv∞tÜφ pravd∞podobnost, ₧e by mohl jφt program p°elo₧it a spustit. Lze takΘ definovat funkci static: static int f(int); Pou₧itφ je dobrΘ, pokud chceme, aby byla funkce vid∞t pouze v tomto souboru, pokud je program rozd∞len na vφce Φßstφ do soubor∙.
O p°φkazu return jsme si ji₧ n∞co °ekli. Zopakujeme si to, pro p°φpad, ₧e by to n∞kdo zapomn∞l. Nech¥ mßme t∞lo funkce. void f(...) { ... } Funkce f se ukonΦφ p°echodem p°es } nebo p°φkazem return. V Pascalu mßme v²stup funkce, ₧e ulo₧φme hodnotu do prom∞nnΘ, kterß se jmenuje stejn∞ jako funkce, Φφm₧ se ale v Pascalu funkce jeÜt∞ neukonΦφ a pokraΦuje dßl. Ale zde v C resp. C++ se funkce p°φkazem return ukonΦφ, a¥ u₧ je to ve funkci, kterß vracφ n∞jakou hodnotu (return hodnota;) nebo ve funkci bez v²stupu (v C resp. C++ se nepou₧φvß v²raz procedura), potom se hodnota nevracφ (return;). Ve void f nelze vrßtit hodnotu a opaΦn∞.
V C nelze definovat funkce se stejn²m jmΘnem. V C++ to lze, naz²vß se to p°etφ₧enφ funkcφ. Funkce se musφ liÜit typem parametr∙, ale ne nikoliv v²stupnφ hodnotou, to pro odliÜenφ funkcφ nestaΦφ (zap°em²Ülejte proΦ). Pokud jste na to nep°iÜli, je to proto, ₧e funkci m∙₧eme volat tak, ₧e jejφ v²stup zahodφme, tak₧e by p°ekladaΦ nev∞d∞l, jakou chceme z nich volat. TakΘ dalÜφm d∙vodem je, ₧e zde probφhajφ konverze a to by takΘ nestaΦilo k rozliÜenφ, kterou funkci vybrat.
V C neexistuje p°edßvßnφ prom∞nnΘ odkazam. Je mo₧nΘ pouze p°edat ukazatel na prom∞nnou. Ukß₧eme si, jak je nutnΘ naprogramovat v C swap:
void swap(int *a, int *b) {
int c = *a; *a=*b; *b=c;
}
void main() {
int y=11, z=98;
swap(&y,&z);
}
D∙sledek toho, ₧e v C neexistuje p°edßvßnφ prom∞nnΘ odkazem je knihovna graph.h, kterß byla p°enesena i do C++. Proto se i v C i v C++ p°i pou₧itφ knihovny <graph.h>, pou₧ije initgraph(&gd,&gm,"..."); - v Pascalu bylo initgraph(d,m,'...');.
Reference je skoro jako p°edßvßnφ prom∞nnΘ odkazem. Uvedeme si p°φklad funkce swap, kterou naprogramujeme dv∞ma zp∙soby, prvnφ mo₧nost je spφÜe z C ne₧ z C++, proto₧e se pracuje s ukazately na prom∞nnΘ (v minulΘ kapitole jsme si °ekli, ₧e C jeÜt∞ nemß p°edßvßnφ prom∞nn²ch odkazem, tak₧e je nutnΘ p°edßvat ukazatel na prom∞nnou). Druh² p°φklad funkce swap1 je naprogramovßna pomocφ prom∞nn²ch p°edßvan²ch odkazem - reference, tak₧e je urΦena pouze pro C++.
void swap(int *a, int *b) {
int c = *a;
*a = *b;
*b = c;
}
void swap1(int &a, int &b) {
int c=a;
a=b;
b=c;
}
void main() {
swap(&y,&z);
swap1(y,z);
}
Poznßmka: JeÜt∞ n∞co o prßci se segmentovou Φßstφ ukazatel∙. V²znam je jasn² z p°φkladu, mßme globßlnφ prom∞nnou i a lokßlnφ prom∞nnou (definovanou ve funkci main()).
int __seg *s;
int i;
void main() {
int j;
s=int(__seg*)&i;
s=int(__seg*)&j;
}
DalÜφ povφdßnφ o referencφch je zde.
Funkce - rozdφly C a C++: Pokud mßme definovanou funkci void f(inz a, int b, int c) {.....}, potom jφ m∙₧eme zavolat nap°. f(3,4,5); Ukß₧eme si, jak je mo₧nΘ deklarovat funkci, aby bylo mo₧nΘ vynechat n∞kter² z prom∞nn²ch.
Ve Fortranu je nap°. funkce graphor, kterß umo₧≥uje prßci s grafikou. graphor(1,.....) je asi otev°enφ grafiky a za nφm parametry, graphor(2) zav°enφ grafiky. My nynφ chceme n∞co podobnΘho naprogramovat. V minul²ch p°φkladech jsem to asi ji₧ pou₧il, ovÜem to nebylo vysv∞tleno. Uka₧me si to na deklaraci nßsledujφcφ funkce h:
int h(int x, double y=3.7, int *r = &i) { ..... } Funkci m∙₧eme volat nßsledujφcφmi zp∙soby:
h(3,d,&j) normßlnφ volßnφ
h(3,d) ... pokud ji takto zavolßme, p°ekladaΦ za chyb∞jφcφ parametr doplnφ implicitnφ hodnotu (proto se to tak jmenuje), kterß je urΦena v hlaviΦce funkce typu int *r = &i. Pokud tedy parametr chybφ, potom se doplnφ chyb∞jφcφ parametr. Potom to bude mφt stejn² ·Φinek jako volßnφ h(3,d,&i);
Metoda implicitnφch parametr∙ se pou₧φvß ve Windows, nap°. create window.
Moje poznßmka: Mß pr² tolik parametr∙, kterΘ nenφ v lidsk²ch silßch si je zapamatoval. Proto existuje vn∞jÜφ pam∞¥, metoda vn∞jÜφ pam∞ti se osv∞dΦuje, proto₧e vÜechno se asi do mozku nedß nacpat, tak₧e u zkouÜek se obΦas hodφ tahßky, tedy pomocnΘ papφry.
Metoda implicitnφch parametr∙ je v²hodnß v tom, ₧e nenφ t°eba vÜechny (jako otrok) opisovat. Mß to ovÜem nev²hodu v tom, ₧e pokud u n∞jakΘho parametru dßme implicitnφ hodnotu v hlaviΦce funkce (tedy umo₧nφme jeho vynechßnφ), potom musφ mφt vÜechny dalÜφ takΘ implicitnφ hodnotu. Rozmyslete si proΦ.
Moje poznßmka: Pokud jste ovÜem ji₧ unavenφ a p°emßÜlenφ jaksi nejde, tak se sna₧te se vzchopit a nynφ dßvejte pozor, pokud jste si to nerozmysleli. Kdo mß uÜi, tak slyÜ: Pokud tedy bychom vynechali n∞jak² parametr (m∞l by n∞jakou implicitnφ hodnotu), potom by se z toho p°ekladaΦ asi zblßznil, proto₧e zpracovßvß parametry zleva doprava a vynechßnφm parametr∙ by se vÜechny dalÜφ parametry posunuly doleva, tak₧e by ji₧ nesed∞ky ani typy parametr∙. Proto vynechßm-li n∞jak² parametr, potom musφm vynechat vÜechny nßsledujφcφ parametry, tak₧e pokud n∞jak² parametr mß implicitnφ hodnotu, potom musφ mφt i dalÜφ implicitnφ hodnotu.
Nesmφme opakovat deklaraci implicitnφch parametr∙, tzn. ₧e pokud mßme n∞ktde ji₧ hlaviΦku a jinde t∞lo funkce (nap°. v objektech, tam mßme v objektu hlaviΦky metod a dßle implemetaci), tak₧e jim v hlaviΦce implementace funkce u₧ nesmφme uvΘst implicitnφ parametr (vlasn∞ to m∙₧eme, ale takov² program se nep°elo₧φ bezchybn∞).
int f(int = 33);
// ....
int f(int y) {
return y+9;
}
void main() {
int i=f();
}
Pokud n∞jakß funkce nemß parametry, je dobrΘ napsat f(void). V∞tÜinou je to ovÜem stejnΘ jako f(). V C se n∞kdy °φkß, ₧e je to fuknce, o jejich₧ parametrech nehovo°φme. V C++ se °φkß, ₧e je to funkce bez parametr∙.
Prom∞nnΘ typu char, float, enum, bitovΘ pole se konvertujφ na int nebo unsigned int
V Kerningham C (no zde majφ v²hodu ti co Φetli ·vod, n∞kdy je dobrΘ si p°eΦφst alespo≥ ·vod a historii C a C++) jsou funkce bez prototyp∙, nem∙₧ou mφt parametry typu char, float a v²ΦtovΘ typy. OvÜem co je povoleno a co je zakßzßno, to ukß₧e konkrΘtnφ p°ekladaΦ.
Podle definice Kerningham - Richie existuje jakßsi definiΦnφ deklarace. Ukß₧eme si to na p°φkladu:
int f(a,b,c)
int a,b;
double c;
{
}
C toto toleruje, ale je to zakßzßno pro metody objektov²ch typ∙. Pokud zde nespecifikujeme typ prom∞nnΘ (jako v∞tÜinou) si program doplnφ int. Jako nev²hoda se n∞kdy uvßdφ, ₧e neznß typy parametr∙ a neumφ je p°evΘst. Je to nßchyln∞jÜφ k chybßm, kterΘ p°ekladaΦ asi neodhalφ.
Pod pojmem v²pustka mßme na mysli n∞co jako, ₧e vynachßme parametry funkce nebo takΘ ₧e je mo₧nΘ volat funkci s libovoln²m poΦtem parametr∙. PoΦet parametr∙ by m∞l b²t koneΦn² (nekoneΦn² poΦet parametr∙ by ₧ßdn² sebelepÜφ poΦφtaΦ nebyl schopen za koneΦnou dobu p°elo₧it, takΘ je tu omezenφ pam∞ti, hlavn∞ zde v p°ekladaΦi BC 3.1 v DOSu, kde mßme 640 kB zßkladnφ pam∞ti a Φßst extended/expanded memory.
P°φkladem v²pustky v C++ resp. C je funkce printf. Proto m∙₧e mφt libovoln² poΦet parametr∙ libovolnΘho typu. Zatφmco toto bychom v Pascalu t∞₧ko vym²Üleli, jak deklarovat funkci s libovoln²m poΦtem parametr∙, toto je v C++ mo₧nΘ. Citßt ing. M. Virius: "Co si m∙₧e v Pascalu dovolit autor p°ekladaΦe, to si u₧ nem∙₧e dovolit jeho u₧ivatel." V tento okam₧ik byla toti₧ °eΦ o naprogramovßnφ write, writeln v Pascalu, kde je mo₧n² libovoln² poΦet parametr∙, co₧ ovÜem mohl ud∞lat autor p°ekladaΦe, ne vÜak jeho u₧ivatel. DalÜφ citßt: "Zde v C++ platφ, ₧e to, co si m∙₧e dovolit autor p°ekladaΦe, m∙₧e si dovolit i u₧ivatel. U₧ivatel si m∙₧e skoro vÜechno naprogramovat sßm."
P°φklad: int printf(char * format, ...);
Upozor≥uji a zd∙raz≥uji, ₧e te∩ jsou ty t°i teΦky skuteΦnΘ (neznamenajφ p°φkazy Φi parametry), ale takto se deklaruje v²pustka. ╪φkß se, ₧e zde mohou b²t libovolnΘ poΦet parametr∙ libovolnΘho typu. Dßle je t°eba upozornit, ₧e funkce s v²pustkou musφ mφt alespo≥ jeden pevn² parametr. V∞tÜina u₧ivatelsky definovan²ch funkcφ s v²pustkou majφ jako prvnφ parametr poΦet parametr∙, ale nenφ to nutnΘ, nebo¥ printf tvo°φ vyjφmku, ta mß jako prvnφ formßtovacφ °et∞zec. Ale v tomto formßtovacφm °et∞zci jsou uvedeny typy prom∞nn²ch (tak₧e to udßvß i jejich poΦet), tak₧e se z toho lze dozv∞d∞t poΦet parametr∙ a jakΘho jsou typu. Jinak toti₧ ve funkci s v²pustkou nenφ mo₧nΘ kontrolovat typy parametr∙, nebo¥ vÜe je nechßno na u₧ivateli, ₧e ten vφ, jak se mß funkce volat, co₧ p°ekladaΦ p°edpoklßdß (n∞kdy je to ukvapen² p°edpoklad, ale to se p°ekladaΦ nedozvφ, dozvφ se to u₧ivatel, pokud se mu v printf vypisujφ podivnΘ ·daje, tak z toho usoudφ nap°. ₧e typ prom∞nnΘ ve formßtovacφm °et∞zci printf nenφ stejn² jako typ prom∞nnΘ, je₧ se p°edßvß za formßtovacφm °et∞zcem.
Probφhajφ konverze float -> double
Pro prßci s v²pustkou je urΦen hlaviΦkov² soubor <stdarg.h>. P°φklad: int max(int n,...) Tuto funkci hledajφcφ maximum koneΦnΘho poΦtu Φφsel se m∙₧e volat nap°. prom∞nnß=max(3,a,b,c); Je nutnΘ dßt v∞d∞t, kolik parametr∙ je na zßsobnφku a kolik se mß vyzvednout. Parametry je t°eba tedy vÜechny vyzvednout a v∞d∞t jakΘho jsou typu. Ale jak jsme ji₧ uvedli, p°ekladaΦ nekontroluje (ani to n∞jak nem∙₧e), jakΘ jsou parametry a jakΘho jsou typu. Ukß₧eme si schematicky znßzorn∞nφ stavu zßsobnφku.
Na zßΦßtek zßsobnφku ukazuje v reßlnΘm m≤du registr SS (stack segment), v Borland C++ 3.1 ukazuje na vrchol zßsobnφku BP. Potom se tak ulo₧φ 3 a vÜechny parametry funkce, kterΘ jsou nutnΘ p°ed vyvolßnφm funkce.
A nynφ si koneΦn∞ uka₧me, jak se z v²pustkou pracuje: Jak jsme ji₧ °ekli, zßkladem je pou₧itφ stdarg.h.
Nynφ m∙₧ete kliknout sem a vyzkouÜet si demonstraΦnφ p°φklad: V tomto p°φkladu je takΘ ukßzßno, co se stane, pokud nerespektujeme, ₧e funkce poΦφtß maximum pouze pro Φφsla int. Potom se samoz°ejm∞ poΦφtß maximum ze Üpatn²ch Φφsel.
Pokud nap°φklad pot°ebujeme, aby se v oknu Debug zobrazovalo u prom∞nnΘ typu char mφsto Φφsla znak, potom tam m∙₧eme zapsat: char(i), kde i bude prom∞nnß typu char.
Zde ve v²pustce se char konvertuje na int. P°i v²pustce p°ekladaΦ nevφ s kolika a jak²mi parametry bude funkce volßna.
V C je za ·klid parametr∙ zodpov∞dn² volajφcφ. Proto₧e volajφcφ vφ, co tam na zßsobnφk nacpal, tak₧e musφ vrßtit zßsobnφk v takovΘm stavu, jak² byl p°ed volßnφm. V∞tÜinou byst∞ m∞li vid∞t v p°elo₧enΘm programu SUB SP, POP CX, RET n - co₧ znamenß, ₧e p°i nßvratu vytßhni ze zßsobnφku nßvratovou adresu a n byt∙. V Pascalu je opaΦnΘ uklßdßnφ parametr∙ na zßsobnφk, tak₧e pokud si p°ejeme vytvo°it funkci, kterou budeme chtφt pou₧φvat v Pascalu, potom existuje mo₧nost, ₧e v deklaraci funkce v C resp. C++ napφÜeme:
Vracen²TypProm∞nnΘ pascal jmΘno(parametry) { t∞lo funkce };
Toto zp∙sobφ, ₧e parametry se budou uklßdat v opaΦnΘm po°adφ do zßsobnφku, tak₧e skuteΦn∞ p∙jde funkci pou₧φt v Pascalu. Je tu ovÜem problΘm, ₧e C++ znaΦn∞ komolφ jmΘna funkcφ, tak₧e n∞kdy Pascal ohlßsφ, ₧e funkce nebyla nalezena v p°elo₧enΘm obj souboru. Mimochodem jmΘno funkce se komolφ podle typu parametr∙ (funkce m∙₧e b²t p°etφ₧ena, tak₧e se n∞jak musφ od sebe odliÜit), p°idßvajφ se tam mnohΘ znaky (mimochodem, koho to zajφma, a¥ se podφvß do p°elo₧enΘho obj souboru). Proto doporuΦuji mφsto C++ pou₧φt C (soubor si mφsto *.CPP pojmenujete *.C a v Options - Compiler - C++ options - v p°epφnaΦi Use C++ compiler a¥ je zvoleno: CPP extension). C mΘn∞ (skoro jen n∞kdy) komolφ jmΘna, tak₧e je to mnohem jednoduÜÜφ. Kdysi jsme si na p°ednßÜce Programovßnφ v Pascalu ukazovali p°φklad p°evodu programu z C resp. C++ do Pascalu. OpaΦn² p°evod asi nenφ mo₧n² (asi podvodem), proto₧e Pascal asi neumo₧≥uje generovat *.obj a com soubory (pouze *.exe). Je vÜak mo₧nΘ v C i v Pascalu pou₧φvat *.obj soubory vytvo°enΘ v Assembleru. OvÜem programovßnφ v Assembleru je zdlouhavΘ a nudnΘ, lze vÜak vytvo°it krßtkΘ programy. Ikdy₧ n∞kdy i v C lze naprogramovat krßtk² program, vhodnΘ pro rezidentnφ programy.
Zopakujeme si: Funkci pro Pascal tedy deklarujeme nap°. int pascal f(parametry) { ....... } ┌mysln∞ jsem napsal do { } vce ne₧ 3 teΦky, aby se to nepletlo s v²pustkou, kterß mß prßv∞ 3 teΦky. N∞kdy lze pou₧φt i deklaraci typu: int __pascal f(............) {.............}; To asi v p°φpad∞, ₧e prvnφ zp∙sob nep∙jde. Ob∞ deklarace by m∞lo b²t asi stejnΘ.
D∙vodem komolenφ jmen v C++ je, ₧e funkce mohou b²t (a zpravidla takΘ jsou) p°etφ₧enΘ. OvÜem program v C++ se p°eklßdß do assembleru (v Options - Compiler - Code generation - v zaÜkrtßvacφch polφΦkßch Options - Generate assembler source lze zvolit generovßnφ programu v Assembleru, vytvo°en² program bude mφt stejnΘ jmΘno jako program *.C resp *.CPP a bude ulo₧en do Output directory). Ale v Assembleru vÜak nenφ mo₧nΘ p°et∞₧ovat funkce a procedury (tak dokonal² Assembler nenφ ani Pascal), tak₧e je nutnΘ rozliÜit funkce r∙zn²mi jmΘny podle parametr∙.
Situace komolenφ jmen si ukß₧eme u p°ekladaΦe Borland C++ 3.1 (u jinΘho p°ekladaΦe jsme si to toti₧ na p°ednßÜkßch (Programovßnφ v C++ - ing. M.Virius, Csc.) neukazovali, tak₧e si to ukß₧eme jen na tomto p°ekladaΦi.
D∙vodem existence PascalovskΘ volacφ konvence je, ₧e funkce ve Windows pou₧φvajφ Pascalovskou volacφ konvenci.
Jazykovß t°φda - modifikßtor:
P°i spuÜt∞nφ programu se n∞kterΘ programy spouÜt∞jφ s parametry. Nap°φklad znßme dir jmΘno souboru, command.com /c p°φkaz DOSu, help jmΘno p°φkazu, BP a BC jmΘno souboru, kterΘ chcete nahrßt do editoru. Parametry programovacφch jazyk∙ m∙₧e b²t nastavenφ prost°edφ. V Borland C++ a Borland nebo Turbo Pascalu existuje p°ekladaΦ jako samostatn² EXE soubor, ten se takΘ ovlßdß pomocφ parametr∙ z p°φkazovΘ °ßdky. Chci tφm naznaΦit, ₧e by bylo dobrΘ, kdybychom ty parametry um∞li n∞jak zjistit a podle toho ud∞lat n∞co ve svΘm u₧ivatelskΘm programu v C resp. C++. Budeme se bavit o p°ekladaΦi Borland C++ 3.1 pracujφcφ v DOSu.
Parametry z p°φkazovΘ °ßdky dostaneme tak, ₧e ve funkci main mohou b²t parametry. Zatφm jsme je nepot°ebovali, proto jsme psali main() bez parametr∙. Nynφ budeme psßt:
int main(int argc, char ** argv, char ** env)
argc je zkratka argument count, tedy poΦet parametr∙
argv je ukazatel na pole parametr∙ DOSu (??? jak je to u jin²ch OS, to nebylo na p°ednßÜce °eΦeno.)
env je ukazatel enviropment, tedy systΘmov²ch prom∞nn²ch DOSu. Ukß₧eme si demonstraΦnφ progrßmek, kter² si m∙₧ete stßhnout.
int main(int argc, char ** argv, char ** env) {
for (int i=0; i<argc;i++) cout << argv[i] << endl;
return 0;
}
Pokud si p°ejeme, aby parametr s mezerou byl brßn jako 1 parametr, je dßme do uvozovek, nap°. "a b n", potom se budou brßt jako 1 parametr. Parametr 0 je v₧dy jmΘno spuÜt∞nΘho programu. Ukazatel end je ukazatel na kopii systΘmov²ch prom∞nn²ch.
Tato kopie systΘmov²ch prom∞nn²ch je ulo₧ena v tzv. PSP - program segment prefix - Φesky: p°edpona programovΘho segmentu. Tuto Φßst si mohu zm∞nit sßm pro sebe, nap°. path, avÜak p∙vodnφ systΘmovΘ prom∞nnΘ se tφm neovlivnφ, proto₧e je to pouze kopie. Ka₧dß polo₧ka je ukonΦena znakem s ASCII k≤dem 0, proto₧e je to °et∞zec v C++, poslednφ polo₧ka je ukonΦena 2x ASCII k≤dem 0.
N∞kterΘ funkce jsou tak krßtkΘ, ₧e se u₧ vyplatφ je nevolat, ale vyplatφ se dßt jejich t∞la na mφsto volßnφ, mφsto toho, aby tam skok. Jednß se pouze o doporuΦenφ, stejn∞ jako register. Modifikßtor inline tedy doporuΦφ p°ekladaΦi, aby mφsto skoku do funkce, vlo₧il jej t∞lo na mφst∞ volßnφ. Odpovφdß to skoro jako makr∙m v C++ a je to stejnΘ jako makra v makroassembleru. Pouze s tφm rozdφlem, ₧e se zde jednß o funkce a ne makra.
Poznßmka k vracen²m hodnotßm funkcφ: Funkce nem∙₧e vracet pole ani funkci, je mo₧nΘ je vÜak vracet jako referenci, ukazatel kter² se dereferencuje.
Reference vytvß°φ L hodnotu, kterou lze napsat na levΘ stran∞ p°i°azovacφho p°φkazu. L je jako lev², left, proto₧e je na levΘ stran∞ p°i°azovacφho p°φkazu. Nßsledujφcφ p°φklad ukß₧e, jak je mo₧nΘ naprogramovat n∞co jako indexovßnφ. Tento jednoduch² progrßmek by bylo mo₧nΘ nap°φklad upravit pro kontrolu mezφ.
Poznßmka: Pokud chci v C++ pou₧φvat knihovnu z C: extern "c" void clrscr(); NapφÜu nßzev funkce, kterou chci pou₧φvat. Pokud je vφce funkcφ, potom napφÜu: extern "c" { ......... }. Pokud se podφvßte na n∞jak² hlaviΦkov² soubor, potom tam n∞co takovΘho uvidφte. Standartnφ hlaviΦkovΘ soubory jsou v adresß°i: BC31\INCLUDE
P°φkladem jsou hlaviΦkovΘ soubory conio.h, math.h, kterΘ chceme pou₧φvat jak v C tak i v C++. Proto byste tam m∞li vid∞t n∞co takovΘho (je to tam trochu slo₧it∞jÜφ...):
#if def __cplusplus
extern "c" {
#endif
double sin(double);
double cos(double);
#if def __cplusplus
}
#endif
Toto se p°elo₧φ v C++ takto: (Tam je definovßno makro _cplusplus.)
extern "c" {
double sin(double);
double cos(double);
}
Pokud se to p°eklßdß kompilßtorem C, potom se to p°elo₧φ takto: (Nenφ definovßno makro _cplusplus.)
double sin(double);
double cos(double);
Pokud napφÜeme extern f(int); a vynechßme vracen² typ, potom si p°ekladaΦ doplnφ int, ovÜem je lepÜφ se tomu vyhnout a rad∞ji tam typ psßt, je to toti₧ programßtorskß sluÜnost. Takto definovanou funkci potom m∙₧eme pou₧φt i v jin²ch modulech. Stejn∞ jako pokud napφÜeme: const c=11; i zde se doplnφ int.
C, C++ nedovoluje vno°enΘ funkce - lze napsat pouze prototyp.
V Trojance na sφti je progrßmek na obsluhu p°eruÜenφ. K tomu si, ₧e existujφ modifikßtory near, far, huge pro vÜechny typy ukazatel∙, kterΘ jsme si tu uvedli. M∙₧eme p°edepsat funkci jako blφzkou (funkce se bude volat pouze offsetem - 2B adresa) nebo vzdßlenou (funkce se bude volat pouze offsetem a segmetem - 4B adresa).
Adresa je tedy blφzkß nebo vzdßlenß. Blφzkß volßnφ jsou rychlejÜφ, proto₧e se p°edßvß pouze offset, kde₧to u vzdßlenΘ se p°edßvß offset i segment. V Assembleru znßme instrukce call near a call far. V C resp. C++ se volßnφ projevφ v knihovnßch, kterΘ majφ vÜechny funkce vzdßlenΘ. Proto je nutnΘ napsat far.
Poznßmka - Pascalovsk² typ Boolean: Z Pascalu znßme prom∞nnou reprezentujφcφ logickΘ hodnoty 0 = False, 1 = True. V C ani v C++ nenφ definovßn tento typ (snad s vyjφmkou nejnov∞jÜφch p°ekladaΦ∙). Ale to nevadφ, lze si ho dost dob°e definovat nap°φklad takto: enum bool (false=0,true); Logickß nula bude 0 a logickß jedniΦka bude 1, jak jednoduÜeji to definovat?
N∞co mßlo jsme si ji₧ °ekli o p°edßvßnφ funkce jako parametru funkcφ.
Existujφ dv∞ definice (zatφm jsme mohli programovat, ani₧ bychom znali n∞jakΘ definice) a to pole Kerningham a Ritchie a podle normy ANSI. TakΘ pravd∞podobn∞ znßme, jak p°edßvat parametr hodnotou (normßln∞) a odkazem (v C++ pomocφ referencφ &). Dßle takΘ asi znßme, ₧e v²sledek je mo₧nΘ vracet hodnotou (normßln∞) nebo odkazem (op∞t pou₧ijeme referenci).
Dßle asi vφme, ₧e existujφ rozdφly mezi C a C++, proto, pokud chceme pou₧φt v C++ funkci z C, zadßme extern "c", co₧ lze pou₧φt jak pro prom∞nnΘ, tak hlavn∞ pro funkce. Ale v hlaviΦkov²ch souborech to ji₧ ud∞lali za nßs, tak₧e se tφm nemusφme zab²vat. Pouze kdy₧ budeme programovat vlastnφ funkce a chceme je pou₧φvat jak v C tak i v C++, potom je dobrΘ si ud∞lat sv∙j vlastnφ hlaviΦkov² soubor, kter² ji₧ rozliÜφ, zda p°eklßdßme p°ekladaΦem C nebo p°ekladaΦem C++, vy°eÜφ se to podmφn∞n²m p°ekladem, jak je to, ji₧ jsme si uvedli a m∙₧ete se na to mrknout ve standartnφm adresß°i hlaviΦkov²ch soubor∙ (tedy pokud jste si ho nainstalovali).
TakΘ znßme inline, co₧ slou₧φ jako doporuΦenφ, ₧e se mß funkce mφsto skoku vlo₧it jejφ t∞lo na mφsto volßnφ. Dßle znßme oarametry t°φdy auto a register, takΘ znßme blφzkΘ a vzdßlenΘ funkce near a far. TakΘ vφme, jak deklarovat implicitnφ hodnoty pro parametry.
Dßle umφme takΘ pracovat s v²pustkou, jejφ₧ hlaviΦkov² soubor je stdarg.h. Vφme, co jsou funkce va_list ap;, va_start, va_arg a va_end.
Nynφ jeÜt∞ malΘ dopln∞nφ k parametr∙m typu funkce. P°φklad:
void f(int i) {
cout << "ahoj";
}
void (*Fun)(int);
Fun=f;
N∞kdy musφm volat ukazatelem na funkci s tφmto prototypem. P°φklad takΘ ukßzal, jak je mo₧nΘ p°i°azenφ prom∞nn²ch typu funkcφ. ╪ekli, jsme si takΘ, ₧e i funkce bez parametr∙ je nutno volat f() (narozdφl od Pascalu, kde je zßpis f() nep°φpustn²). TakΘ jsme si n∞co °ekli o rekurzi.
P°φklad: ZAKL\07\INTEG.CPP - p°edßvßnφ funkce jako parametr. Progrßmek mß za ·kol vypoΦφtat urΦit² integrßl funkce zadanΘ jako parametr. Funkce pro integrovßnφ je univerzßlnφ a jejφm parametrem je libovolnß matematickß funkce, kterß mß n∞jak² rozumn² v²stup. PoΦφtß integrßl s p°edepsanou p°esnostφ.
Ikdy₧ se nepoda°ilo nakreslit obrßzek se stejnou Üφ°kou obdΘlnφk∙, m∞ly by skuteΦn∞ b²t stejn∞ ÜirokΘ. Mßme tedy n∞jak² interval a na n∞m definovanou funkci. Integrßl se poΦφtß touto znßmou metodou - obdΘlnφkov²m pravdilem. UrΦit² integrßl je roven ploÜe mezi osou a k°ivkou. Proto lze ho spoΦφtat takΘ tak, ₧e plochu rozd∞lφme na obdΘlnφky a jejich plochy ji₧ umφme seΦφst. P°itom kdyby byly nekoneΦn∞ malΘ (limitn∞ nulovΘ), dostali bychom p°esnΘ numero, odpovφdajφcφ ploÜe mezi k°ivkou a osou - tedy urΦit² integrßl v intervalu <a,b>. Takto struΦn∞ byla popsßna metoda obdΘlnφkovΘ pravidlo.
Velk² matematik by °eÜil problΘm odliÜn∞. Vzal by si funkci, zintegroval by a pokud by se mu to poda°ilo (co₧ nelze zaruΦit), Φφm₧ by zφskal tzv. primitivnφ funkci. Plochu by potom zφskal jako rozdφl primitivnφ funkce v bod∞ b, od kterΘ se odeΦte hodnota primitivnφ funkce v bod∞ a. Nejv∞tÜφ problΘm je ovÜem nalΘzt primitivnφ funkci k zadanΘ funkce. Matematik (pokud by se ovÜem dopracoval k v²sledku a nestrßvil nad integracφ mnoho bezesn²ch nocφ), by zcela jist∞ zφskal skuteΦn∞ p°esn² v²sledek, proto₧e poΦφtßnφ na poΦφtaΦi p°inßÜφ chybu, kterß se p°iΦφtß k mnoha dalÜφm chybßm.
Zvolφme poloviΦnφ krok, pokud je rozdφl menÜφ ne₧ e, potom p°esnost je menÜφ ne₧ e. Potom ji₧ poΦφtßm jenom hodnoty mezi, proto₧e ty ostatnφ ji₧ mßm vypoΦφtanΘ. Takov²to postup zmenÜovßnφ obdΘlnφΦk∙ bychom mohli d∞lat tak dlouho, dokud mßme malou p°esnost. Uvedeme si jeÜt∞ hlaviΦku funkce pro integrovßnφ.
double Integral(double a, double b, double (*f)(double)) { ............. }; Funkci potom pou₧ijeme nap°φklad: cout << Integral(0,M_PI,sin) << endl; nebo cout << Integral(0,1,&F) << endl; kde F je n∞jakß specißlnφ funkce.
HUGE_VAL - udßvß nejv∞tÜφ zobrazitelnou hodnotu v double. Odpovφdß to nekoneΦnu, se kter²m je schopen poΦφtat. Pokud tedy tuto hodnotu n∞Φφm vynßsobφme, vyjde nßm op∞t, podobn∞ kdy₧ k nφ n∞co kladnΘho p°iΦteme, vyjde nßm takΘ tato hodnota. Chovß se jako matematickΘ plus nekoneΦno.
╚φslo Pφ: Pokud chceme zapsat tuto hodnotu, musφme natßhnout hlaviΦkov² soubor MATH.H. Konstanta se jmenuje M_PI, napsßnφm M_PI a stiskem Ctrl+F1 zφskßte i oznaΦenφ nßsobk∙ Φφsla pφ.
P°φklad: ZAKL\00\MAIN.CPP: Program slou₧φ k vypsßnφ prom∞nn²ch DOSu. Poslednφm prvkem je nulov² °et∞zec, kter² obsahuje jen znak s ASCII k≤dem 0 (tak₧e jsou 2 znaky s ASCII k≤dem 0 za sebou, proto₧e ka₧d² °et∞zec mß nakonci znak s ASCII k≤dem 0), co₧ je mo₧nΘ testovat jako druh² parametr ve for. JednoduchΘ? P°iΦem₧ char * * env je ukazatel na pole char∙ - °et∞zc∙. Primitivnφ p°φklad by mohl vypadat n∞jak takto jako tento p°φklad. DalÜφ hezk² p°φklad je u p°φkazu getenv a putenv v nßpov∞d∞ Borland C++ 3.1.
Mnoho programßtor∙ pou₧φvß C resp. C++ pro programovßnφ rezidentnφch program∙ (to jsou ty, kterΘ po svΘm ukonΦenφ jeÜt∞ z∙stanou v pam∞ti a n∞co d∞lajφ) a vir∙ (co₧ je specißlnφ p°φpad rezidentnφch program∙, proto₧e v∞tÜina vir∙ je skuteΦn∞ rezidentnφch).
Mezi systΘmovΘ programy pat°φ krom∞ rezidentnφch program∙ takΘ programy pro obsluhu p°eruÜenφ, mezi kterΘ pat°φ i ovladaΦe za°φzenφ. Ka₧dΘ za°φzenφ, kterΘ mßte p°ipojeno v DOSu, komunikuje pomocφ tzv. p°eruÜenφ, co₧ je ₧ßdost urΦenß pro procesor, aby dokonΦil prßv∞ provßd∞nou instrukci a p°edal °φzenφ jinam, proto₧e se n∞co stalo. Udßlosti, p°i kter²ch se generuje p°eruÜenφ je nap°φklad:
V Trojance na serveru jsou programy (pokud je n∞kdo nevymazal) KBD.CPP, INT8.CPP, INT8A.CPP, INT8B.CPP, kterΘ jsou pro obsluhu p°eruÜenφ z klßvesnice. P°eruÜenφ je generovßno, ikdy₧ program p°ejde do nekoneΦnΘho cyklu typu:
l: goto l; while (true) do ; to je v Pascalu
l: goto l; while (1) ; to je v C resp. C++
Po skonΦenφ obsluhy p°eruÜenφ se program navrßtφ zp∞t za instrukci, p°i kterΘ p°iÜlo p°eruÜenφ. Existujφ dva druhy p°eruÜenφ: Maskovatelnß a nemaskovatelnß. Maskovatelnß p°eruÜenφ lze zakßzat a op∞tovn∞ povolit instrukcemi CLI (zakßzßnφ), STI (povolenφ). Nemaskovatelnß p°eruÜenφ nelze zakßzat, proto₧e se budou v₧dy provßd∞t. Podle typu zdroje p°eruÜenφ rozeznßvßme hardwarovΘ a softwarovΘ. HardwarovΘ jsou ty, kterΘ jsou generovßny n∞jak²m za°φzenφm resp. ΦasovaΦem. SoftwarovΘ jsou generovßny n∞Φφm v programu (nap°. d∞lenφ nulou, krokovßnφ, u₧ivatelskΘ vyvolßnφ p°eruÜenφ nap°. instrukcφ INT, p°eteΦenφm podteΦenφm Φφsla v matematickΘm koprocesoru). Pokud zm∞nφme adresu obsluhy p°eruÜenφ a zadßme svou novou (nap°φklad virus, kter² chce p°evzφt kontrolu na systΘmem), potom je programßtorskou sluÜnostφ zavolat starou obsluhu, je pravda, ₧e to mnoho programßtor∙ o to nestarß, proto se n∞kterΘ programy zasekßvajφ a p∙sobφ krach systΘmu. Pokud vir nezavolß starΘ p°eruÜenφ, potom n∞co zpravidla p°estane fungovat a takov² vir nep°φmo prozradφ svou existenci.
M∙₧e b²t a₧ 16 hardwarov²ch p°eruÜenφ IRQ (interrupt request). V pam∞ti od adresy 0:0 je v DOSu tabulka p°eruÜenφ, 1 polo₧ka 4 B (2 B segment a 2 B offset) a jejich poΦet j 256, tak₧e tabulka zabφrß 1024. Mnoho programßtor∙, kte°φ tam nßhodn∞ n∞co zapsali si asi °φkajφ, proΦ ten m∙j program nefunguje a proΦ se zasekßvß? Dokud toti₧ nep°ijde p°eruÜenφ, tak se nic nestane, ale pokud p°ijde, poΦφtaΦ p°ed provedenφm p°eruÜenφ vybere odtud adresu a provede skok na adresu podle tabulky p°eruÜenφ. Kam skoΦφ? To nelze s urΦitostφ °φci. D∙sledkem je zpravidla zaseknutφ nebo n∞jakß nep°edvφdatelnß Φinnost. Rada: Neskßkejte a nenastavujte adresy, kterΘ sm∞°ujφ do ROM BIOS, nebo¥ ka₧dß ROM je jinß, mohly byste se nap°. strefit na podprogram pro hloubkovΘ formßtovßnφ disku, kter² tam n∞kde v ROM je.
P°ed zavolßnφm p°eruÜenφ se na zßsobnφk ulo₧φ CS (2B), IP (2B), FLAGS (2B) v tomto po°adφ. Nynφ si uvedeme n∞co z tabulky p°eruÜenφ a Φφslo p°eruÜenφ (adresa = 4*Φφslo p°eruÜenφ):
U hardwarov²ch p°eruÜenφ je nutnΘ upozornit OS, ₧e obluha p°eruÜenφ ji₧ byla ukonΦena. To se provede zßpisem hodnoty 0x20 na port 0x20 (ne na adresu), co₧ zp∙sobφ, ₧e budou p°ijφmßna dalÜφ hardwarovß p°eruÜenφ. Do tΘ doby jsou ostatnφ hardwarovß p°eruÜenφ blokovßny, tak₧e nelze napsat nic z klßvesnice, poΦφtaΦ tedy p°estane reagovat. Obsluha p°eruÜenφ by m∞la b²t spolehlivß, tak₧e by se poΦφtaΦ nem∞l b∞hem p°eruÜenφ zaseknout, potom nelze ani p°epnout do jinΘ aplikace, tak₧e nezb²vß ne₧ RESET. U obsluhy klßvesnice se v p°eruÜenφ musφ zapsat na port (ne na adresu) 0x61 hodnotu n∞jakou. Je to slo₧it∞jÜφ, najdete to v p°φkladu CPP\#ZAKL\09\!KBD.CPP void oziv_kl(). V p°eruÜenφ ud∞lßm v∞ci, kterΘ pot°ebuji a zavolßm starou funkci pro obsluhu p°eruÜenφ.
void interrupt int8(...) zde se pφÜe v²pustka - registry DOSu. Registry lze pou₧φt, ale zm∞n∞nΘ registry by se m∞ly obnovit po ukonΦenφ p°eruÜenφ. Pokud zavolßme starou obsluhu p°eruÜenφ, nemusφme se starat o o₧ivenφ klßvesnice, proto₧e to za nßm ud∞lß starß obluha p°eruÜenφ, pokud jφ zavolßme. Pokud ovÜem nechceme zavolat starou obsluhu p°eruÜenφ (nap°φklad softwarovß ochrana disku proti zßpisu), potom musφme ud∞lat skoro vÜechno to, co ud∞lala starß obsluha p°eruÜenφ, aby se poΦφtaΦ dal pou₧φt i po provedenφ p°eruÜenφ. Jinak je nutn² reset.
Novinka: N∞kde v tom souboru je prom∞nnß deklarovanß jako extern volatile. Jednß se o prom∞nnou, kterou lze m∞nit i mimo program, neoptimalizuje se, v₧dy je brßna z pam∞¥ovΘho mφsta.
V souboru !KBD.CPP je n∞co jako:
unsigned c=inportb(KbData);
if (c==1) {pocet++;oziv_kl();}
else StaKlav();
StaKlav() je vlastn∞ starß obsluha p°eruÜenφ, oziv_kl() je vlastn∞ funkce pro o₧ivenφ klßvesnice, kterou musφme zavolat, abychom rozchodili poΦφtaΦ, kdy₧ nezavolßme starou obsluhu p°eruÜenφ. Funkce inportb(port) Φte byte z portu.
V souboru INT8.CPP je n∞co jako prßce s klßvesnicφ a zßrovne≥ v²pis na videopam∞¥.
unsigned (*obr)[00]=(unsigned (*)[00]) MK_FP(0xB800,0);
Jak znßme z kapitoly o obrazovΘ pam∞ti 0xB800 je poΦßteΦnφ adresa obrazovΘ pam∞ti v textovΘm re₧imu 80x25 znak∙. Makrem MK_FP z n∞ho ud∞lßme vzßlen² ukazatel, kter²m potom m∙₧eme adresovat obrazovou pam∞¥ a tak tam n∞co zapisovat resp. Φφst.
outportb(unsigned port,unsigned char co_poslat); poÜle "co_poslat" (co₧ je zde 1 B) na hardwarov² port "port". Je to adresa portu 16-ti bitovΘ Φφslo = 2B (0 - 65535). Lze to pou₧φt pro p°φm² tisk na tiskßrnu, sΘriov² COM resp. paralelnφ port LPT - CENTRONICS nebo takΘ pro operace se zvukovou kartou, prßci s pam∞tφ C-MOS nebo takΘ pro o₧ivenφ klßvesnice v p°eruÜenφ, pokud nepou₧ijeme standartnφ obsluhu.
_setcursortype(int typ_kurzoru); Zm∞nφ tvar kurzoru v textovΘm re₧imu na PC v DOSu. To je ta standartn∞ blikajφcφ Φßrka. UrΦuje, kam se bude psßt znak. Pozici kurzoru v textovΘm re₧imu m∙₧eme nastavit void gotoxy(int x, int y); a zjistit sou°adnice lze zjistit funkcφ int wherex(void) x-ovou sou°adnici (vodorovnou) resp. int wherey(void) y-ovou sou°adnici (svislou). V grafickΘm re₧imu lze pozici nastavit funkcφ void far moveto(int x, int y); a zjistit lze x-ovou sou°adnici v grafickΘm re₧imu pou₧itφm funkce int far getx(void); a y-ovou funkcφ int far gety(void); . O grafice bude samostatnß kapitola. Pro pou₧φvßnφ standartnφch grafick²ch funkcφ pou₧ijte hlaviΦkov² soubor graphics.h. Funkce se zde v BC 3.1 skoro jmenujφ stejn∞ jako v Turbo / Borland Pascalu 6.0 resp. 7.0, pouze se pφÜφ s mal²mi pφsmeny. Rozdφl je u initgraph, ₧e se zde p°edßvß prom∞nnß odkazem, tak₧e musφme zadat referenci na prom∞nnou typu int u gd a gm (graphics driver a graphics mode). ╚astß chyba (proto ji rad∞ji ned∞lejte!), ₧e v cest∞ k BGI souboru zapomenete zdvojit \ !!! Tak₧e zatφmco v Pascalu pφÜete n∞co jako 'c:\bp\bgi', tak v BC31 napφÜete n∞co jako "c:\\bc31\\bgi". Zde v cest∞ m∙₧ete napsat i malΘ i velkΘ pφsmena, je to jedno, proto₧e se cesta p°edß DOSu, kter² malß a velkß pφsmena nerozliÜuje. Zatφmco jmΘna prom∞nn²ch je nutno dodr₧ovat a grafickΘ funkce je nutno psßt mal²mi pφsmeny. Budete se asi divit (zkuÜen∞jÜφ asi ne), ₧e Pascal 7.0 resp. 6.0 pou₧φvß jinΘ BGI soubory, kterΘ vzßjemn∞ nelze zam∞≥ovat, co₧ m∞ vadilo, kdy₧ jsem m∞l v Pascalu BGI soubor pro 256 barevnou grafiku a cht∞l jsem ho pou₧φt i v BC 3.1, tak₧e to skonΦilo chybou na funkci installuserdriver slou₧φcφ pro zavedenφ u₧ivatelskΘho ovladaΦe. Nynφ ji₧ mßm sprßvn² ovladaΦ pro 256 barevnou grafiku.
O referencφch jsme si ji₧ n∞co °ekli. Tak₧e asi u₧ vφme, ₧e na reference je mo₧no se dφvat jako na ukazazele, kterΘ se dereferencujφ. Znak * pro ukazatele se nahradφ znakem &, co₧ lze v∞tÜinou (a₧ na nßsledujφcφ vyjφmku) brßt jako p°edßvßnφ parametru odkazem, co₧ mnoho programßtor∙ pou₧φvß. Ale nenφ to totΘ₧. Ukß₧eme si p°φkalad:
#include <iostream.h>
#include <iomanip.h>
int i = 11;
int &ri = i;
void main()
{
cout << ri;
}
Moje poznßmka: Vlastnφ zkoumßnφ jazyka C++, kdy₧ jsem byl jeÜt∞ zaΦßteΦnφk... Stßhn∞te si archiv.
Moje poznßmka: P°ipravuje se druhß Φßst tΘto WWW strßnky, proto₧e editory www strßnek ji₧ p°estßvajφ um∞t generovat tak dlouhΘ WWW strßnky. Druhou Φßst najdete takΘ na mΘ strßnce pod jmΘnem http://www-troja.fjfi.cvut.cz/~sokolovs/CPP2.HTM
Poslednφ modifikace strßnek byla provedena - Last modified: