PREDMLUVA --------- C je univerzalni programovaci jazyk, ktery charakterizuji usporne vyrazy,  moderné rizené behő á strukturá udaju, bohatstvé operatoru. C nené jazyë "ná vysoke urovni˘ ané neni"velky˘ á nené specializovaî  ná  pouzité pouze v urcite  oblasti. Tato  obecnost jazyka C jej cini vhodnejsi a efektivnejsi pro mnohe ulohy nez jine "mocnejsi jazyky. Autorem jazyka C je Denis Ritchie, ktery puvodne koncipoval tento jazyk pro operacni system UNIX na pocitaci DEC PDP-11. Operacni system, prekladac jazyka C a vlastne vsechny aplikacni programy systemu UNIX /i veskere programove vybaveni pripravene pro tutu knihu/, jsou napsany v jazyku C. Ucinne prekladace existuji pro dalsi pocitace, napr. IBM System 1370, Honeywell 6000 a Interdata 8/32. Jazyk C neni svazan s urcitym konstruk- cnim resenim pocitacu. Tato kniha je koncipovana tak, aby pomohla naucit se ctenari programovat v jazyku C. Obsahuje ucebni uvod, umoznujici uziva- telum co nejrychlejsi zacatek, samostatne kapitoly tykajici se nejdulezitejsich rysu jazyka a prehled literatury. Uspech pri studiu bude zajisten zejmena ctenim, psanim a opakovanim uvede- nych prikladu, nez pouhym ucenim zakonitosti. Vsechny uvedene priklady tvori uplne, skutecne programy /nikoliv pouze casti/. Jsou spojeny s pruvodnim textem a jsou napsany ve vhodne poci- tacove forme. Krome ukazek, jak ucinne tento jazyk pouzivat, jsme se pokusili na vhodnych mistech ukazat uzitecne algoritmy a vhodny programovaci styl. Tato kniha neni uvodni programovaci priruckou, presto i no- vacek bude po prostudovani schopny se v jazyku C vyznat a orientovat, zvlaste kdyz mu pomuze zkusenejsi kolega. V nasich zkusenostech se jazyk C projevil jako prijemny, vy- razny a mnohostranny jazyk se sirokym pouzitim v ruznych pro- blemech. Snadno se uci a cim vice porostou vase zkusenosti s nim, tim lepe vam bude slouzit. Doufame, ze kniha vam pomuze uzivat C dobre a prospesne. Brian W. Kernighan Denis M. Ritchie KAPITOLA 0: UVOD ----------------- C je univerzalni programovaci jazyk. Je uzce spjat s opera- cnim systemem UNIX, protoze byl vyvinut v tomto systemu a take operacni system UNIX a jeho programove vybaveni je napsano v jazyku C. Jazyk C neni vsak vazan k urcitemu operacnimu systemu nebo urcitemu typu pocitace, prestoze je nazyvan "systemovym programovacim jazykem" a je pouzitelny pro psani operacnich systemu. Je stejne dobre pouzitelny i pro tvoreni programu numerickeho charakteru, programu pro zpracovani textu a hroma- dnych dat. Jazyk C je jazyk relativne nizke urovne. Tento jeho rys ne- snizuje vsak jeho vyznam; je tim receno, ze C pracuje se stej- nou tridou objektu jako vetsina pocitacu tj. se znaky, cisly a adresami. To muze byt kombinovano s obvyklymi aritmetickymi a logickymi operatory implementovanymi na konkretnim poci- taci. Jazyk C nema operace zpracovavajici primo slozene objekty jako jsou retezce znaku, seznamy nebo pole uvazovane jako ce- lek. Neni zde napr. analogie s operacemi jazyka PL/1, ktere zpracovavaji cela pole znaku. Jazyk umoznuje pouze staticke definice obsazeni pameti, neni zde moznost dynamickeho obsazeni pameti a obsazeni volnych mist jako v jazyku ALGOL 68. Konecne, C nema vybaveni pro vstupni a vystupni operace. Nema prikazy READ a WRITE a primy pristup k souborum. Vsechny tyto mecha- nizmy zname z vyssich programovacich jazyku musi byt vykonany explicitne volanim funkci. Jazyk C umoznuje pouze prime jednoduche rizeni behu progra- mu: testy, cykly podprogramy. V tomto jazyce neni mozne uvazo- vat o multiprogramovani, paralelnich operacich nebo synchroni- zaci. Presto,ze nepritomnost techto moznosti muze vypadat jako vazny nedostatek /"To mi chcete rici, ze musim zavolat funkci, kdyz chci porovnat dva retezce znaku?"/, tak udrzeni jazyka na nizsi urovni prinasi opravdu znacne vyhody. Protoze jazyk C je relativne maly, muze byt popsan na male plose a je snadne se mu naucit. Prekladac jazyka C muze proto byt jednoduchy a kompaktni a muze byt snadno vytvoren. Pouzitim soucasnych pos- tupu muze tvorba prekladace pro novy pocitac trvat pouze neko- lik mesicu, protoze 80% prekladace je shodnych jiz s existuji- cimi prekladaci. To je umozneno vysokym stupnem prenositelnosti jazyka. Protoze typy dat a struktury, jake jsou v jazyku C pou- zivany, jsou zajistovany vetsinou pocitacu, tak implementace knihoven je jednoducha. Napr. na PDP 11 obsahuje pouze pod- programy pro nasobeni a deleni 32-bitovych slov a podprogramy pro volani funkci a navrat z nich. Ovsem kazda implementace pocita s kompaktni knihovnou pro vstupy a vystupy, obsluhovani retezcu a operace s pameti. Protoze jsou vsak volany pouze explicitne, mohou byt pripojeny jen kdyz je treba. Rovnez mohou byt napsany v jazyku C. Programy v jazyku C jsou dostatecne efektivni a neni treba misto nich psat programy v assembleru. Jednim z takovych prikladu je operacni system UNIX, ktery je temer cely napsan v jazyku C. Navic veskery aplikacni software sys- temu UNIX je psan v jazyku C. Prevazna vetsina uzivatelu syste- mu UNIX /vcetne autoru teto knihy/ nezna assembler pocitace PDP - 11. Prestoze jazyk C pracuje na mnoha pocitacich, je nezavisly na architekture daneho pocitace, a tak je mozne s trochou pece psat "prenositelne" programy. V nasem oddeleni je software, ktery je vyvinut pod systemem UNIX, prenasen na pocitace HONEYWELL, IBM a Interdata system. Ve skutecnosti jsou prekla- dace a prostredky jazyka C na techto ctyrech pocitacich pod- statne kompatibilnejsi nez napr. ANSI standart FORTRAN. Pro programatory, kteri pracuji s jinymi jazyky, muze byt uzitecne pro srovnani se dozvedet o historickych, technickych a filozo- fickych aspektech jazyka C. Vetsina nejdulezitejsich myslenek jazyka C pochazi z dosti stareho, ale staleho ziveho jazyka BCPL, ktery byl vyvinut Martinem Richardsem. Vliv BCPL na C se uskutecnil neprimo jazy- kem B, ktery napsal Ken Thompson v r. 1970 pro prvni system UNIX na PDP-11. Prestoze jazyk C ma s BCPL mnoho spolecnych znaku, neni v zadnem pripade jeho kopii. Jazyky BCPL a B jsou jazyky bez "typu". Jediny typ dat je slovo pocitace a pristup k ostatnim druhum je pomoci specialnich operatoru nebo funkci. V jazyku C jsou zakladnimi datovymi typy znaky, cela cisla ruznych delek a cisla s pohyblivou desetinou carkou. Navic je zde hierarchie odvozenych datovych typu vytvorenych pointry, poli, strukturami, uniony a funkcemi. C umoznuje zak- ladni konstrukce pro rizeni behu, pozadovane pro dobre struk- turovane programy: shlukovani prikazu, rozhodovani /if/, cykly s testem na ukonceni nahore /while,for/ nebo dole /do/, klic vyberu /switch/. Vse jiz bylo implementovano v jazyku BCPL, ale s ponekud jinou syntaxi; BCPL dlouha leta ocekaval prichod "strukturovaneho programovani". Jazyk C umoznuje pouzivani pointru a aritmeticke adresy. Argumenty funkci jsou pri predani kopirovany a neni mozne, aby volana funkce zmenila hodnotu aktualniho parametru ve volajici funkci. Pokud pozadujeme "predavani adresou", muze byt predan pointer, a volana funkce muze zmenit objekt, na ktery pointer ukazuje. Pole jsou predavana adresou pocatku pole. Funkce mohou byt volany rekurzivne a lokalni promenne jsou prevazne "automaticke", tj. jsou vytvoreny znovu pri kaz- dem vyvolani. Definice funkci nemohou byt vkladany do sebe, ale promenne mohou byt deklarovany blokovou strukturou. Funkce jazyka C mohou byt prekladany samostatne. Promenne mohou byt interni dane funkci, externi a zname jen v jednom zdrojovem souboru a nebo uplne globalni. Interni vnitrni promenne mohou byt automaticke nebo staticke. Automaticke promenne mohou byt ukladany pro zvyseni efektivity do registru pocitace, ale prikaz register je pouze pokyn pro prekladac a ne primo pro pocitacovy registr. Jazyk C neni tak jednoznacny jazyk ve smyslu Pascalu nebo Algolu 68. Umoznuje datove verze, ale neprovadi automaticke konverze dat s velkou prehlizivosti jazyka PL/1. Dosud vytvo- rene prekladace nekontroluji pri vypoctu meze poli, typy argu- mentu atd. Pro situace, kdy je nezbytne kontrolovat typy dat, se pouzi- va specialni verze prekladace. Tento program se nazyva LINT. LINT negeneruje kod, ale misto toho prisne kontroluje vse, co je mozne kontrolovat pri behu a zavadeni programu. Nalezne nesouhlas typu, nekonzistentni pouziti argumentu, nepouzite nebo ocividne neinicializovane promenne atd. Program, ktery projde pres LINT si muze uzivat /s nekolika vyjimkami/ svobodu hlaseni chyb tak jako napr. programy v Algolu 68. O dalsich vlastnostech programu LINT se zminime, az k tomu bude prile- zitost. Jazyk C ma stejne jako ostatni jazyky svoje nedostatky. Nektere operatory maji nespravnou prioritu, nektera cast syn- taxe by mohla byt lepsi; existuje mnoho verzi jazyka, lisicich se od sebe. Nicmene se ukazalo, ze jazyk C je velice efektivni a vyrazny pro celou skalu aplikaci. Kniha je organizovana nasledujicim zpusobem: kapitola 1 tvori uvod do vyuky stredni partie jazyka C. Ucelem je zacit co nejrychleji, protoze pevne verime tomu, ze novy jazyk se nejlepe naucime, budeme-li v nem psat programy. Predpokladame zakladni znalost programovani. Neni zde vysvetlovano, co je to pocitac, prekladac ani co znamena vyraz n = n + 1. Prestoze jsme se pokouseli ukazat uzitecnou techniku programovani vsude, kde to jen bylo mozne, tak si nemyslime, ze tato kniha bude priruckou datovych struktur a algoritmu. Vzdy, kdyz jsme si mohli vybrat, jsme se soustredili na jazyk. V kapitolach 2 az 6 jsou probrany detailneji vlastnosti jazyka C. Duraz je stale kladen na tvorbu kompletnich uzitec- nych programu. Kapitola 2 pojednava o zakladnich typech dat, operatoru a vyrazu. V kapitole 3 se hovori o rizeni behu: if-else, while, for atd. Kapitola 4 popisuje funkce a struk- turu programu - externi promenne, oblast platnosti promennych atd. V kapitole 5 je diskutovano pouziti pointru a aritmetic- kych adres. Kapitola 6 obsahuje popis struktur a unionu. Kapitola 7 popisuje standardni knihovnu vstupu a vystupu, ktera zajistuje styk s operacnim systemem. Tato kniha je do- davana na vsechny typy pocitacu, kde pracuje jazyk C, takze programy mohou byt snadno prevadeny z jednoho systemu do dru- heho temer beze zmeny. KAPITOLA 1: UVOD K VYUCE ------------------------- Zacneme strucnym uvodem do jazyka C. Nasim cilem je ukazat zakladni prvky jazyka na skutecnych programech s tim, ze nebu- dou vynechavany detaily, formalni pravidla ani vyjimky. V tom- to okamziku se nesnazime o kompletnost. Snazime se umoznit cte- narum psat uzitecne programy tak rychle, jak jen je mozne. Soustredime se na tyto zakladni pojmy: promenne a konstanty, aritmetika, vetveni programu, funkce a zaklady vstupu a vystu- pu. Umyslne v teto kapitole vynechavame vlastnosti jazyka C, ktere jsou dulezite pro psani vetsich programu. Jedna se o pointry, struktury, vetsinu z bohateho rejstriku operatoru ja- zyka C, vetsinu prikazu pro podminene vetveni programu a dalsi podrobnosti. Tento pristup ma ovsem sve nevyhody. Zvlastnosti je to, ze kompletni popis urcite vlastnosti jazyka neni na jednom miste. Vzhledem k tomu, ze neni mozne pouzivat od zacatku vsechny moz- nosti jazyka C, priklady nejsou tak strucne a elegantni jak by mohly byt. Snazili jsme se tuto nevyhodu minimalizovat, ale presto na to ctenare upozornujeme. Dalsi nevyhodou je to, ze se budeme v nekolika clancich opa- kovat. Myslime si vsak, ze opakovani vam spise pomuze v uceni, nez ze vas bude obtezovat. Zkuseni programatori by meli byt v kazdem pripade schopni si z teto kapitoly vybrat to, co potrebuji. Zacatecnikum dopo- rucujeme, aby si studium teto kapitoly dopnili napsanim ma- lych programu, ktere jsou modifikaci programu uvedenych. 1.1 Zaciname ------------ Jediny zpusob, jak se naucit novy programovaci jazyk, je psat programy v tomto jazyku. Prvni program, ktery napiseme, je stejny pro vsechny jazyky: Vytiskni slova h e l l o , w o r l d To je zakladni prekazka; k tomu, abychom ji prekonali, musime byt schopni nekde vytvorit text programu, uspesne ho prelozit, sestavit a spustit, a potom zkontrolovat, zda vytvoril pozado- vany vystup. Vse ostatni je snadne. Program pro vytisteni textu "hello, world" vypada v jazyku C takto: main () { printf ("hello, world\n"); } Jak nalozit s timto programem zalezi na tom, jaky system pouzivate. Napr. v operacnim systemu UNIX je treba vytvorit zdrojovy text programu do souboru, jehoz jmeno ma priponu ".c" /napr. hello.c/ prelozit ho pouzitim prikazu cc hello.c Pokud jste neco nepokazili, preklad probehne bez chyb a vytvori se soubor a.out, ktery je mozno spustit prikazem a.out Vystupem bude text hello, world Na jinych systemech budou ovsem platit jina pravidla a ty je treba konzultovat s mistnim odbornikem. C v i c e n i 1-1. Spustte tento program na vasem systemu. Zkousejte vynechavat nektere jeho casti a pozorujte chybova hlaseni. Nyni neco o programu samem. Program v jazyce C, at je jeho velikost jakakoliv, sestava vzdy z jedne nebo vice "funkci", ktere maji byt vykonany. Funkce v jazyce C jsou obdobne fun- kcim a podprogramum v jazyce FORTRAN nebo proceduram v jazy- ce PASCAL, PL/1 atd. V nasem pripade je takovou funkci m a i n. Obycejne muzeme davat funkci libovolne nazvy, avsak nazev main je specialni nazev - vas program zahajuje svoji cinnost vzdy na zacatku funkce main. To znamena, ze kazdy program musi obsa- hovat jednotku main. Main se obvykle odvolava na ostatni fun- kce; nektere jsou primo obsazeny v programu, nektere se pouzi- vaji z knihoven jiz drive vytvorenych funkci. Jednou z metod vzajemne komunikace mezi funkcemi je preda- vani dat argumenty. Zavorky nasledujici za nazvem funkce ohra- nicuji seznam argumentu. V nasem prikladu je main funkci, ktera nema parametry. To je znazorneno symboly ( ) - prazdnym seznamem argumentu. Slozene zavorky { } zdruzuji prikazy, ktere vytvareji telo funkce. Jsou analogicke prikazum DO - END v ja- zyku PL/1 nebo prikazum begin - end v ALGOLU, PASCALU atd. Funkce je vyvolavana nazvem, za kterym nasleduje seznam argu- metu v kulatych zavorkach. Nepouziva se prikaz CALL jako ve FORTRANU nebo v PL/1. Zavorky musi byt uvedeny, i kdyz neob- sahuji seznam argumentu. Programova radka printf("hello, world\n"); je volanim funkce, ktera se jmenuje printf a jedinym argumentem je retezec znaku "hello, world\n". Printf je knihovni funkce, ktera zobrazuje vystup na terminal /jestlize neni specifikovano jine medium/. V tomto pripade zob- razi retezec znaku, ktery je jejim argumentem. Souvisla rada libovolneho mnozstvi znaku vlozena do uvo- zovek "...." je nazyvana znakovy retezec nebo retezcova konstanta. V teto chvili budou znakove retezce pouzivany jako argumenty funkci jako je napr. funkce p r i n t f. Dvojice znaku \n v retezci je v jazyku C symbolem pro znak nove radky, ktery kdyz je nalezen, posune kurzor na levy okraj nove radky. Kdybychom znaky \n neuvedli /coz mu- zeme udelat jako pokus/, uvidime, ze vystup neni ukoncen pre- sunem na novou radku. Uvedeni dvojice znaku \n je jediny zpusob prechodu na novou radku. Kdyz budete zkouset neco po- dobneho jako printf("hello, world "); tak prekladac jazyka C bude hlasit chybu /chybejici prave uvo- zovky/. Funkce printf nikdy nevykonava presun na novou radku automa- ticky, proto pro vytvoreni pozadovaneho vystupu muze byt pouzi- to vicenasobne vyvolani funkce printf. Nas prvni program muze tedy vypadat takto main() { printf ("hello,"); printf (" world"); printf ("\n"); } Vystup bude stejny jako v predeslem prikladu. Je treba si uvedomit, ze dvojice znaku \n reprezentuje pouze jeden znak; znak zmeny, (escape) oznaceny znakem \, umoznuje obecny a rozsiritelny mechanizmus pro reprezenta- ci tezko zobrazitelnych nebo neviditelnych znaku. Napr. \t je symbol pro tabelator, \b pro zpetny posun, \" pro uvozovky a \\ pro obracene lomitko samo. C v i c e n i 1-2. Pokuste se zjistit co se stane, kdyz retezec znaku, ktery je argumentem funkce printf, obsahuje \x, kde x je nejaky znak, ktery jsme vyse neuvedli. 1.2. Promenne a aritmetika -------------------------- Nasledujici program tiskne prevodni tabulku mezi stupni Fahrenheita a stupni Celsia za pouziti vztahu C = (5/9) . (F - 32) 0 -17,8 20 -6.7 40 4.4 60 15.6 ... .... 260 126.7 280 137.8 300 148.9 Program vypada takto: /*tisk prevodni tabulky Fahrenheit - Celsius pro f=0, 20, ..., 300*/ int lower, upper, step; main () { float fahr, celsius; lower = 0; /* dolni mez tabulky teplot */ upper = 300; /* horni mez */ step = 20; /* hodnota kroku */ fahr = lower; while(fahr <= upper) { celsius = (5.0/9.0) * (fahr - 32.0); printf ("%4.0f %6.1f\n",fahr,celsius); fahr = fahr + step; } } Prvni dve radky programu /*tisk prevodni tabulky Fahrenheit - Celsius pro f = 0,20,....., 300 */ jsou komentar, ktery v tomto pripade ve strucnosti vysvetluje cinnost programu. Libovolne znaky mezi /* a */ jsou prekladacem ignorovany. Komentare mohou a maji byt pouzivany pro zprehled- neni programu. Je dovoleno je pouzivat pro zprehledneni pro- gramu vsude tam, kde se jinak muze objevit mezera nebo novy ra- dek. V jazyku C musi byt vsechny promenne deklarovany pred prv- nim pouzitim, obvykle na zacatku funkce pred prvnim vykonnym prikazem. Kdyz zapomenete nejake promenne deklarovat, prekla- dac to bude hlasit jako chybu. Deklarace sestava z urceni typu a seznamu promennych. int lower, upper, step; float fahr, celsius; Typ i n t znamena, ze vsechny uvedene promenne budou celoci- selne promenne ; f l o a t znamena, ze se jedna o promenne s pohyblivou radovou carkou. Presnost a rozsah obou typu je za- visly na pouzitem typu pocitace. Napr. na pocitaci PDP-11 je int 16-ti bitove cislo se znamenkem a lezi v rozsahu -32768 a +32767. Cislo typu f l o a t je 32 bitove, ma 7 vyznamovych cislic a lezi v rozsahu 10 E-38 do 10 E+38. V kapitole 2 je uveden rozsah pro ostatni vybrane typy pocitacu. Dalsi z typu promennych v jazyce C jsou napr.: char znak - jeden byte short kratke cele cislo long dlouhe cele cislo double cislo s pohyblivou carkou a dvo- jitou presnosti Rozsah techto typu zavisi na pouzitem typu pocitace. Podrob- nosti jsou uvedeny v kapitole 2. Zakladni typy jsou rovnez pole, struktury, uniony, ukazatele na ne, a funkce, ktere s ni- mi pracuji. Se vsemi temito typy se postupne v textu shledame. Skutecny vypocet v programu pro vypocet prevodni ta- bulky mezi stupni Fahrenheita a stupni Celsia zaciname prirazenim lower = 0; upper = 300 ; step = 20 ; fahr = lower ; ktere nastavi pocatecni hodnoty promennych. Jednotlive pri- kazy jsou oddeleny strednikem. Protoze kazdy radek tabulky je pocitan ze stejneho vy- razu, muzeme pouzit cyklus, ktery je definovan prikazem w h i l e : while (fahr <= upper) { ... } Podminka v kulatych zavorkach je vyhodnocena. Jestlize je pravdiva /tj. promenna fahr je mensi nebo rovna pro- menne upper/, telo cyklu /tj. prikazy ohranicene slo- zenymi zavorkami { a }/ je vykonano. Potom je podminka znovu vyhodnocena a jestlize je opet pravdiva, telo cy- klu je opet vykonano. Jestlize je podminka nepravdiva /fahr je vetsi nez upper/ cyklus je ukoncen. Protoze jiz nejsou v nasem programu zadne prikazy, tak je pro- gram ukoncen. Telo prikazu w h i l e muze byt tvoreno jednim ne- bo vice prikazy vlozenymi do slozenych zavorek, jako je tomu v nasem programu, nebo jednim prikazem bez sloze- nych zavorek, napr. while (i < j) i = 2 * i; V obou prikladech prikazy, ktere jsou podmineny prikazem while, zacinaji jednim tabelatorem, takze je mozne na prv- ni pohled urcit, ktere prikazy jsou uvnitr tela cyklu. "Zubova" struktura textu programu zduraznuje logickou strukturu programu. Prestoze v jazyku C nezalezi prilis na pozici prikazu v textu, je vhodne zduraznovat logic- kou strukturu a uzivat mezery, abychom zduraznili clene- ni programu. Doporucujeme psat pouze jeden prikaz na radku a nechavat mezery okolo operatoru. Poloha zavorek neni jiz tak dulezita; my jsme si vybrali jeden z mnoha popularnich zpusobu. Vyberte si i vy svuj zpusob, ktery vam vyhovuje, a ten pak stale pouzivejte. Vetsina cinnosti naseho programu je vykonana v tele smycky while. Teplota ve stupnich Celsia je vypocitana a prirazena promenne celsius prikazem celsius = (5.0/9.0) * (fahr - 32.0); Duvod pro pouziti 5.0/9.0 misto jednodussiho 5/9 je ten, ze v jazyku C, stejne v jako mnoha dalsich jazycich, je vysledek celociselneho deleni o r e z a n , takze dese- tinna cast je ztracena. Proto vysledek operace 5/9 je nula a vsechny teploty by byly take nula. Desetinna tec- ka v konstante indikuje, ze se jedna o cislo s pohyb- livou radovou carkou, a proto 5.0/9.0 = 0.555..., coz je to, co jsme potrebovali. Rovnez piseme 32.0 misto 32, prestoze fahr je typu float a 32 by bylo automaticky zkonvertovano na float /na 32.0/ pred odecitanim. Je to vlastne jen otazka stylu, presto je vsak lepsi psat konstanty typu float s dese- tinnou teckou i kdyz maji celou hodnotu. Je tim zduraz- nen jejich typ i pro ctenare programu a zajistuje se i stejne chapani veci prekladacem. Podrobna pravidla pro to, zda budou celociselne kon- stanty konvertovany na typ float jsou uvedeny v kapitole 2. Nyni si vsimneme, ze prirazeni fahr = lower; a podminka while (fahr <= upper) funguje tak, jak ocekavame, tj. ze promenne typu i n t jsou konvertovany na typ f l o a t pred uskutecenim operaci. Tento priklad take podrobneji ukazuje, jak pracuje funkce printf. printf je vlastne obecne pouzitelna funkce pro formatovany vystup. Podrobne bude popsana v kapitole 7. Jejim prvnim argumentem je retezec znaku, ktery ma byt zobrazen a znak % urcuje, kam maji byt dalsi argumenty /druhy, treti.../ umisteny a jakym zpusobem maji byt tis- teny. Napr. v prikazu: printf ("%4.0f %6.1f\n" , fahr, celsius); specifikace %4.0f znamena, ze cislo typu float ma byt zobrazeno s delkou ctyr znaku a nema mit desetinnou cast. %6.1f urcuje, ze dalsi cislo bude v delce 6 znaku s jednou cislici za desetinnou teckou: tato specifikace je obdobou formatove specifikace f6.1 ve FORTRANU nebo specifikace f(6,1) v jazyku PL/1. Nektere casti specifikace nemusi byt uvedeny, napr. %6f urcuje, ze cislo ma byt alespon 6 znaku dlouhe; %.2f pozaduje dve mista za desetinnou teckou a cel- kovy pocet znaku neni omezen: %f specifikuje pouze tisk cisla typu float. Printf rovnez rozpoznava %d pro desetinne cele cis- lo, %o pro oktalovou reprezentaci, %x pro hexadecimalni repre- zentaci, %c pro znak, %s pro retezec znaku a %% pro % samo. Kazdy znak % v prvnim argumentu funkce printf je spojen jen s odpovidajici druhym, tretim,... argumentem. Specifikace musi byt presne popsana cisly a typem, jinak dava program nes- myslne vysledky. Mimochodem funkce p r i n t f neni casti jazyka C. Sam ja- zyk C nema definovane operace vstupu nebo vystupu. Funkce printf neni obestrena nejakymi kouzly, je to jen uzitecna funk- ce, ktera je casti standartni knihovny funkci jazyka C. Abychom se mohli soustredit pouze na jazyk C nebudeme az do kapitoly 7 mnoho uvadet o vstupne-vystupnich operacich. Odlozime take do te doby pojednani o formatovanem vstupu. Kdyz budete chtit za- davat cisla jako vstup, prectete si o funkci s c a n f v kap. 7 odstavec 7.4. Scanf je funkce obdobna jako printf, az nato, ze cte vstup misto psani vystupu. C v i c e n i 1-3. Upravte program pro konverzi teplot tak, aby vytiskl zahlavi tabulky. C v i c e n i 1-4. Napiste program, ktery bude pocitat stupne Celsia v zavislosti na stupnich Fahrenheita. 1.3 Prikaz for -------------- Jak je mozno ocekavat, je mnoho zpusobu, jak napsat program; uvedeme jinou variantu naseho programu pro konverzi teplot main () /* tabulka prevodu Fahrenheit-Celsius/ { int fahr for (fahr = 0; fahr <= 300; fahr = fahr + 20) printf("%4d %6.1f\n", fahr, (5.0/9.0) * (fahr - 32)); } Vysledek bude stejny, ale program vypada jinak. Jednou z hlav- nich zmen je zmenseni poctu promennych. Jedina promenna, ktera zustala, je celociselna promenna f a h r /celociselna proto, abychom mohli ukazat, jak pracuje konverze %d v printf/. Dolni a horni mez a hodnota kroku step se objevuji jenom jako kons- tanty prikazu for, ktery je pro nas novinkou. Vyraz pro vypocet prevodu se nyni vyskytuje na miste tretiho argumentu funkce Posledni zmena je prikladem obecneho pravidla jazyka C; v kteremkoliv miste, kde se muze vyskytnout promenna, je mozne pouzit vyraz stejneho typu. Protoze treti argument funkce printf ma byt typu float, aby jej bylo mozno zobrazit konverzi %6.1f, tak na jeho pozici se muze pouzit vyraz v pohyblive radove carce. Prikaz f o r je podmineny prikaz, ktery je zobecnenim prika- zu w h i l e. Jestlize jej porovname s prikazem while, tak jeho cinnost by nam mela byt jasna. Sestava ze tri casti, ktere jsou od sebe oddeleny stredniky. Prvni cast, iniciace fahr = 0; je provedena jednou na zacatku. Druha cast je podminka, ktera ridi cyklus fahr <= 300; Podminka je vyhodnocena; jestlize je pravdiva, tak prikazy tela cyklu for jsou vykonany /v nasem pripade je to pouze jedno vy- volani funkce printf/. Potom je vykonana treti cast prikazu for, reinicializace, fahr = fahr + 20 a znovu je vyhodnocena podminka v druhe casti prikazu for. Cy- klus je ukoncen, jestlize je podminka nepravdiva. Stejne jako u prikazu while muze telo cyklu tvorit jeden nebo skupina pri- kazu, ktere jsou uvnitr slozenych zavorek. Inicializace a reinicializacce mohou byt jednoduche vyrazy. Je lhostejne, pouzijeme-li v programu prikaz for nebo while, ale vzdy se snazime volit tu variantu, ktera vypada jasneji. Prikaz for je vhodny obycejne v takovych prikladech, kdy inicializace a reinicializace jsou jednoduche, logicky svazane prikazy. Prikaz for je tomto pripade kompaktnejsi, protoze prikazy pro rizeni cyklu jsou zde pohromade. C v i c e n i 1-5. Modifikujte program pro konverzi teplot tak aby, tiskl tabulku v opacnem poradi tj. od 300 stup.F do 0 stup. F. 1.4 Symbolicke konstanty ------------------------ Predtim, nez opustime nas program pro konverzi teplot, ucin- me jeste zaverecnou poznamku. Je spatne "pohrbivat magicka cis- la" jako jsou cisla 300 a 20 v programu, protoze neposkytuji tem, kteri budou program cist nebo upravovat, systematickou in- formaci. Nastesti existuje v jazyku C zpusob, jak se temto "magickym cislum" v programu vyhnout. Na zacatku programu je totiz mozne popisem # d e f i n e definovat retezce znaku jako symbolicka jmena nebo symbolicke konstanty. Potom prekladac nahradi tato jmena odpovidajicimi retezci znaku vsude tam, kde se objevi. Nahradou muze byt skutecne libovol- ny text, nejen cisla. #define LOWER 0 /*dolni mez tabulky*/ #define UPPER 300 /*horni mez*/ #define STEP 20 /*hodnota kroku*/ main () /*tabulka prevodu Fahrenheit - Celsius/ { int fahr for (fahr=LOWER; fahr <= UPPER; fahr=fahr + STEP) printf("%4d %6.1f\n", fahr, (5.0/9.0) * (fahr-32)); } Polozky LOWER,UPPER,STEP jsou konstanty a proto se neobjevuji v deklaracich. Symbolicka jmena je vhodne psat velkymi pisme- ny, aby je bylo mozno jednoduse odlisit od nazvu promennych, ktera jsou psana pismeny malymi. Uvedomme si, ze na konci popisu #define neni strednik, protoze vse, co se objevi za symbolickym jmenem je za nej v programu dosazovano. 1.5 Vyber uzitecnych programu ----------------------------- Uvazujeme nyni o skupine programu, ktere budou vykonavat jednoduche operace se znaky. Zjistime potom, ze mnohe z pro- gramu jsou jen rozsirenim techto zakladnich programu. Vstup a vystup znaku -------------------- Standardni knihovna obsahuje funkce pro cteni a vypsani znaku. Funkce g e t c h a r nacita vzdy dalsi znak pokazde, kdyz je vyvolana a jako hodnotu vraci tento znak. Takze po prikazu c = getchar() obsahuje promenna c dalsi znak ze vstupu. Znaky jsou obvykle zadavany z klavesnice, ale to nas az do 7. kapitoly nemusi zajimat. Funkce p u t c h a r je doplkem funkce getchar putchar (c) Tato funkce vytiskne obsah promenne c na nejake medium-obycejne na obrazovku. Volani funkce putchar a printf muzeme kombinovat; vystup se objevi v tomto poradi, jak byly funkce volany. Stejne jako v pripade funkce printf neni na funkcich getchar a putchar nic magickeho. Nejsou soucasti jazyka C, ale jsou z neho dosa- zitelne. Kopirovani souboru ------------------ Znate-li funkce getchar a putchar, muzete napsat mnoho uzi- tecnych programu, aniz budete vedet neco dalsiho o operacich vstupu a vystupu. Nejjednodussim prikladem je program, ktery kopiruje vstup do vystupu po jednom znaku. Vyvojove schema vypada takto: precti znak while (znak neni symbol pro konec souboru) vypis znak precti dalsi znak Program v jazyku C bude vypadat takto: main () /*kopirovani vstupu na vystup: 1.verze*/ { int c; c = getchar(); while (c != EOF) { putchar (c); c = getchar(); } } Operator != znamena "nerovna se". Hlavnim problemem je zjistit, byl-li nacten konec vstupu. Obvykle je dano konvenci, ze kdyz funkce getchar narazi na ko- nec vstupu, tak vraci hodnotu, ktera neni normalnim platnym znakem. Jediny, ale zanedbatelny problem je to, ze existuji dve konvence pro indikaci konce souboru. My jsme se teto neprijem- nosti vyhnuli tim, ze pouzivame symbolicke jmeno EOF pro hod- notu "konec souboru", at uz je jakakoliv. V praxi je EOF bud rovna -1 nebo 0, a proto musi byt na zacatku programu definice #define EOF -1 nebo #define EOF 0 Pouzitim symbolickeho jmena EOF, ktere reprezentuje hodnotu funkce getchar pro nacteni konce vstupu, jsme si zajistili, ze jen jedina radka v programu zavisi na konkretni ciselne hodnote. Soucasne musime deklarovat promenou c typu int, ne c h a r, aby mohla obsahovat hodnotu jakou funkce getchar vraci. Jak potom uvidime v kapitole 2, tato funkce je skutecne typu int, protoze musi byt schopna vracet nejen znaky, ale take repre- zentaci symbolu EOF. Program pro kopirovani muze byt zkusenejsimi programatory v jazyku C napsan strucneji. V tomto jazyku muze byt kazdy pri- kaz, jako napr. c = getchar() pouzit ve vyrazu, jehoz hodnota je proste rovna hodnote, ktera je prirazovana leve strane vyrazu. Jestlize je prirazeni znaku promenne c vlozeno na pozici podminky v prikazu while, tak program pro kopirovani souboru muze byt napsan takto: main /* kopirovani vstupu ve vystup 2.verze */ { int c; while((c = getchar()) != EOF) putchar (c); } Program precte znak, priradi ho promenne c a testuje, zda tento znak byl priznakem konce souboru. Jestlize nebyl, tak telo cyklu while je vykonano. Testovani se znovu opakuje. Kdyz se narazi na konec souboru, prikaz while, stejne jako funkce main, ukonci cinnost. Tato verze programu soustreduje vstup na jedno misto - nyni je uz jen jedno volani funkce getchar - a zkracuje text programu. Vlozeni prirazovaciho prikazu do testovaciho vyra- zu je jednou z moznosti, kdy jazyk C umoznuje vyznamne zkrace- ni textu programu. /Je mozne se o tuto moznost nestarat a a vytvaret "nevnorujici se" text programu, ale tomu se budeme snazit vyhybat./ Je dulezite si uvedomit, ze vlozeni prirazovaciho prikazu do zavorek je opravdu nezbytne. Priorita operatoru != je vyssi nez prirazeni = , z cehoz vyplyva, ze pri nepritomnosti zavo- rek bude prikaz != vykonan drive nez prirazeni =. Proto prikaz c = getchar() != EOF je shodny s prikazem c = (getchar() != EOF) To ma za nasledek to, ze promenne c bude prirazena hodnota 0 nebo 1 podle toho, zda funkce getchar narazila na konec sou- boru nebo ne /vice o teto problematice bude uvedeno v kapito- le 2/. Pocitani znaku -------------- Nasledujici program pocita znaky. Je to uprava programu pro kopirovani. main() /*pocitani znaku na vstupu*/ { long nc; nc = 0; while (getchar()! = EOF) ++nc; printf("%1d\n",nc); } Prikazem ++nc uvadime novy operator, ++, ktery provadi zvetseni o jednicku. Muzeme ovsem rovnez napsat nc = nc + 1, ale ++nc je strucnejsi a mnohem efektivnejsi z vypocetniho hlediska. --je obdobny ope- rator pro odecitani jednicky. Operatory ++ a -- se mohou obje- vit bud pred promennou /++nc/ nebo za ni /nc++/. Tyto dva tvary maji ve vyrazech ruzny vyznam, jak uvidime v kap. 2, ale jak ++nc tak i nc++ zvetsuje promennou nc o jednicku. V teto chvili budeme psat tyto operatory pred promennymi. Program pro pocitani znaku scita znaky v promenne nc, ktera je typu l o n g i n t /promenna s dvojnasobnym poctem by- tu/. Maximalni mozna hodnota, kterou muze nabyt promenna typu int je na pocitaci PDP-11 32767 a muze se proto snadno pri po- citani znaku preplnit; na pocitacich IBM jsou promenne typu long a int identicke a mnohem vetsi. Vystupni konverze %1d umoznuje tisk promennych deklarovanych jako long int. Chceme-li pocitat s mnohem vetsimi cisly, muzeme pouzit promenne typu d o u b le /promennych s pohyblivou carkou, ktere maji proti promennym typu float dvojnasobnou delku/. Take pouzijeme prikaz for misto prikazu while, abychom ukaza- li jiny zpusob tvorby cyklu. main() /*pocitani znaku na vstupu*/ { double nc; for(nc = 0; getchar() != EOF; ++nc) ; printf("%.0f\n", nc); } Specifikace %f ve funkci printf je pouzivana jak pro promenne typu float, tak i typu double. %.0f potlacuje tisk neexistujici desetinne casti cisla nc. V tomto programu je telo cyklu for prazdne, protoze veskera cinnost je soustredena na testovani a reinicializaci. Ale gra- maticka pravidla jazyka C vyzaduji, aby telo cyklu for existo- valo. Izolovany strednik zde predstavuje prazdny prikaz, a tak je pravidlo splneno. V programu jej piseme na samostatnou rad- ku, abychom si ho snadneji vsimli. Predtim, nez program pro pocitani znaku opustime, vsimneme si, ze kdyz vstup neobsahuje zadne znaky, tak je podminka cyk- lu while nebo for nepravdiva hned pri prvnim vyhodnoceni a promenna nc je rovna nule, coz je spravny vysledek. Jednou z prijemnych vlastnosti prikazu while a for je to, ze podminka je testovana na zacatku cyklu, predtim, nez je vykonano telo cyklu /na rozdil od jazyka FORTRAN - pozn.prekl./. Program pocita spravne, i kdyz vstup nenabizi "zadne znaky". Pocitani radek -------------- Nasledujici program pocita radky na vstupu. Predpokladame, ze radky jsou od sebe oddeleny znakem \n. main() /*pocitani radek*/ { int c, nl; nl = 0; while((c = getchar()) != EOF) if(c == '\n') ++nl; printf("%d\n", nl); } Telo cyklu while nyni obsahuje prikaz if, ktery ridi prirustek ++nl. Prikaz i f testuje podminku v zavorkach a jestlize je pravdiva, tak vykona prikaz /nebo skupinu prikazu/, ktere nasleduji. Znovu muzeme snadno ukazat, co je cim rizeno. Dvojnasobny znak == je v jazyku C zapis podminky "je rovno" /jako .EQ. ve FORTRANU/. Dvojity symbol je pouzit proto, aby jej bylo mozno odlisit od prirazovaciho symbolu =. Protoze se prirazovaci prikaz v typickych programech jazyka C pouziva zhruba dvakrat casteji nez relacni operator pro rovnost, je logicke, ze ma polovicni pocet znaku /na rozdil od ALGOLU nebo PASCALU/. Je-li nektery znak psan mezi apostrofy, vysledkem je cisel- na hodnota tohoto znaku. To se nazyva znakovou konstantou. Tak napr. 'A' je znakova konstanta; v ASCII je jeji hodnota 65, coz je vlastne vnitrni reprezentace znaku A. Vzdy ale davame pred- nost psani 'A' pred 65: vyznam je jasnejsi a nezalezi na mistni reprezentaci znaku. Znaky uvedene za obracenym lomitkem jsou rovnez dovolene znakove konstanty a mohou se vyskytnout v podminkach i arimetickych vyrazech. '\n' se pouziva misto hodnoty znaku pro novou radku. Uvedomte si, ze '\n' je jeden znak a ve vyra- zu je roven cislu typu int a na druhe strane, "\n" je rete- zec znaku, ktery obsahuje jeden znak. Tato problematika je podrobne popsana v kap. 2. C v i c e n i 1-6. Napiste program, ktery pocita mezery, tabe- latory a znaky pro novou radku. C v i c e n i 1-7. Napiste program, ktery kopiruje text ze vstupu na vystup a nahrazuje jednu nebo vice mezer vzdy jen jednou mezerou. C v i c e n i 1-8. Napiste program,vktery nahrazuje kazdy ta- belator radou tri znaku >, zpetny znak, -, ktere tiskne jako -> a kazdy zpetny znak obdobnou radou <-. To nam zviditelni znaky pro tabelator a backspace. Pocitani slov ------------- Ctvrty ze serie uzitecnych programu pocita radky, slova a znaky. Slovo je zde definovano jako skupina znaku, ktera neob- sahuje mezeru, tabelator nebo znak pro novou radku. /Tento pro- gram je kostrou obsluzniho programu wc v systemu UNIX./ #define YES 1 #define NO 0 main() /*zjisteni poctu radek, slov, znaku ze vstupu*/ { int c, nl, nw, nc, inword; inword = NO; nl = nw = nc = 0; while((c=getchar()) != EOF) { ++nc; if(c == '\n') ++nl; if(c == ' ' || c == '\n' || c == '\t') inword = NO; else if(inword == NO) { inword = YES; ++nw; } } printf( "%d %d %d\n", nl, nw, nc); } Pokazde, kdyz program narazi na prvni znak slova, zapocita slo- vo. Promenna inword indikuje, zda je program prave uprostred slova nebo ne; na zacatku "neni uprostred slova", coz je vyja- dreno hodnotou NO. Davame prednost symbolickym konstantam YES a NO pred hodnotami 1 a 0, protoze je program srozumitelnejsi. Ovsem v tak malem programu, jako je nas, to prinasi spise po- tize, ale potom ve vetsich programech se nam toto usili, jez od zacatku vynakladame, bohate vrati. Poznate take, ze lze snadne- ji delat rozsahle zmeny v programu, kde se cisla vyskytuji pou- ze jako symbolicke konstanty. Radka nl = nw = nc = 0; vynuluje vsechny promenne. Neni to specialni pripad, ale vysle- dek toho, ze prirazovani probiha zprava doleva. Je to jako bychom napsali nc = ( nl = ( nw = 0 )); Operator || znamena nebo a tak radka if (c == ' ' || c == '\n' || c == '\t') znamena: "jestlize c je mezera, nebo c je znak pro novy radek nebo c je tabelator...". && je obdobny operator pro logicky soucin. Vyrazy obsahujici operatory || nebo && jsou vyhodnoco- vany odleva doprava a je zajisteno, ze vyhodnocovani se ukonci tehdy, jestlize je uz znamo, ze vyraz je pravdivy nebo neprav- divy. Proto obsahuje-li promenna c mezeru, neni treba dale tes- tovat zda obsahuje tabelator, ci znak pro novou radku. V kratkem programu to neni dulezite, ale na vyznamu to nabyva v komplikovanejsich situacich, jak brzy uvidime. Nas program take ukazuje cinnost prikazu e l s e, ktery urcuje alternativni cinnost, ktera ma byt konana, kdyz podmin- ka prikazu if neni splnena. Obecny tvar je if (podminka) prikaz-1 else prikaz-2 miki Za teto situace bude vykonan prave jeden ze dvou prikazu spoje- nych s prikazem if. Jestlize je podminka pravdiva, bude vykonan prikaz-1; jestlize ne, bude vykonan prikaz-2. Kazdy prikaz muze byt ve skutecnosti slozen zase z prikazu. V programu pro pocitani slov je za else dalsi prikaz if, ktery podminuje dal- si dva prikazy ve slozenych zavorkach. C v i c e n i 1-9. Jak budete testovat program pro pocitani slov. Jaka jsou omezeni? C v i c e n i 1-10. Napiste program, ktery tiskne slova ze vstupu vzdy na novou radku. C v i c e n i 1-11. Upravte definici slova v programu pro pocitani slov. Napr. slovo je posloupnosti pismen, cislic a apostrofu, ktera zacinaji pismenem. 1.6 Pole -------- Napisme program, ktery bude zjistovat pocet vyskytu kazdeho z cisel, oddelovacu /tj. mezer tabelatoru a znaku nove radky/ a vsech ostatnich znaku. Je to umele vykonstruovany priklad, ale umozni nam ilustrovat ruzne vlastnosti jazyka C v jednom programu. Na vstupu se muze objevit dvanact druhu znaku, a proto spi- se nez jednotlive promenne pouzijeme s vyhodou pole, ktere bude obsahovat pocet vyskytu kazdeho cisla. Zde je jedna z verzi programu: main() /*pocitani cisel, oddelovacu, ostatnich*/ { int c, i, nwhite, nother; int ndigit[10]; nwhite = nother = 0; for(i = 0; i<10; ++i) ndigit[i] = 0; while((c = getchar()) != EOF) if (c >= '0' && c <= '9') ++ndigit [c-'0']; else if(c == ' ' || c == '\n' || c == '\t') ++nwhite; else ++nother; printf ("digits ="); for (i = 0; i<10; ++i) printf (" %d", ndigit[i]); printf ( "\nwhite space = %d, other = %d\n", nwhite, nother); } Popis int ndigit[10]; deklaruje pole ndigit o delce 10 celociselnych promenych. Inde- xy pole zacinaji v jazyku C vzdy s nulou /oproti FORTRANU nebo PL/1, kde pole zacinaji indexem 1/, a tak prvky pole jsou ndigit [0], ndigit [1] ...., ndigit [9]. To se projevuje v cyk- lu for, ktery inicializuje a tiskne pole ndigit. Indexem pole muze byt libovolny celociselny vyraz, ve kterem mohou byt jen celociselne promenne nebo celociselne konstanty. Tento zvlastni program se spoleha na znakovou reprezentaci ci- sel. Napr. podminka if(c >= '0' && c <= '9')... urcuje, zda je znak c cislici. Jestlize cislici je, tak nume- ricka hodnota teto cislice je c - '0' Tento postup funguje jen tehdy, kdyz '0', '1',... jsou kladna cisla usporadana vzestupne a jestlize mezi '0' a '9' jsou jen cislice. Nastesti je to splneno pro vetsinu konvencnich soubo- ru znaku. V aritmetickych operacich, kde se vyskytuji promenne typu char a int, jsou pred vypoctem promenne typu char preve- deny na celociselne promenne a jsou tedy shodne co do obsahu s promennymi typu int. Je to vyhodne a zcela prirozene; napr. c - '0' je celociselny vyraz, jehoz hodnota lezi mezi 0 a 9 v odpovidajicim poradi jako znaky '0' a '9' a je tedy platnym indexem pole ndigit. Rozhodnuti, zda je znak cislici, oddelovacem nebo necim jinym je dano posloupnosti if (c >= '0' && c <= '9') ++ndigit [c-'0']; else if (c == ' ' || c == '\n' || c == '\t') ++nwhite; else ++nother; Konstrukce if (podminka) prikaz else if (podminka) prikaz else prikaz se casto v programech objevuje jako zpusob vicenasobneho rozho- dovani. Prikazy jsou jedoduse vykonavany odshora dolu. Kdyz je nektera podminka splnena, tak je odpovidajici prikaz vykonan a sekvence prikazu je ukoncena. Prikazem muze byt samozrejme ne- kolik prikazu ve slozenych zavorkach. Jestlize neni zadna z po- dminek splnena, prikaz nasledujici posledni prikaz else je vy- konan, pokud je ovsem uveden. Jestlize je zaverecny prikaz else a prikaz vynechan /jako je tomu v nasem programu pocitani slov/, neprovede se nic. V teto konstrukci muze byt mezi prv- nim if a poslednim else libovolny pocet skupin else if (podminka) prikaz Je moudrejsi formovat tyto konstrukce tak, jak jsme ukazali, aby se dlouhy rozhodovaci prikaz prilis neblizil prave stra- ne papiru /pouzivame-li zvyraznene logicke struktury odsazo- vanim prikazu/. Prikaz s w i t c h, ktery bude popsan va kapitole 3, umoznuje dalsi zpusob mnohonasobneho rozhodovani. Je vhodny pro zjistovani, ze ktereho souboru je dane cislo nebo znakovy vyraz. C v i c e n i 1-12. Napiste program, ktery bude tisknou histo- gram delek nactenych slov. Nejsnadnejsi je nakreslit histogram horizontalne, vertikalni usporadani je slozitejsi. 1.7 Funkce ---------- Funkce jazyka C jsou obdobou podprogramu a funkci jazyka FORTRAN a procedur v PL/1, PASCALU atd. Funkce umoznuje vhodnym zpusobem soustredit urcite vypocty do "cerne skrinky", kterou muzeme pouzit, aniz se starame o to, co je uvnitr. Uziti funkci je jedinym zpusobem, jak preklenout slozitost velkeho programu. Mame-li spravne nadefinovane funkce, tak potom nas nemusi zaji- mat, jak jsou udelany; staci vedet co delaji. Jazyk C je navr- zen tak, aby pouzivani funkci bylo snadne, vyhodne a ucinne. Casto se setkame s funkci, ktera ma jen nekolik radek, a je jen jednou volana. Je pouzita proto, ze zprehlednuje program. Zatim jsme pouzivali funkce printf, getchar a putchar, kte- re jiz drive nekdo vytvoril; nyni je cas napsat nekolik vlast- nich funkci. Protoze v jazyku C vubec neni operator mocneni /jako je ** ve FORTRANU ne PL/1/, vysvetleme si zpusob vy- tvoreni funkce a napisme funkci p o w e r (m,n), ktera umoc- nuje cele cislo m na cele cislo . Napr. hodnota power(2,5) je 32. Tato funkce nenahrazuje zcela operator **, protoze pracu- je pouze s malymi celymi cisly, ale je vhodne zacinat jednodu- chou ukazkou. Nasleduje funkce power a hlavni program main, ktery ji pro- veri, takze je mozne videt celou strukturu programu najednou. main() /*testovani funkce power*/ { int i; for (i = 0; i = 10; ++i) printf("%d %d %d\n", i, power(2,i), power(-3,i)); } power(x, n) /*funkce power*\ int x, n; { int i, p; p = 1; for(i = 1; i <= n; ++i) p = p * x; return (p); } Vsechny funkce maji stejnou strukturu: jmeno (seznam argumentu, pokud jsou nejake) deklarace argumentu, pokud jsou nejake; { deklarace prikazy } Funkce se mohou umistit v textu programu libovolne, a to jak v jednom nebo ve dvou souborech. Ovsem kdyz je text pro- gramu ve dvou souborech, je treba zmenit prikazy pro kompila- ci a sestaveni, ale to je jiz zalezitost operacniho systemu a ne jazyka C. Pro zacatek budeme predpokladat, ze obe funkce jsou v jednom souboru, takze plati vse, co jste se zatim o spousteni programu v jazyku C naucili. Funkce power je vyvolana v jednom radku dvakrat printf ("%d %d %d\n", i, power (2,i), power (-3,i)); Pri kazdem vyvolani funkce power jsou ji predavany dva argu- menty a hodnotou funkce je cele cislo a to je zformatovano a vytisteno. Ve vyrazu je power (2,i) cele cislo zrovna tak, jako 2 a i /ne vsechny funkce vraci cele cislo - blize o tom v kapitole 4/. Ve funkci power je treba argumenty nadeklarovat. To je provedeno v radce int x, n; ktera nasleduje za radkou se jmenem funkce. Deklarace argumen- tu je umistnena mezi seznam argumentu a prvni slozenou zavor- ku; kazda deklarace je zakoncena strednikem. Jmena, ktera se pro argumenty pouzivaji ve funkci power jsou ciste mistni a ostatni funkce k nim nemaji pristup. To zna- mena, ze ostatni funkce mohou pouzivat stejna jmena pro jine promenne. To plati i pro promenne i a p: promenna i ve funkci power nema zadny vztah promenne i v hlavnim programu main. Hodnota funkce power je vracena programu main prikazem return stejnym zpusobem jako v jazyku PL/1. V zavorkach se zde muze objevit libovolny vyraz. Funkce nemusi ale vracet hodnotu; samotny prikaz return predava pouze rizeni volajici jednotce a nepredava hodnotu. C v i c e n i 1-13. Napiste program, ktery prevadi pismena ze vstupu na mala pismena a pouziva pri tom funkci lower. Funkce lower vraci c, kdyz c neni pismeno, a hodnota maleho pisme- na c, jestlize je c pismeno. 1.8 Argumenty - volani hodnotou ------------------------------- Jedna z vlastnosti jazyka C muze byt nekterym programatorum, kteri pouzivaji FORTRAN a PL/1 atd., neznama. V jazyce C jsou vsechny argumenty /vyjma poli/ predavany "hodnotou". To zname- na, ze volana funkce pracuje s docasnymi promennymi, ktere jsou kopiemi skutecnych argumentu /ve skutecnosti jsou v zasobniku/. Tento zpusob je odlisny oproti FORTRANU a PL/1, kde jsou argu- menty predavany adresou a nikoli hodnotou. Hlavni rozdil je v tom, ze funkce v jazyku C nemuze menit hodnoty promennych volajici funkce; muze pouze menit hodnoty vlastnich, docasnych promennych. Volani hodnotou je kladnou strankou a nikoliv omezenim. Obvykle je mozne vytvaret kompak- tnejsi programy s mensim mnozstvim pridavnych promennych,pro- toze s argumenty je mozno ve volani funkci nakladat jako s bez- nymi mistnimi promennymi. Jako priklad uvedeme variantu funkce power, ktera tohoto faktu vyuziva. power(x, n) /*umocneni x na n; n>0; verze 2*/ int x, n; { int p; for (p = 1; n>0; --n) p = p * x; return (p); } Argument n je pouzit jako docasna promena a je zmensovan az do nuly. Jiz nemusime pouzivat promennou i. Je lhostejne jake hod- noty promenna n ve funkci power nabude - nebude to mit zadny vliv na argument, s nimz byla funkce power volana. Kdyz je potreba, tak funkce muze menit hodnoty promenych ve volajici jednotce. Volajici funkce musi predat adresu pro- menne /coz se nazyva ukazatel, pointer na promennou/ a volana funkce musi tento argument deklarovt jako ukazatel a odkazovat se na promennou timto ukazatelem. Tato problematika bude diskutovana v kapitole 5. Je-li jako argument uvedeno jmeno pole, tak predavana hod- nota je ve skutecnosti pozice nebo adresa zacatku tohoto po- le. /Prvky pole se nekopiruji!/ Funkce ma tedy pristup ke kteremukoliv prvku pole. O tomto budeme hovorit v nasledu- jicim odstavci. 1.9 Znakova pole ---------------- Pravdepodobne nejrozsirenejsimi typy poli v jazyku C jsou znakova pole. Abychom si ilustrovali pouziti techto poli a cinnost funkci, ktere s nimi pracuji, napiseme program, ktery nacita radky a vytiskne tu nejdelsi. Hruby diagram je velmi jednoduchy: while /existuje dalsi radka/ if(je delsi nez predchozi nejdelsi) uchovej ji a jeji delku vytiskni nejdelsi radku Tento diagram objasnuje cinnost programu a umoznuje nam rozde- lit ho na jednotlive casti. Jedna cast nacita radku, druha cast ji testuje, dalsi uchovava a zbytek ridi cinnost. Protoze je diagram uplne rozdelen, bude treba napsat program stejnym zpusobem. Napisme tedy nejprve funkci getline, ktera nacita ze vstupu dalsi radku; getline je zobecneni funkce getchar. Aby byla funkce getline uzitecna i v dalsich progra- mech, napisme ji tak univerzalne, jak jen to pujde. Prinejmen- sim musi funkce getline signalizovat konec souboru; dalsim zobecnenim bude, aby jeji hodnota byla rovna delce nactene radky, nebo nule, byl-li nacten konec souboru. Kazda radka ma minimalne jeden znak a tak nula neni platna hodnota pro del- ku radky ani tehdy, obsahuje-li radka jen znak pro novou radku /takova radka ma delku 1/. Kdyz narazime na radku, ktera je delsi nez predchazejici nejdelsi radka, tak ji musime nekde uchovat. To bude vykona- vat druha funkce, c o p y, ktera bude nejdelsi radku kopi- rovat na bezpecne misto. A na zaver potrebujeme hlavni program, ktery bude cinnost funkci getline a copy ridit. Zde je vysle- dek: #define MAXLINE 1000 /*max. velikost vstupu radky*/ main() /*nalezeni nejdelsi radky*/ { int len; /*delka bezne radky*/ int max; /*maximalni dosazena velikost*/ char line[MAXLINE]; /*bezna radka*/ char save[MAXLINE]; /*nejdelsi radka uchovana*/ max = 0; while ((len = getline (line, MAXLINE)) > 0) if (len > max) { max = len; copy(line, save); } if (max>0) /*byla nactena radka/* printf("%s", save); } getline (s, lim) /*nacti radku do s, vrat delku/* char s []; int lim; { int c, i; for(i=0; i 0) if (len > max) { max = len; copy(); } if (max> 0) /*byla nactena veta*/ printf("%s", save); } getline() /*zvlastni verze*/ { int c, i; extern char line[]; for(i = 0; i < MAXLINE-1 && (c=getchar()) != EOF && c != '/n'; ++i) line[i] = c; if (c == '/n') { line [i] = c; ++i; } line[i] = '/0'; return (i); } copy() /*zvlastni verze*/ { int i; extern char line[], save[]; i = 0; while ((save[i] = line[i]) != '\0') ++i; } Externi promenne v jednotkach main, getline a copy jsou definovany prvnim radkem programu. Zde je urcen typ techto promennych a vyhrazeno misto v pameti. Po syntakticke strance jsou definice externich promennych shodne s deklaracemi, ktere jsme jiz drive uzivali, ale protoze se objevuji mimo funkce, tak jsou chapany jako externi promenne. Predtim, nez funkce muze pouzivat externi promennou, musi znat jeji jmeno. Jeden ze zpusobu je napsat deklaraci extern uvnitr funkce; deklarace je stejna s tim, ze na zacatku je slovo extern. Za jistych okolnosti muze byt deklarace extern vynechana. Jestlize se objevi ve zdrojovem textu pred pouzitim urcite funkce, tak neni nutne pouzit deklarace extern uvnitr teto funkce. V nasem prikladu jsou tedy deklarace extern v jednot- kach main, getline, copy nadbytecne. Je rozsirenou praxi umis- tit definice vsech externich promennych na zacatek souboru a nikde jiz nepouzivat prikaz extern. Jestlize je ale program rozdelen do nekolika souboru a pro- menna je definovna rekneme v souboru file1 a pouzivana v sou- boru file2, potom je deklarace extern ve file2 nezbytna. To bude ukazano v kapitole 4. Vsimnete si, jak opatrne pouzivame vyrazy deklarace a defi- nice, kdyz mluvime v teto sekci o externich promennych. "Definice" urcuje misto, kde bude externi promenna vytvorena /tj. jeji prideleno misto v pameti/. "Deklarace" poukazuje na misto, kde je promenna ulozena, ale neni ji pridelena pritom pamet. Zdalo by se, ze je vyhodne veskerou komunikaci mezi funkcemi omezit na pouzivani externich promennych - seznamy argumentu jsou kratke a promenne jsou vzdy tam, kde je ocekavame. Ale externi promenne jsou tam i tehdy, kdyz je nepotrebujeme. Tento zpusob programovani je ale plny nebezpeci, protoze vede k programum, kde je vzajemna komunikace nejasna. Za prve promenne mohou byt neocekavane z nedopatreni zmeneny a za druhe neni snadne program modifikovat. Druha verze programu je horsi nez prvni castecne kvuli smyslu a castec- ne proto, ze znicila univerzalnost dvou uzitecnych funkci, kdyz je pripoutala k urcitym nazvum promennych. C v i c e n i 1-18. Podminka v prikazu for ve funkce getline je ponekud neohrabana. Prepiste program tak, aby byla jasnejsi a pritom zachovavejte cinnost funkce, narazi-li na konec dat nebo pri preplneni pole. 1.11 Shrnuti ------------- V teto chvili jsme probrali to, co muze byt chapano jako za- klady jazyka C. Se stavebnimi bloky, ktere jsme sestrojili, mu- zeme nyni psat uzitecne programy rozumne velikosti a bude jiste dobre, kdyz si je procvicite pred dalsim studiem. Nasledujici priklady by vam mely dat namety pro slozitejsi programy, nez jsou popsany v teto kapitole. C v i c e n i 1-19. Napiste program detab, ktery nahrazuje ta- belatory ze vstupu odpovidajicim poctem mezer. Predpokladejte, tabelatory jsou na kazde n-te pozici. C v i c e n i 1-20. Napiste program entab, ktery nahrazuje retezec mezer minimalnim poctem tabelatoru a mezer. Uzivejte stejne pozice tabelatoru jako ve funkci detab. C v i c e n i 1-21. Napiste program, ktery rozdeluje dlouhe radky ze vstupu po poslednim nemezerovem znaku, ktera se objevi pred n-tym sloupcem vstupu, kde n je parametr. Ujistete se, ze vas program funguje s velmi dlouhym radkem, nebo jestlize nejsou zadne mezery nebo tabelatory pred n-tou pozici. C v i c e n i 1-22. Napiste program, ktery vyjima vsechny komentare z programu v jazyce C. Neopomente spravne pouzivat znakove retezce a znakove konstanty. C v i c e n i 1-23. Napiste program , ktery kontroluje zaklad- ni symboly programu jako jsou neuzavrene kulate, hranate a slo- zene zavorky, nezapomente na apostrofy, uvozovky a komentare. Tento program je slozity, jestlize jej plne zobecnite.