pravila o tom, v kakom sluchae celye preobrazu- yutsya k tipu s plavayushej tochkoj, privedeny v glave 2. Sejchas zhe otmetim, chto prisvaivanie FAHR = LOWER; proverka WHILE (FAHR <= UPPER) rabotayut, kak ozhidaetsya, - pered vypolneniem operacij celye preobrazuyutsya v plavayushchuyu formu. |tot zhe primer soobshchaet chut' bol'she o tom, kak rabotaet PRINTF. Funkciya PRINTF fakticheski yavlyaetsya universal'noj funkciej formatnyh preobrazovanij, kotoraya budet polnost'yu opisana v glave 7. Ee pervym argumentom yavlyaetsya stroka sim- volov, kotoraya dolzhna byt' napechatana, prichem kazhdyj znak % ukazyvaet, kuda dolzhen podstavlyat'sya kazhdyj iz ostal'nyh ar- gumentov /vtoroj, tretij, .../ i v kakoj forme on dolzhen pe- chatat'sya. Naprimer, v operatore PRINTF("%4.0F %6.1F\N", FAHR, CELSIUS); specifikaciya preobrazovaniya %4.0F govorit, chto chislo s pla- vayushchej tochkoj dolzhno byt' napechatano v pole shirinoj po kraj- nej mere v chetyre simvola bez cifr posle desyatichnoj tochki. specifikaciya %6.1F opisyvaet drugoe chislo, kotoroe dolzhno zanimat' po krajnej mere shest' pozicij s odnoj cifroj posle desyatichnoj tochki, analogichno specifikaciyam F6.1 v fortrane ili F(6,1) v PL/1. Razlichnye chasti specifikacii mogut byt' opushcheny: specifikaciya %6F govorit, chto chislo budet shirinoj po krajnej mere v shest' simvolov; specifikaciya %2 trebuet dvuh pozicij posle desyatichnoj tochki, no shirina pri etom ne ogranichivaetsya; specifikaciya %F govorit tol'ko o tom, chto nuzhno napechatat' chislo s plavayushchej tochkoj. Funkciya PRINTF takzhe raspoznaet sleduyushchie specifikacii: %D - dlya desyatichno- go celogo, %o - dlya vos'merichnogo chisla, %h - dlya shestnadca- tirichnogo, %s - dlya simvola, %S - dlya simvol'noj stroki i %% - dlya samogo simvola %. Kazhdaya konstrukciya s simvolom % v pervom argumente funk- cii PRINTF sochetaetsya s sootvetstvuyushchim vtorym, tret'im, i t.d. Argumentami; oni dolzhny soglasovyvat'sya po chislu i ti- pu; v protivnom sluchae vy poluchite bessmyslennye rezul'taty. Mezhdu prochim, funkciya PRINTF ne yavlyaetsya chast'yu yazyka "C"; v samom yazyke "C" ne opredeleny operacii vvoda-vyvoda. Net nichego tainstvennogo i v funkcii PRINTF ; eto - prosto poleznaya funkciya, yavlyayushchayasya chast'yu standartnoj biblioteki podprogramm, kotoraya obychno dostupna "C"-programmam. CHtoby sosredotochit'sya na samom yazyke, my ne budem podrobno osta- navlivat'sya na operaciyah vvoda-vyvoda do glavy 7. V chastnos- ti, my do teh por otlozhim formatnyj vvod. Esli vam nado vvesti chisla - prochitajte opisanie funkcii SCANF v glave 7, razdel 7.4. Funkciya SCANF vo mnogom shodna s PRINTF , no ona schityvaet vhodnye dannye, a ne pechataet vyhodnye. Uprazhnenie 1-3 ---------------- Preobrazujte programmu perevoda temperatur takim obra- zom, chtoby ona pechatala zagolovok k tablice. Uprazhnenie 1-4 ---------------- Napishite programmy pechati sootvetstvuyushchej tablicy pere- hoda ot gradusov cel'siya k gradusam farengejta. 1.3. Operator FOR Kak i mozhno bylo ozhidat', imeetsya mnozhestvo razlichnyh sposobov napisaniya kazhdoj programmy. Davajte rassmotrim ta- koj variant programmy perevoda temperatur: MAIN() /* FAHRENHEIT-CELSIUS TABLE */ { INT FAHR; FOR (FAHR = 0; FAHR <= 300; FAHR = FAHR + 20) PRINTF("%4D %6.1F\N", FAHR, (5.0/9.0)*(FAHR-32.0)); } |ta programma vydaet te zhe samye rezul'taty, no vyglyadit bezuslovno po-drugomu. Glavnoe izmenenie - isklyuchenie bol'- shinstva peremennyh; ostalas' tol'ko peremennaya FAHR , prichem tipa INT (eto sdelano dlya togo, chtoby prodemonstrirovat' preobrazovanie %D v funkcii PRINTF). Nizhnyaya i verhnyaya grani- cy i razmer shchaga poyavlyayutsya tol'ko kak konstanty v operatore FOR , kotoryj sam yavlyaetsya novoj konstrukciej, a vyrazhenie, vychislyayushchee temperaturu po cel'siyu, vhodit teper' v vide tret'ego argumenta funkcii PRINTF , a ne v vide otdel'nogo operatora prisvaivaniya. Poslednee izmenenie yavlyaetsya primerom vpolne obshchego pra- vila yazyka "C" - v lyubom kontekste, v kotorom dopuskaetsya ispol'zovanie znacheniya peremennoj nekotorogo tipa, vy mozhete ispol'zovat' vyrazhenie etogo tipa. Tak kak tretij argument funkcii PRINTF dolzhen imet' znachenie s plavayushchej tochkoj, chtoby sootvetstvovat' specifikacii %6.1F, to v etom meste mozhet vstretit'sya lyuboe vyrazhenie plavayushchego tipa. Sam operator FOR - eto operator cikla, obobshchayushchij opera- tor WHILE. Ego funkcionirovanie dolzhno stat' yasnym, esli vy sravnite ego s ranee opisannym operatorom WHILE . Operator FOR soderzhit tri chasti, razdelyaemye tochkoj s zapyatoj. Pervaya chast' FAHR = 0 vypolnyaetsya odin raz pered vhodom v sam cikl. Vtoraya chast' - proverka, ili uslovie, kotoroe upravlyaet ciklom: FAHR <= 300 eto uslovie proveryaetsya i, esli ono istinno, to vypolnyaetsya telo cikla (v dannom sluchae tol'ko funkciya PRINTF ). Zatem vypolnyaetsya shag reinicializacii FAHR =FAHR + 20 i uslovie proveryaetsya snova. cikl zavershaetsya, kogda eto us- lovie stanovitsya lozhnym. Tak zhe, kak i v sluchae operatora WHILE , telo cikla mozhet sostoyat' iz odnogo operatora ili iz gruppy operatorov, zaklyuchennyh v figurnye skobki. Iniciali- ziruyushchaya i reinicializiruyushchaya chasti mogut byt' lyubymi ot- del'nymi vyrazheniyami. Vybor mezhdu operatorami WHILE i FOR proizvolen i osnovy- vaetsya na tom , chto vyglyadit yasnee. Operator FOR obychno udo- ben dlya ciklov, v kotoryh inicializaciya i reinicializaciya logicheski svyazany i kazhdaya zadaetsya odnim operatorom, tak kak v etom sluchae zapis' bolee kompaktna, chem pri ispol'zo- vanii operatora WHILE , a operatory upravleniya ciklom sosre- dotachivayutsya vmeste v odnom meste. Uprazhnenie 1-5 --------------- Modificirujte programmu perevoda temperatur takim obra- zom, chtoby ona pechatala tablicu v obratnom poryadke, t.e. Ot 300 gradusov do 0. 1.4. Simvolicheskie konstanty Poslednee zamechanie, prezhde chem my navsegda ostavim programmu perevoda temperatur. Pryatat' "magicheskie chisla", takie kak 300 i 20, vnutr' programmy - eto neudachnaya prakti- ka; oni dayut malo informacii tem, kto, vozmozhno, dolzhen bu- det razbirat'sya v etoj programme pozdnee, i ih trudno izme- nyat' sistematicheskim obrazom. K schast'yu v yazyke "C" predus- motren sposob, pozvolyayushchij izbezhat' takih "magicheskih chi- sel". Ispol'zuya konstrukciyu #DEFINE , vy mozhete v nachale programmy opredelit' simvolicheskoe imya ili simvolicheskuyu konstantu, kotoraya budet konkretnoj strokoj simvolov. Vpos- ledstvii kompilyator zamenit vse ne zaklyuchennye v kavychki po- yavleniya etogo imeni na sootvetstvuyushchuyu stroku. Fakticheski eto imya mozhet byt' zameneno absolyutno proizvol'nym tekstom, ne obyazatel'no ciframi #DEFINE LOWER 0/* LOWER LIMIT OF TABLE */ #DEFINE UPPER 300 /* UPPER LIMIT */ #DEFINE STEP 20 /* STEP SIZE */ MAIN () /* FAHRENHEIT-CELSIUS TABLE */ { INT FAHR; FOR (FAHR =LOWER; FAHR <= UPPER; FAHR =FAHR + STEP) PRINTF("%4D %6.1F\N", FAHR, (5.0/9.0)*(FAHR-32)); } velichiny LOWER, UPPER i STEP yavlyayutsya konstantami i poe- tomu oni ne ukazyvayutsya v opisaniyah. Simvolicheskie imena obychno pishut propisnymi bukvami, chtoby ih bylo legko otli- chit' ot napisannyh strochnymi bukvami imen peremennyh. otme- tim, chto v konce opredeleniya ne stavitsya tochka s zapyatoj. Tak kak podstavlyaetsya vsya stroka, sleduyushchaya za opredelennym imenem, to eto privelo by k slishkom bol'shomu chislu tochek s zapyatoj v operatore FOR . 1.5. Nabor poleznyh programm Teper' my sobiraemsya rassmotret' semejstvo rodstvennyh programm, prednaznachennyh dlya vypolneniya prostyh operacij nad simvol'nymi dannymi. V dal'nejshem vy obnaruzhite, chto mnogie programmy yavlyayutsya prosto rasshirennymi versiyami teh prototipov, kotorye my zdes' obsuzhdaem. 1.5.1. Vvod i vyvod simvolov Standartnaya biblioteka vklyuchaet funkcii dlya chteniya i za- pisi po odnomu simvolu za odin raz. funkciya GETCHAR() izvle- kaet sleduyushchij vvodimyj simvol kazhdyj raz, kak k nej obrashcha- yutsya, i vozvrashchaet etot simvol v kachestve svoego znacheniya. |to znachit, chto posle C = GETCHAR() peremennaya 'C' soderzhit sleduyushchij simvol iz vhodnyh dannyh. Simvoly obychno postupayut s terminala, no eto ne dolzhno nas kasat'sya do glavy 7. Funkciya PUTCHAR(C) yavlyaetsya dopolneniem k GETCHAR : v rezul'tate obrashcheniya PUTCHAR (C) soderzhimoe peremennoj 'C' vydaetsya na nekotoryj vyhodnoj no- sitel', obychno opyat' na terminal. Obrashchenie k funkciyam PUTCHAR i PRINTF mogut peremezhat'sya; vydacha budet poyavlyat'sya v tom poryadke, v kotorom proishodyat obrashcheniya. Kak i funkciya PRINTF , funkcii GETCHAR i PUTCHAR ne so- derzhat nichego ekstraordinarnogo. Oni ne vhodyat v sostav yazy- ka "C", no k nim vsegda mozhno obratit'sya. 1.5.2. Kopirovanie fajla Imeya v svoem rasporyazhenii tol'ko funkcii GETCHAR i PUTCHAR vy mozhete, ne znaya nichego bolee ob operaciyah vvo- da-vyvoda, napisat' udivitel'noe kolichestvo poleznyh prog- ramm. Prostejshim primerom mozhet sluzhit' programma posimvol'- nogo kopirovaniya vvodnogo fajla v vyvodnoj. Obshchaya shema ime- et vid: vvesti simvol WHILE (simvol ne yavlyaetsya priznakom konca fajla) vyvesti tol'ko chto prochitannyj simvol vvesti novyj simvol programma, napisannaya na yazyke "C", vyglyadit sleduyushchim obra- zom: MAIN() /* COPY INPUT TO OUTPUT; 1ST VERSION */ { INT C; C = GETCHAR(); WHILE (C != EOF) { PUTCHAR (C); C = GETCHAR(); } } operator otnosheniya != oznachaet "ne ravno". Osnovnaya problema zaklyuchaetsya v tom, chtoby zafiksiro- vat' konec fajla vvoda. Obychno, kogda funkciya GETCHAR natal- kivaetsya na konec fajla vvoda, ona vozvrashchaet znachenie , ne yavlyayushcheesya dejstvitel'nym simvolom; takim obrazom, programma mozhet ustanovit', chto fajl vvoda ischerpan. Edinstvennoe os- lozhnenie, yavlyayushcheesya znachitel'nym neudobstvom, zaklyuchaetsya v sushchestvovanii dvuh obshcheupotrebitel'nyh soglashenij o tom, ka- koe znachenie fakticheski yavlyaetsya priznakom konca fajla. My otsrochim reshenie etogo voprosa, ispol'zovav simvolicheskoe imya EOF dlya etogo znacheniya, kakim by ono ni bylo. Na prakti- ke EOF budet libo -1, libo 0, tak chto dlya pravil'noj raboty pered programmoj dolzhno stoyat' sobstvenno libo #DEFINE EOF -1 libo #DEFINE EOF 0 Ispol'zovav simvolicheskuyu konstantu EOF dlya predstavle- niya znacheniya, vozvrashchaemogo funkciej GETCHAR pri vyhode na konec fajla, my obespechili, chto tol'ko odna velichina v prog- ramme zavisit ot konkretnogo chislennogo znacheniya. My takzhe opisali peremennuyu 'C' kak INT , a ne CHAR , s tem chtoby ona mogla hranit' znachenie, vozvrashchaemoe GETCHAR . kak my uvidim v glave 2, eta velichina dejstvitel'no INT, tak kak ona dolzhna byt' v sostoyanii v dopolnenie ko vsem vozmozh- nym simvolam predstavlyat' i EOF. Programmistom, imeyushchim opyt raboty na "C", programma kopirovaniya byla by napisana bolee szhato. V yazyke "C" lyuboe prisvaivanie, takoe kak C = GETCHAR() mozhet byt' ispol'zovano v vyrazhenii; ego znachenie - prosto znachenie, prisvaivaemoe levoj chasti. Esli prisvaivanie sim- vola peremennoj 'C' pomestit' vnutr' proverochnoj chasti ope- ratora WHILE , to programma kopirovaniya fajla zapishetsya v vide: MAIN() /* COPY INPUT TO OUTPUT; 2ND VERSION */ { INT C; WHILE ((C = GETCHAR()) != EOF) PUTCHAR(C); } Programma izvlekaet simvol , prisvaivaet ego peremennoj 'C' i zatem proveryaet, ne yavlyaetsya li etot simvol priznakom konca fajla. Esli net - vypolnyaetsya telo operatora WHILE, vyvodyashchee etot simvol. Zatem cikl WHILE povtoryaetsya. kogda, nakonec, budet dostignut konec fajla vvoda, operator WHILE zavershaetsya, a vmeste s nim zakanchivaetsya vypolnenie i funk- cii MAIN . V etoj versii centralizuetsya vvod - v programme tol'ko odno obrashchenie k funkcii GETCHAR - i uzhimaetsya programma. Vlozhenie prisvaivaniya v proveryaemoe uslovie - eto odno iz teh mest yazyka "C", kotoroe privodit k znachitel'nomu sokra- shcheniyu programm. Odnako, na etom puti mozhno uvlech'sya i nachat' pisat' nedostupnye dlya ponimaniya programmy. |tu tendenciyu my budem pytat'sya sderzhivat'. Vazhno ponyat' , chto kruglye skobki vokrug prisvaivaniya v uslovnom vyrazhenii dejstvitel'no neobhodimy. Starshinstvo operacii != vyshe, chem operacii prisvaivaniya =, a eto oznacha- et, chto v otsutstvie kruglyh skobok proverka usloviya != bu- det vypolnena do prisvaivaniya =. Takim obrazom, operator C = GETCHAR() != EOF ekvivalenten operatoru C = (GETCHAR() != EOF) |to, vopreki nashemu zhelaniyu, privedet k tomu, chto 'C' budet prinimat' znachenie 0 ili 1 v zavisimosti ot togo, na- tolknetsya ili net GETCHAR na priznak konca fajla. Podrobnee ob etom budet skazano v glave 2/. 1.5.3. Podschet simvolov Sleduyushchaya programma podschityvaet chislo simvolov; ona predstavlyaet soboj nebol'shoe razvitie programmy kopirovaniya. MAIN() /* COUNT CHARACTERS IN INPUT */ { LONG NC; NC = 0; WHILE (GETCHAR() != EOF ) ++NC; PRINTF("%1D\N", NC); } Operator ++NC; demonstriruet novuyu operaciyu, ++, kotoraya oznachaet uveliche- nie na edinicu. Vy mogli by napisat' NC = NC + 1 , no ++NC bolee kratko i zachastuyu bolee effektivno. Imeetsya sootvetst- vuyushchaya operaciya -- umen'shenie na edinicu. Operacii ++ i -- mogut byt' libo prefiksnymi (++NC), libo postfiksnymi (NC++); eti dve formy, kak budet pokazano v glave 2, imeyut v vyrazheniyah razlichnye znacheniya, no kak ++NC, tak i NC++ uve- lichivayut NC. Poka my budem priderzhivat'sya prefiksnyh opera- cij. Programma podscheta simvolov nakaplivaet ih kolichestvo v peremennoj tipa LONG, a ne INT . Na PDP-11 maksimal'noe zna- chenie ravno 32767, i esli opisat' schetchik kak INT , to on budet perepolnyat'sya dazhe pri sravnitel'no malom fajle vvoda; na yazyke "C" dlya HONEYWELL i IBM tipy LONG i INT yavlyayutsya sinonimami i imeyut znachitel'no bol'shij razmer. Specifikaciya preobrazovaniya %1D ukazyvaet PRINTF , chto sootvetstvuyushchij argument yavlyaetsya celym tipa LONG . CHtoby spravit'sya s eshche bol'shimi chislami, vy mozhete is- pol'zovat' tip DOUBLE / FLOAT dvojnoj dliny/. my takzhe is- pol'zuem operator FOR vmesto WHILE s tem, chtoby proillyustri- rovat' drugoj sposob zapisi cikla. MAIN() /* COUNT CHARACTERS IN INPUT */ { DOUBLE NC; FOR (NC = 0; GETCHAR() != EOF; ++NC) ; PRINTF("%.0F\N", NC); } Funkciya PRINTF ispol'zuet specifikaciyu %F kak dlya FLOAT , tak i dlya DOUBLE ; specifikaciya %.0F podavlyaet pechat' ne- sushchestvuyushchej drobnoj chasti. Telo operatora cikla FOR zdes' pusto, tak kak vsya rabo- ta vypolnyaetsya v proverochnoj i reinicializacionnoj chastyah. No grammaticheskie pravila yazyka "C" trebuyut, chtoby operator FOR imel telo. Izolirovannaya tochka s zapyatoj, sootvetstvuyu- shaya pustomu operatoru, poyavlyaetsya zdes', chtoby udovletvorit' etomu trebovaniyu. My vydelili ee na otdel'nuyu stroku, chtoby sdelat' ee bolee zametnoj. Prezhde chem my rasprostimsya s programmoj podscheta simvo- lov, otmetim, chto esli fajl vvoda ne soderzhit nikakih simvo- lov, to uslovie v WHILE ili FOR ne vypolnitsya pri samom per- vom obrashchenii k GETCHAR , i, sledovatel'no , programma vy- dast nul', t.e. Pravil'nyj otvet. eto vazhnoe zamechanie. od- nim iz priyatnyh svojstv operatorov WHILE i FOR yavlyaetsya to, chto oni proveryayut uslovie v nachale cikla, t.e. Do vypolneniya tela. Esli delat' nichego ne nado, to nichego ne budet sdela- no, dazhe esli eto oznachaet, chto telo cikla nikogda ne budet vypolnyat'sya. programmy dolzhny dejstvovat' razumno, kogda oni obrashchayutsya s fajlami tipa "nikakih simvolov". Operatory WHILE i FOR pomogayut obespechit' pravil'noe povedenie prog- ramm pri granichnyh znacheniyah proveryaemyh uslovij. 1.5.4. Podschet strok Sleduyushchaya programma podschityvaet kolichestvo strok v fajle vvoda. Predpolagaetsya, chto stroki vvoda zakanchivayutsya simvolom novoj stroki \N, skrupulezno dobavlennym k kazhdoj vypisannoj stroke. MAIN() /* COUNT LINES IN INPUT */ { INT C,NL; NL = 0; WHILE ((C = GETCHAR()) != EOF) IF (C =='\N') ++NL; PRINTF("%D\N", NL); } Telo WHILE teper' soderzhit operator IF , kotoryj v svoyu ochered' upravlyaet operatorom uvelicheniya ++NL. Operator IF proveryaet zaklyuchennoe v kruglye skobki uslovie i, esli ono istinno, vypolnyaet sleduyushchij za nim operator /ili gruppu operatorov, zaklyuchennyh v figurnye skobki/. My opyat' ispol'- zovali sdvig vpravo, chtoby pokazat', chto chem upravlyaet. Udvoennyj znak ravenstva == yavlyaetsya oboznacheniem v yazyke "C" dlya "ravno" /analogichno .EQ. V fortrane/. |tot simvol vveden dlya togo, chtoby otlichat' proverku na ravenstvo ot odinochnogo =, ispol'zuemogo pri prisvaivanii. Poskol'ku v tipichnyh "C" - programmah znak prisvaivaniya vstrechaetsya pri- merno v dva raza chashche, chem proverka na ravenstvo, to estest- venno, chtoby znak operatora byl vpolovinu koroche. Lyuboj otdel'nyj simvol mozhet byt' zapisan vnutri odi- nochnyh kavychek, i pri etom emu sootvetstvuet znachenie, rav- noe chislennomu znacheniyu etogo simvola v mashinnom nabore sim- volov; eto nazyvaetsya simvol'noj konstantoj. Tak, naprimer, 'A' - simvol'naya konstanta; ee znachenie v nabore simvolov ASCII /amerikanskij standartnyj kod dlya obmena informaciej/ ravno 65, vnutrennemu predstavleniyu simvola a. Konechno, 'A' predpochtitel'nee, chem 65: ego smysl ocheviden i on ne zavisit ot konkretnogo mashinnogo nabora simvolov. Uslovnye posledovatel'nosti, ispol'zuemye v simvol'nyh strokah, takzhe zanimayut zakonnoe mesto sredi simvol'nyh kon- stant. Tak v proverkah i arifmeticheskih vyrazheniyah '\N' predstavlyaet znachenie simvola novoj stroki. Vy dolzhny tverdo uyasnit', chto '\N' - otdel'nyj simvol, kotoryj v vyrazheniyah ekvivalenten odinochnomu celomu; s drugoj storony "\N" - eto simvol'naya stroka, kotoraya soderzhit tol'ko odin simvol. Vop- ros o sopostavlenii strok i simvolov obsuzhdaetsya v glave 2. Uprazhnenie 1-6 ---------------- Napishite programmu dlya podscheta probelov, tabulyacij i novyh strok. Uprazhnenie 1-7 ---------------- Napishite programmu, kotoraya kopiruet vvod na vyvod, za- menyaya pri etom kazhduyu posledovatel'nost' iz odnogo ili bolee probelov na odin probel. 1.5.5. Podschet slov CHetvertaya programma iz nashej serii poleznyh programm podschityvaet kolichestvo strok, slov i simvolov, ispol'zuya pri etom ves'ma shirokoe opredelenie, chto slovom yavlyaetsya lyu- baya posledovatel'nost' simvolov, ne soderzhashchaya probelov, ta- bulyacij ili novyh strok. /|to - uproshchennaya versiya utility 'WC' sistemy 'UNIX'/ #DEFINE YES 1 #DEFINE NO 0 MAIN() /* COUNT LINES, WORDS, CHARS IN INPUT */ { INT C, NL, NW, 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); } Kazhdyj raz, kogda programma vstrechaet pervyj simvol slova, ona uvelichivaet schetchik chisla slov na edinicu. Pere- mennaya INWORD sledit za tem, nahoditsya li programma v nasto- yashchij moment vnutri slova ili net; snachala etoj peremennoj prisvaivaetsya " ne v slove", chemu sootvetstvuet znachenie NO. My predpochitaem simvolicheskie konstanty YES i NO liternym znacheniyam 1 i 0, potomu chto oni delayut programmu bolee udob- noj dlya chteniya. Konechno, v takoj kroshechnoj programme, kak eta, eto ne privodit k zametnoj raznice, no v bol'shih prog- rammah uvelichenie yasnosti vpolne stoit teh skromnyh dopolni- tel'nyh usilij, kotoryh trebuet sledovanie etomu principu s samogo nachala. Vy takzhe obnaruzhite, chto sushchestvennye izmene- niya gorazdo legche vnosit' v te programmy, gde chisla figuri- ruyut tol'ko v kachestve simvol'nyh konstant. Stroka NL = NW = NC = 0; polagaet vse tri peremennye ravnymi nulyu. |to ne osobyj sluchaj, a sledstvie togo obstoyatel'stva, chto operato- ru prisvaivaniya sootvetstvuet nekotoroe znachenie i prisvai- vaniya provodyatsya posledovatel'no sprava nalevo. Takim obra- zom, delo obstoit tak, kak esli by my napisali NC = (NL = (NW = 0)); operaciya \!\! Oznachaet OR , tak chto stroka IF( C==' ' \!\! C=='\N' \!\! C=='\T') govorit "esli s - probel, ili s - simvol novoj stroki, ili s -tabulyaciya ..."./uslovnaya posledovatel'nost' \T yavlyaetsya izobrazheniem simvola tabulyacii/. Imeetsya sootvetstvuyushchaya operaciya && dlya AND. Vyrazheniya, svyazannye operaciyami && ili \!\! , Rassmatrivayutsya sleva na pravo, i pri etom garantiruetsya, chto ocenivanie vyrazhenij budet prekrashcheno, kak tol'ko stanet yasno, yavlyaetsya li vse vyrazhenie istinnym ili lozhnym. Tak, esli 'C' okazyvaetsya probelom, to net nikakoj neobhodimosti proveryat', yavlyaetsya li 'C' simvolom novoj stroki ili tabulyacii, i takie proverki dejstvitel'no ne delayutsya. V dannom sluchae eto ne imeet principial'nogo znacheniya, no, kak my skoro uvidim, v bolee slozhnyh situaciyah eta osobennost' yazyka ves'ma sushchestvenna. |tot primer takzhe demonstriruet operator ELSE yazyka "C", kotoryj ukazyvaet to dejstvie, kotoroe dolzhno vypolnyat'sya, esli uslovie, soderzhashcheesya v operatore IF, okazhetsya lozhnym. Obshchaya forma takova: IF (vyrazhenie) operator-1 ELSE operator-2 Vypolnyaetsya odin i tol'ko odin iz dvuh operatorov, svya- zannyh s konstrukciej IF-ELSE. Esli vyrazhenie istinno, vy- polnyaetsya operator-1; esli net - vypolnyaetsya operator-2. Fakticheski kazhdyj operator mozhet byt' dovol'no slozhnym. V programme podscheta slov operator, sleduyushchij za ELSE , yavlya- etsya opertorom IF , kotoryj upravlyaet dvumya operatorami v figurnyh skobkah. Uprazhnenie 1-9 ---------------- Kak by vy stali proveryat' programmu podscheta slov ? Kakie imeyutsya ogranicheniya ? Uprazhnenie 1-10 ----------------- Napishite programmu, kotoraya budet pechatat' slova iz faj- la vvoda, prichem po odnomu na stroku. Uprazhnenie 1-11 ---------------- Peredelajte programmu podscheta slov, ispol'zuya luchshee predelenie "slova"; schitajte, naprimer slovom posledovatel'- nost' bukv, cifr i apostrofov, rachinayushchuyusya s bukvy. 1.6. Massivy Davajte napishem programmu podscheta chisla poyavlenij kazh- doj cifry, simvolov pustyh promezhutkov/probel, tabulyacii, novaya stroka/ i vseh ostal'nyh simvolov. Konechno, takaya za- dacha neskol'ko iskusstvenna, no ona pozvolit nam proillyust- rirovat' v odnoj programme srazu neskol'ko aspektov yazyka "C". My razbili vvodimye simvoly na dvenadcat' kategorij, i nam udobnee ispol'zovat' massiv dlya hraneniya chisla poyavlenij kazhdoj cifry, a ne desyat' otdel'nyh peremennyh. Vot odin iz variantov programmy: MAIN() /* COUNT DIGITS, WHITE SPACE, OTHERS */ { 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); } Opisanie INT NDIGIT[10]; ob®yavlyaet, chto NDIGIT yavlyaetsya massivom iz desyati celyh. v yazyke "C" indeksy massiva vsegda nachinayutsya s nulya /a ne s 1, kak v fortrane ili PL/1/, tak chto elementami massiva yav- lyayutsya NDIGIT[0], NDIGIT[1],..., NDIGIT[9]. eta osobennost' otrazhena v ciklah FOR , kotorye inicializiruyut i pechatayut massiv. Indeks mozhet byt' lyubym celym vyrazheniem, kotoroe, ko- nechno, mozhet vklyuchat' celye peremennye, takie kak I , i ce- lye konstanty. |ta konkretnaya programma sil'no opiraetsya na svojstva simvol'nogo predstavleniya cifr. Tak, naprimer, v programme proverka IF( C >= '0' && C <= '9')... opredelyaet, yavlyaetsya li simvol v 'C' cifroj, i esli eto tak, to chislennoe znachenie etoj cifry opredelyaetsya po formule / C - '0'/. Takoj sposob rabotaet tol'ko v tom sluchae, esli zna- cheniya simvol'nyh konstant '0', '1' i t.d. Polozhitel'ny, ras- polozheny v poryadke vozrastaniya i net nichego, krome cifr, mezhdu konstantami '0' i '9'. K schast'yu, eto verno dlya vseh obshcheprinyatyh naborov simvolov. Po opredeleniyu pered provedeniem arifmeticheskih opera- cij, vovlekayushchih peremennye tipa CHAR i INT, vse oni preob- razuyutsya k tipu INT, TAK chto v arifmeticheskih vyrazheniyah pe- remennye tipa CHAR po sushchestvu identichny peremennym tipa INT. |to vpolne estestvenno i udobno; naprimer, C -'0'- eto celoe vyrazhenie so znacheniem mezhdu 0 i 9 v sootvetstvii s tem, kakoj simvol ot '0' do '9' hranitsya v 'C', i, sledova- tel'no, ono yavlyaetsya podhodyashchim indeksom dlya massiva NDIGIT. Vyyasnenie voprosa, yavlyaetsya li dannyj simvol cifroj, simvolom pustogo promezhutka ili chem-libo eshche, osushchestvlyaetsya posledovatel'nost'yu operatorov IF (C >= '0' && C <= '9') ++NDIGIT[C-'0']; ELSE IF(C == ' ' \!\! C == '\N' \!\! C == '\T') ++NWHITE; ELSE ++NOTHER; Konstrukciya IF (uslovie) operator ELSE IF (uslovie) operator ELSE operator chasto vstrechayutsya v programmah kak sredstvo vyrazheniya situa- cij, v kotoryh osushchestvlyaetsya vybor odnogo iz neskol'kih vozmozhnyh reshenij. Programma prosto dvizhetsya sverhu vniz do teh por, poka ne udovletvoritsya kakoe-nibud' uslovie; togda vypolnyaetsya sootvetstvuyushchij 'operator', i vsya konstrukciya zavershaetsya. /Konechno, 'operator' mozhet sostoyat' iz neskol'kih operato- rov, zaklyuchennyh v figurnye skobki/. Esli ni odno iz uslovij ne udovletvoryaetsya, to vypolnyaetsya 'operator', stoyashchij posle zaklyuchitel'nogo ELSE, esli ono prisutstvuet. Esli posledneE ELSE i sootvetstvuyushchij 'operator' opushcheny (kak v programme podscheta slov), to nikakih dejstvij ne proizvoditsya. Mezhdu nachal'nym IF i konechnym ELSE mozhet pomeshchat'sya proizvol'noe kolichestvo grupp ELSE IF (uslovie) operator S tochki zreniya stilya celesoobrazno zapisyvat' etu konst- rukciyu tak, kak my pokazali, s tem chtoby dlinnye vyrazheniya ne zalezali za pravyj kraj stranicy. Operator SWITCH (pereklyuchatel'), kotoryj rassmatrivaetsya v glave 3, predstavlyaet druguyu vozmozhnost' dlya zapisi raz- vetvleniya na neskol'ko variantov. etot operator osobenno udoben, kogda proveryaemoe vyrazhenie yavlyaetsya libo prosto ne- kotorym celym, libo simvol'nym vyrazheniem, sovpadayushchim s od- noj iz nekotorogo nabora konstant. Versiya etoj programmy, ispol'zuyushchaya operator SWITCH, budet dlya sravneniya privedena v glave 3. Uprazhnenie 1-12 ---------------- Napishite programmu, pechatayushchuyu gistogrammu dlin slov iz fajla vvoda. Samoe legkoe - nachertit' gistogrammu gorizon- tal'no; vertikal'naya orientaciya trebuet bol'shih usilij. 1.7. Funkcii V yazyke "C" funkcii ekvivalentny podprogrammam ili funk- ciyam v fortrane ili proceduram v PL/1, paskale i t.d. Funk- cii dayut udobnyj sposob zaklyucheniya nekotoroj chasti vychisle- nij v chernyj yashchik, kotoryj v dal'nejshem mozhno ispol'zovat', ne interesuyas' ego vnutrennim soderzhaniem. Ispol'zovanie funkcij yavlyaetsya fakticheski edinstvennym sposobom spravit'sya s potencial'noj slozhnost'yu bol'shih programm. Esli funkcii organizovany dolzhnym obrazom, to mozhno ignorirovat' to, kak delaetsya rabota; dostatochno znanie togo, chto delaetsya. YAzyk "C" razrabotan takim obrazom, chtoby sdelat' ispol'zovanie funkcij legkim, udobnym i effektivnym. Vam budut chasto vst- rechat'sya funkcii dlinoj vsego v neskol'ko strochek, vyzyvae- mye tol'ko odin raz, i oni ispol'zuyutsya tol'ko potomu, chto eto proyasnyaet nekotoruyu chast' programmy. Do sih por my ispol'zovali tol'ko predostavlennye nam funkcii tipa PRINTF, GETCHAR i PUTCHAR; teper' pora napisat' neskol'ko nashih sobstvennyh. tak kak v "C" net operacii voz- vedeniya v stepen', podobnoj operacii ** v fortrane ili PL/1, davajte proillyustriruem mehaniku opredeleniya funkcii na pri- mere funkcii POWER(M,N), vozvodyashchej celoe m v celuyu polozhi- tel'nuyu stepen' N. Tak znachenie POWER(2,5) ravno 32. Konech- no, eta funkciya ne vypolnyaet vsej raboty operacii **, pos- kol'ku ona dejstvuet tol'ko s polozhitel'nymi stepenyami ne- bol'shih chisel, no luchshe ne sozdavat' dopolnitel'nyh zatrud- nenij, smeshivaya neskol'ko razlichnyh voprosov. Nizhe privoditsya funkciya POWER i ispol'zuyushchaya ee osnovnaya programma, tak chto vy mozhete videt' celikom vsyu strukturu. MAIN() /* TEST POWER FUNCTION */ { INT I; FOR(I = 0; I < 10; ++I) PRINTF("%D %D %D\N",I,POWER(2,I),POWER(-3,I)); } POWER(X,N) /* RAISE X N-TH POWER; N > 0 */ INT X,N; { INT I, P; P = 1; FOR (I =1; I <= N; ++I) P = P * X; RETURN (P); } Vse funkcii imeyut odinakovyj vid: imya (spisok argumentov, esli oni imeyutsya) opisanie argumentov, esli oni imeyutsya { opisaniya operatory } |ti funkcii mogut byt' zapisany v lyubom poryadke i naho- dit'sya v odnom ili dvuh ishodnyh fajlah. Konechno, esli is- hodnaya programma razmeshchaetsya v dvuh fajlah, vam pridetsya dat' bol'she ukazanij pri kompilyacii i zagruzke, chem esli by ona nahodilas' v odnom, no eto delo operacionnoj sistemy, a ne atribut yazyka. V dannyj moment, dlya togo chtoby vse polu- chennye svedeniya o progone "C"- programm, ne izmenilis' v dal'nejshem, my budem predpolagat', chto obe funkcii nahodyatsya v odnom i tom zhe fajle. Funkciya POWER vyzyvaetsya dvazhdy v stroke PRINTF("%D %D %D\N",I,POWER(2,I),POWER(-3,I)); pri kazhdom obrashchenii funkciya POWER, poluchiv dva argumenta, vazvrashchaet celoe znachenie, kotoroe pechataetsya v zadannom formate. V vyrazheniyah POWER(2,I) yavlyaetsya tochno takim zhe ce- lym, kak 2 i I. /Ne vse funkcii vydayut celoe znachenie; my zajmemsya etim voprosom v glave 4/. Argumenty funkcii POWER dolzhny byt' opisany sootvetstvu- yushchim obrazom, tak kak ih tipy izvestny. |to sdelano v stroke INT X,N; kotoraya sleduet za imenem funkcii. Opisaniya argumentov pomeshchayutsya mezhdu spiskom argumentov i otkryvayushchejsya levoj figurnoj skobkoj; kazhdoe opisanie za- kanchivaetsya tochkoj s zapyatoj. Imena, ispol'zovannye dlya ar- gumentov funkcii POWER, yavlyayutsya chisto lokal'nymi i nedos- tupny nikakim drugim funkciyam: drugie procedury mogut is- pol'zovat' te zhe samye imena bez vozniknoveniya konflikta. |to verno i dlya peremennyh I i P; I v funkcii POWER nikak ne svyazano s I v funkcii MAIN. Znachenie, vychislennoe funkciej POWER, peredayutsya v MAIN s pomoshch'yu operatora RETURN, tochno takogo zhe, kak v PL/1. vnutri kruglyh skobok mozhno napisat' lyuboe vyrazhenie. Funk- ciya ne obyazana vozvrashchat' kakoe-libo znachenie; operator RETURN, ne soderzhashchij nikakogo vyrazheniya, privodit k takoj zhe peredache upravleniya, kak "svalivanie na konec" funkcii pri dostizhenii konechnoj pravoj figurnoj skobki, no pri etom v vyzyvayushchuyu funkciyu ne vozvrashchaetsya nikakogo poleznogo zna- cheniya. Uprazhnenie 1-13 ---------------- Apishite programmu preobrazovaniya propisnyh bukv iz ajla vvoda v strochnye, ispol'zuya pri etom funkciyu OWER(C), koto- raya vozvrashchaet znachenie 'C', esli C'- ne bukva, i znachenie sootvetstvuyushchej strochnoj ukvy, esli 'C'-bukva. 1.8. Argumenty - vyzov po znacheniyu Odin aspekt v "C" mozhet okazat'sya neprivychnym dlya prog- rammistov, kotorye ispol'zovali drugie yazyki, v chastnosti, fortran i PL/1. v yazyke "C" vse argumenty funkcij peredayutsya "po znacheniyu". eto oznachaet, chto vyzvannaya funkciya poluchaet znacheniya svoih argumentov s pomoshch'yu vremennyh peremennyh /fakticheski cherez stek/, a ne ih adresa. |to privodit k ne- kotorym osobennostyam, otlichnym ot teh, s kotorymi my stalki- valis' v yazykah tipa fortrana i PL/1, ispol'zuyushchih "vyzov po ssylke ", gde vyzvannaya procedura rabotaet s adresom argu- menta, a ne s ego znacheniem. Glavnoe otlichie sostoit v tom, chto v "C" vyzvannaya funk- ciya ne mozhet izmenit' peremennuyu iz vyzyvayushchej funkcii; ona mozhet menyat' tol'ko svoyu sobstvennuyu vremennuyu kopiyu. Vyzov po znacheniyu, odnako, ne pomeha, a ves'ma cennoe kachestvo. Ono obychno privodit k bolee kompaktnym programmam, soderzhashchim men'she ne otnosyashchihsya k delu peremennyh, potomu chto s argumentami mozhno obrashchat'sya kak s udobno inicializi- rovannymi lokal'nymi peremnnymi vyzvannoj procedury. Vot, naprimer, variant funkcii POWER ispol'zuyushchej eto obstoyatel'- stvo POWER(X,N) /* RAISE X N-TH POWER; N > 0; VERSION 2 */ INT X,N; { INT P; FOR (P = 1; N > 0; --N) P = P * X; RETURN (P); } Argument N ispol'zuetsya kak vremennaya peremennaya; iz ne- go vychitaetsya edinica do teh por, poka on ne stanet nulem. Peremennaya I zdes' bol'she ne nuzhna. chtoby ni proishodilo s N vnutri POWER eto nikak ne vliyaet na argument, s kotorym per- vonachal'no obratilis' k funkcii POWER. Pri neobhodimosti vse zhe mozhno dobit'sya, chtoby funkciya izmenila peremennuyu iz vyzyvayushchej programmy. |ta programma dolzhna obespechit' ustanovlenie adresa peremennoj /tehniches- ki, cherez ukazatel' na peremennuyu/, a v vyzyvaemoj funkcii nado opisat' sootvetstvuyushchij argument kak ukazatel' i ssy- lat'sya k fakticheskoj peremennoj kosvenno cherez nego. My ras- smotrim eto podrobno v glave 5. Kogda v kachestve argumenta vystupaet imya massiva, to fakticheskim znacheniem, peredavaemym funkcii, yavlyaetsya adres nachala massiva. /Zdes' net nikakogo kopirovaniya elementov massiva/. S pomoshch'yu indeksacii i adresa nachala funkciya mozhet najti i izmenit' lyuboj element massiva. |to - tema sleduyushche- go razdela. 1.9. Massivy simvolov Po-vidimomu samym obshchim tipom massiva v "C" yavlyaetsya massiv simvolov. CHtoby proillyustrirovat' ispol'zovanie mas- sivov simvolov i obrabatyvayushchih ih funkcij, davajte napishem programmu, kotoraya chitaet nabor strok i pechataet samuyu dlin- nuyu iz nih. Osnovnaya shema programmy dostatochno prosta: WHILE (imeetsya eshche stroka) IF (eta stroka dlinnee samoj dlinnoj iz predydushchih) zapomnit' etu stroku i ee dlinu napechatat' samuyu dlinnuyu stroku Po etoj sheme yasno, chto programma estestvennym obrazom raspadaetsya na neskol'ko chastej. Odna chast' chitaet novuyu stroku, drugaya proveryaet ee, tret'ya zapominaet, a ostal'nye chasti programmy upravlyayut etim processom. Poskol'ku vse tak prekrasno delitsya, bylo by horosho i napisat' programmu sootvetsvuyushchim obrazom. Davajte snachala napishem otdel'nuyu funkciyu GETLINE, kotoraya budet izvlekat' sleduyushchuyu stroku iz fajla vvoda; eto - obobshchenie funkcii GETCHAR. my popytaemsya sdelat' etu funkciyu po vozmozhnosti bolee gibkoj, chtoby ona byla poleznoj i v drugih situaciyah. Kak minimum GETLINE dolzhna peredavat' signal o vozmozhnom po- yavlenii konca fajla; bolee obshchij poleznyj variant mog by pe- redavat' dlinu stroki ili nul', esli vstretitsya konec fajla. nul' ne mozhet byt' dlinoj stroki, tak kak kazhdaya stroka so- derzhit po krajnej mere odin simvol; dazhe stroka, soderzhashchaya tol'ko simvol novoj stroki, imeet dlinu 1. Kogda my nahodim stroku, kotoraya dlinnee samoj dlinnoj iz predydushchih, to ee nado gde-to zapomnit'. |to navodit na mysl' o drugoj funkcii, COPY , kotoraya budet kopirovat' no- vuyu stroku v mesto hraneniya. Nakonec, nam nuzhna osnovnaya programma dlya upravleniya funkciyami GETLINE i COPY . Vot rezul'tat : #DEFINE MAXLINE 1000 /* MAXIMUM INPUT LINE SIZE */ MAIN() /* FIND LONGEST LINE */ { INT LEN; /* CURRENT LINE LENGTH */ INT MAX; /* MAXIMUM LENGTH SEEN SO FAR */ CHAR LINE[MAXLINE]; /* CURRENT INPUT LINE */ CHAR SAVE[MAXLINE]; /* LONGEST LINE, SAVED */ MAX = 0; WHILE ((LEN = GETLINE(LINE, MAXLINE)) > 0) IF (LEN > MAX) { MAX = LEN; COPY(LINE, SAVE); } IF (MAX > 0) /* THERE WAS A LINE */ PRINTF("%S", SAVE); } GETLINE(S,LIM) /* GET LINE INTO S,RETURN LENGTH */ CHAR S[]; INT LIM; { INT C, I; FOR(I=0;I<LIM-1 && (C=GETCHAR())!=EOF && C!='\N';++I) S[I] = C; IF (C == '\N') { S[I] = C; ++I; } S[I] = '\0'; RETURN(I); } COPY(S1, S2) /* COPY S1 TO S2; ASSUME S2 BIG ENOUGH */ CHAR S1[], S2[]; { INT I; I = 0; WHILE ((S2[I] = S1[I] != '\0') ++I; } Funkciya MAIN i GETLINE obshchayutsya kak cherez paru argumen- tov, tak i cherez vozvrashchaemoe znachenie. argumenty GETLINE opisany v strokah CHAR S[]; INT LIM; kotorye ukazyvayut, chto pervyj argument yavlyaetsya massivom, a vtoroj - celym. Dlina massiva S ne ukazana, tak kak ona opredelena v MAIN . funkciya GETLINE ispol'zuet operator RETURN dlya pere- dachi znacheniya nazad v vyzyvayushchuyu programmu tochno tak zhe, kak eto delala funkciya POWER. Odni funkcii vozvrashchayut nekotoroe nuzhnoe znachenie; drugie, podobno COPY, ispol'zuyutsya iz-za ih dejstviya i ne vozvrashchayut nikakogo znacheniya. CHtoby pometit' konec stroki simvolov, funkciya GETLINE pomeshchaet v konec sozdavaemogo ej massiva simvol \0 /nulevoj simvol, znachenie kotorogo ravno nulyu/. |to soglashenie is- pol'zuetsya takzhe kompilyatorom s yazyka "C": kogda v "C" - programme vstrechaetsya strochnaya konstanta tipa "HELLO\N" to kompilyator sozdaet massiv simvolov, soderzhashchij simvoly etoj stroki, i zakanchivaet ego simvolom \0, s tem chtoby fun- kcii, podobnye PRINTF, mogli zafiksirovat' konec massiva: ------------------------------------------- ! H ! E ! L ! L ! O ! \N ! \0 ! ------------------------------------------- Specifikaciya formata %S ukazyvaet, chto PRINTF ozhidaet stro- ku, predstavlennuyu v takoj forme. Proanalizirovav funkciyu COPY, vy obnaruzhite, chto i ona opiraetsya na tot fakt, chto ee vhodnoj argument okanchivaetsya simvolom \0, i kopiruet etot simvol v vyhodnoj argument S2. /Vse eto podrazumevaet, chto simvol \0 ne yavlyaetsya chast'yu normal'nogo teksta/. Mezhdu prochim, stoit otmetit', chto dazhe v takoj malen'koj programme, kak eta, voznikaet neskol'ko nepriyatnyh organiza- cionnyh problem. Naprimer, chto dolzhna delat' MAIN, esli ona vstretit stroku, prevyshayushchuyu ee maksimal'no vozmozhnyj raz- mer? Funkciya GETLINE postupaet razumno: pri zapolnenii mas- siva ona prekrashchaet dal'nejshee izvlechenie simvolov, dazhe es- li ne vstrechaet simvola novoj stroki. Proveriv poluchennuyu dlinu i poslednij simvol, funkciya MAIN mozhet ustanovit', ne byla li eta stroka slishkom dlinnoj, i postupit' zatem, kak ona sochtet nuzhnym. Radi kratkosti my opustili etu problemu. Pol'zovatel' funkcii GETLINE nikak ne mozhet zaranee uz- nat', naskol'ko dlinnoj okazhetsya vvodimaya stroka. Poetomu v GETLINE vklyuchen kontrol' perepolneniya. v to zhe vremya pol'zo- vatel' funkcii COPY uzhe znaet /ili mozhet uznat'/, kakov raz- mer strok, tak chto my predpochli ne vklyuchat' v etu funkciyu dopolnitel'nyj kontrol'. Uprazhnenie 1-14 ----------------- Peredelajte vedushchuyu chast' programmy poiska samoj dlinnoj stroki takim obrazom, chtoby ona pravil'no pechatala dliny skol' ugodno dlinnyh vvodimyh strok i vozmozhno bol'shij tekst. Uprazhnenie 1-15 ----------------- Napishite programmu pechati vseh strok dlinnee 80 simvo- lov. Uprazhnenie 1-16 ----------------- Napishite programmu, kotoraya budet udalyat' iz kazhdoj stroki stoyashchie v konce probely i tabulyacii, a takzhe stroki, celikom sostoyashchie iz probelov. Uprazhnenie 1-17 ----------------- Napishite funkciyu REVERSE(S), kotoraya raspologaet sim- vol'nuyu stroku S v obratnom poryadke. S ee pomoshch'yu napishite programmu, kotoraya obratit kazhduyu stroku iz fajla vvoda. 1.10. Oblast' dejstviya: vneshnie peremennye Peremennye v MAIN(LINE, SAVE i t.d.) yavlyayutsya vnutrenni- mi ili lokal'nymi po otnosheniyu k funkcii MAIN, potomu chto oni opisany vnutri MAIN i nikakaya drugaya funkciya ne imeet k nim pryamogo dostupa. |to zhe verno i otnositel'no peremennyh v drugih funkciyah; naprimer, peremennaya I v funkcii GETLINE nikak ne svyazana s I v COPY. Kazhdaya lokal'naya peremennaya su- shchestvuet tol'ko togda, kogda proizoshlo obrashchenie k sootvets- tvuyushchej funkcii, i ischezaet, kak tol'ko zakonchitsya vypolne- nie etoj funkcii. Po etoj prichine takie peremennye, sleduya terminologii drugih yazykov, obychno nazyvayut avtomaticheskimi. My vpred' budem ispol'zovat' termin avtomaticheskie pri ssyl- ke na eti dinamicheskie lokal'nye peremennye. /v glave 4 ob- suzhdaetsya klass staticheskoj pamyati, kogda lokal'nye peremen- nye vse zhe okazyvayutsya v sostoyanii sohranit' svoi znacheniya mezhdu obrashcheniyami k funkciyam/. Poskol'ku avtomaticheskie peremennye poyavlyayutsya i ischeza- yut vmeste s obrashcheniem k funkcii, oni ne sohranyayut svoih znachenij v promezhutke ot odnogo vyzova do drugogo, v silu chego im pri kazhdom vhode nuzhno yavno prisvaivat' znacheniya. Esli etogo ne sdelat', to oni budut soderzhat' musor. V kachestve al'ternativy k avtomaticheskim peremennym mozh- no opredelit' peremennye, kotorye budut vneshnimi dlya vseh funkcij, t.e. Global'nymi peremennymi, k kotorym mozhet obra- tit'sya po imeni lyubaya funkciya, kotoraya pozhelaet eto sdelat'. (etot mehanizm ves'ma shoden s "COMMON" v fortrane i "EXTERNAL" v PL/1). Tak kak vneshnie peremennye dostupny vsyu- du, ih mozhno ispol'zovat' vmesto spiska argumentov dlya pere- dachi dannyh mezhdu funkciyami. Krome togo, poskol'ku vneshnie peremennye sushchestvuyut postoyanno, a ne poyavlyayutsya i ischezayut vmeste s vyzyvaemymi funkciyami, oni sohranyayut svoi znacheniya i posle togo, kak funkcii, prisvoivshie im eti znacheniya, za- vershat svoyu rabotu. Vneshnyaya peremennaya dolzhna byt' opredelena vne vseh funk- cij; pri etom ej vydelyaetsya fakticheskoe mesto v pamyati. Ta- kaya peremennaya dolzhna byt' takzhe opisana v kazhdoj funkcii, kotoraya sobiraetsya ee ispol'zovat'; eto mozhno sdelat' libo yavnym opisaniem EXTERN, libo neyavnym po kontekstu. CHtoby sdelat' obsuzhdenie bolee konkretnym, davajte perepishem prog- rammu poiska samoj dlinnoj stroki, sdelav LINE, SAVE i MAX vneshnimi peremennymi. |to potrebuet izmeneniya opisanij i tel vseh treh funkcij, a takzhe obrashchenij k nim. #DE