FINE MAXLINE 1000 /* MAX. INPUT LINE SIZE*/ CHAR LINE[MAXLINE]; /* INPUT LINE */ CHAR SAVE[MAXLINE];/* LONGEST LINE SAVED HERE*/ INT MAX;/*LENGTH OF LONGEST LINE SEEN SO FAR*/ MAIN() /*FIND LONGEST LINE; SPECIALIZED VERSION*/ { INT LEN; EXTERN INT MAX; EXTERN CHAR SAVE[]; MAX = 0; WHILE ( (LEN = GETLINE()) > 0 ) IF ( LEN > MAX ) { MAX = LEN; COPY(); } IF ( MAX > 0 ) /* THERE WAS A LINE */ PRINTF( "%S", SAVE ); } GETLINE() /* SPECIALIZED VERSION */ { INT C, I; EXTERN CHAR LINE[]; FOR (I = 0; I < MAXLINE-1 && (C=GETCHAR()) !=EOF && C!='\N'; ++I) LINE[I] = C; ++I; } LINE[I] = '\0' RETURN(I) } COPY() /* SPECIALIZED VERSION */ { INT I; EXTERN CHAR LINE[], SAVE[]; I = 0; WHILE ((SAVE[I] = LINE[I]) !='\0') ++I; } Vneshnie peremennye dlya funkcij MAIN, GETLINE i COPY op- redeleny v pervyh strochkah privedennogo vyshe primera, koto- rymi ukazyvaetsya ih tip i vyzyvaetsya otvedenie dlya nih pamya- ti. sintaksicheski vneshnie opisaniya tochno takie zhe, kak opi- saniya, kotorye my ispol'zovali ranee, no tak kak oni raspo- lozheny vne funkcij, sootvetstvuyushchie peremennye yavlyayutsya vneshnimi. CHtoby funkciya mogla ispol'zovat' vneshnyuyu pereme- nuyu, ej nado soobshchit' ee imya. Odin sposob sdelat' eto - vklyuchit' v funkciyu opisanie EXTERN; eto opisanie otlichaetsya ot predydushchih tol'ko dobavleniem klyuchevogo slova EXTERN. V opredelennyh situaciyah opisanie EXTERN mozhet byt' opu- shcheno: esli vneshnee opredelenie peremennoj nahoditsya v tom zhe ishodnom fajle, ran'she ee ispol'zovaniya v nekotoroj konkret- noj funkcii, to ne obyazatel'no vklyuchat' opisanie EXTERN dlya etoj peremennoj v samu funkciyu. Opisaniya EXTERN v funkciyah MAIN, GETLINE i COPY yavlyayutsya, takim obrazom, izlishnimi. Fakticheski, obychnaya praktika zaklyuchaetsya v pomeshchenii oprede- lenij vseh vneshnih peremennyh v nachale ishodnogo fajla i posleduyushchem opuskanii vseh opisanij EXTERN. Esli programma nahoditsya v neskol'kih ishodnyh fajlah, i nekotoraya peremennaya opredelena, skazhem v fajle 1, a ispol'- zuetsya v fajle 2, to chtoby svyazat' eti dva vhozhdeniya pere- mennoj, neobhodimo v fajle 2 ispol'zovat' opisanie EXTERN. |tot vopros podrobno obsuzhdaetsya v glave 4. Vy dolzhno byt' zametili, chto my v etom razdele pri ssyl- ke na vneshnie peremennye ochen' akkuratno ispol'zuem slova opisanie i opredelenie. "Opredelenie" otnositsya k tomu mes- tu, gde peremennaya fakticheski zavoditsya i ej vydelyaetsya pa- myat'; "opisanie" otnositsya k tem mestam, gde ukazyvaetsya priroda peremennoj, no nikakoj pamyati ne otvoditsya. Mezhdu prochim, sushchestvuet tendenciya ob®yavlyat' vse, chto ni popadetsya, vneshnimi peremennymi, poskol'ku kazhetsya, chto eto uproshchaet svyazi, - spiski argumentov stanovyatsya koroche i pe- remennye vsegda prisutstvuyut, kogda by vam oni ni ponadobi- lis'. No vneshnie peremennye prisutstvuyut i togda, kogda vy v nih ne nuzhdaetes'. Takoj stil' programmirovaniya chrevat opas- nost'yu, tak kak on privodit k programmam, svyazi dannyh vnut- ri kotoryh ne vpolne ochevidny. Peremennye pri etom mogut iz- menyat'sya neozhidannym i dazhe neumyshlennym obrazom, a program- my stanovitsya trudno modificirovat', kogda voznikaet takaya neobhodimost'. Vtoraya versiya programmy poiska samoj dlinnoj stroki ustupaet pervoj otchasti po etim prichinam, a otchasti potomu, chto ona lishila universal'nosti dve ves'ma poleznye funkcii, vvedya v nih imena peremennyh, s kotorymi oni budut manipulirovat'. Uprazhnenie 1-18 --------------- Proverka v operatore FOR funkcii GETLINE dovol'no neuk- lyuzha. Perepishite programmu takim obrazom, chtoby sdelat' etu proverku bolee yasnoj, no sohranite pri etom to zhe samoe po- vedenie v konce fajla i pri perepolnenii bufera. YAvlyaetsya li eto povedenie samym razumnym? 1.11. Rezyume Na dannom etape my obsudili to, chto mozhno by nazvat' tradicionnym yadrom yazyka "C". Imeya etu gorst' stroitel'nyh blokov, mozhno pisat' poleznye programmy ves'ma znachitel'nogo razmera, i bylo by veroyatno neplohoj ideej, esli by vy za- derzhalis' zdes' na kakoe-to vremya i postupili takim obrazom: sleduyushchie nizhe uprazhneniya predlagayut vam ryad programm nes- kol'ko bol'shej slozhnosti, chem te, kotorye byli privedeny v etoj glave. Posle togo kak vy ovladeete etoj chast'yu "C", pristupajte k chteniyu sleduyushchih neskol'kih glav. Usiliya, kotorye vy pri etom zatratite, polnost'yu okupyatsya, potomu chto v etih glavah obsuzhdayutsya imenno te storony "C", gde moshch' i vyrazitel'- nost' yazyka nachinaet stanovit'sya ochevidnoj. Uprazhnenie 1-19 --------------- Napishite programmu DETAB, kotoraya zamenyaet tabulyacii vo vvode na nuzhnoe chislo probelov tak, chtoby promezhutok dosti- gal sleduyushchej tabulyacionnoj ostanovki. Predpolozhite fiksiro- vannyj nabor tabulyacionnyh ostanovok, naprimer, cherez kazhdye N pozicij. Uprazhnenie 1-20 ---------------- Napishite programmu ENTAB, kotoraya zamenyaet stroki probe- lov minimal'nym chislom tabulyacij i probelov, dostigaya pri etom teh zhe samyh promezhutkov. Ispol'zujte te zhe tabulyacion- nye ostanovki, kak i v DETAB. Uprazhnenie 1-21 ---------------- Napishite programmu dlya "sgibaniya" dlinnyh vvodimyh strok posle poslednego otlichnogo ot probela simvola, stoyashchego do stolbca N vvoda, gde N - parametr. ubedites', chto vasha prog- ramma delaet chto-to razumnoe s ochen' dlinnymi strokami i v sluchae, kogda pered ukazannym stolbcom net ni tabulyacij, ni probelov. Uprazhnenie 1-22 ---------------- Napishite programmu udaleniya iz "C"-programmy vseh kom- mentariev. Ne zabyvajte akkuratno obrashchat'sya s "zakavychenny- mi" strokami i simvol'nymi konstantami. Uprazhnenie 1-23 ---------------- Napishite programmu proverki "C"-programmy na elementar- nye sintaksicheskie oshibki, takie kak nesootvetstvie kruglyh, kvadratnyh i figurnyh skobok. Ne zabud'te o kavychkah, kak odinochnyh, tak i dvojnyh, i o kommentariyah. (|ta programma ves'ma slozhna, esli vy budete pisat' ee dlya samogo obshchego sluchaya).  * 2. Tipy, operacii i vyrazheniya *  Peremennye i konstanty yavlyayutsya osnovnymi ob®ektami, s kotorymi operiruet programma. Opisaniya perechislyayut peremen- nye, kotorye budut ispol'zovat'sya, ukazyvayut ih tip i, voz- mozhno, ih nachal'nye znacheniya. Operacii opredelyayut, chto s ni- mi budet sdelano. vyrazheniya ob®edinyayut peremennye i konstan- ty dlya polucheniya novyh znachenij. Vse eto - temy nastoyashchej glavy. 2.1. Imena peremennyh Hotya my etogo srazu pryamo ne skazali, sushchestvuyut nekoto- rye ogranicheniya na imena peremennyh i simvolicheskih kons- tant. Imena sostavlyayutsya iz bukv i cifr; pervyj simvol dol- zhen byt' bukvoj. Podcherkivanie "_" tozhe schitaetsya bukvoj; eto polezno dlya udobochitaemosti dlinnyh imen peremennyh. Propisnye i strochnye bukvy razlichayutsya; tradicionnaya prakti- ka v "s" - ispol'zovat' strochnye bukvy dlya imen peremennyh, a propisnye - dlya simvolicheskih konstant. Igrayut rol' tol'ko pervye vosem' simvolov vnutrennego imeni, hotya ispol'zovat' mozhno i bol'she. Dlya vneshnih imen, takih kak imena funkcij i vneshnih peremennyh, eto chislo mo- zhet okazat'sya men'she vos'mi, tak kak vneshnie imena ispol'zu- yutsya razlichnymi assemblerami i zagruzchikami. Detali privo- dyatsya v prilozhenii a. Krome togo, takie klyuchevye slova kak IF, ELSE, INT, FLOAT i t.d., zarezervirovany: vy ne mozhete ispol'zovat' ih v kachestve imen peremennyh. (Oni pishutsya strochnymi bukvami). Konechno, razumno vybirat' imena peremennyh takim obra- zom, chtoby oni oznachali nechto, otnosyashcheesya k naznacheniyu pe- remennyh, i chtoby bylo menee veroyatno sputat' ih pri napisa- nii. 2.2. Tipy i razmery dannyh YAzyke "C" imeetsya tol'ko neskol'ko osnovnyh tipov dan- nyh: CHAR odin bajt, v kotorom mozhet nahodit'sya odin simvol iz vnutrennego nabora simvolov. INT Celoe, obychno sootvetstvuyushchee estestvennomu razmeru ce- lyh v ispol'zuemoj mashine. FLOAT S plavayushchej tochkoj odinarnoj tochnosti. DOUBLE S plavayushchej tochkoj dvojnoj tochnosti. Krome togo imeetsya ryad kvalifikatorov, kotorye mozhno is- pol'zovat' s tipom INT: SHORT (korotkoe), LONG (dlinnoe) i UNSIGNED (bez znaka). Kvalifikatory SHORT i LONG ukazyvayut na razlichnye razmery celyh. CHisla bez znaka podchinyayutsya za- konam arifmetiki po modulyu 2 v stepeni N, gde N - chislo bi- tov v INT; chisla bez znakov vsegda polozhitel'ny. Opisaniya s kvalifikatorami imeyut vid: SHORT INT X; LONG INT Y; UNSIGNED INT Z; Clovo INT v takih situaciyah mozhet byt' opushcheno, chto obychno i delaetsya. Kolichestvo bitov, otvodimyh pod eti ob®ekty zavisit ot imeyushchejsya mashiny; v tablice nizhe privedeny nekotorye harak- ternye znacheniya. Tablica 1 --------------------------------------------------------- ! DEC PDP-11 HONEYWELL IBM 370 INTERDATA ! 6000 8/32 ! ! ASCII ASCII EBCDIC ASCII ! ! CHAR 8-BITS 9-BITS 8-BITS 8-BITS ! INT 16 36 32 32 ! SHORT 16 36 16 16 ! LONG 32 36 32 32 ! FLOAT 32 36 32 32 ! DOUBLE 64 72 64 64 ! ! --------------------------------------------------------- Cel' sostoit v tom, chtoby SHORT i LONG davali vozmozh- nost' v zavisimosti ot prakticheskih nuzhd ispol'zovat' raz- lichnye dliny celyh; tip INT otrazhaet naibolee "estestvennyj" razmer konkretnoj mashiny. Kak vy vidite, kazhdyj kompilyator svobodno interpretiruet SHORT i LONG v sootvetstvii so svoi- mi apparatnymi sredstvami. Vse, na chto vy mozhete tverdo po- lagat'sya, eto to, chto SHORT ne dlinnee, chem LONG. 2.3. Konstanty Konstanty tipa INT i FLOAT my uzhe rassmotreli. Otmetim eshche tol'ko, chto kak obychnaya 123.456e-7, tak i "nauchnaya" zapis' 0.12e3 dlya FLOAT yavlyaetsya zakonnoj. Kazhdaya konstanta s plavayushchej tochkoj schitaetsya imeyushchej tip DOUBLE, tak chto oboznachenie "E" sluzhit kak dlya FLOAT, tak i dlya DOUBLE. Dlinnye konstanty zapisyvayutsya v vide 123L. Obychnaya ce- laya konstanta, kotoraya slishkom dlinna dlya tipa INT, rassmat- rivaetsya kak LONG. Sushchestvuet sistema oboznachenij dlya vos'merichnyh i shest- nadcaterichnyh konstant: lidiruyushchij 0(nul') v konstante tipa INT ukazyvaet na vos'merichnuyu konstantu, a stoyashchie vperedi 0X sootvetstvuyut shestnadcaterichnoj konstante. Naprimer, de- syatichnoe chislo 31 mozhno zapisat' kak 037 v vos'merichnoj for- me i kak 0X1F v shestnadcaterichnoj. SHestnadcaterichnye i vos'- merichnye konstanty mogut takzhe zakanchivat'sya bukvoj L, chto delaet ih otnosyashchimisya k tipu LONG. 2.3.1. Simvol'naya konstanta Simvol'naya konstanta - eto odin simvol, zaklyuchennyj v odinarnye kavychki, kak, naprimer, 'h'. Znacheniem simvol'noj konstanty yavlyaetsya chislennoe znachenie etogo simvola vo vnut- rennem mashinnom nabore simvolov. Naprimer, v nabore simvolov ASCII simvol'nyj nul', ili '0', imeet znachenie 48, a v kode EBCDIC - 240, i oba eti znacheniya sovershenno otlichny ot chisla 0. Napisanie '0' vmesto chislennogo znacheniya, takogo kak 48 ili 240, delaet programmu ne zavisyashchej ot konkretnogo chis- lennogo predstavleniya etogo simvola v dannoj mashine. Sim- vol'nye konstanty tochno tak zhe uchastvuyut v chislennyh opera- ciyah, kak i lyubye drugie chisla, hotya naibolee chasto oni is- pol'zuyutsya v sravnenii s drugimi simvolami. Pravila preobra- zovaniya budut izlozheny pozdnee. Nekotorye negraficheskie simvoly mogut byt' predstavleny kak simvol'nye konstanty s pomoshch'yu uslovnyh posledovatel'- nostej, kak, naprimer, \N (novaya stroka), \T (tabulyaciya), \0 (nulevoj simvol), \\ (obratnaya kosaya cherta), \' (odinarnaya kavychka) i t.d. Hotya oni vyglyadyat kak dva simvola, na samom dele yavlyayutsya odnim. Krome togo, mozhno sgenerirovat' proiz- vol'nuyu posledovatel'nost' dvoichnyh znakov razmerom v bajt, esli napisat' '\DDD' gde DDD - ot odnoj do treh vos'merichnyh cifr, kak v #DEFINE FORMFEED '\014' /* FORM FEED */ Simvol'naya konstanta '\0', izobrazhayushchaya simvol so znache- niem 0, chasto zapisyvaetsya vmesto celoj konstanty 0 , chtoby podcherknut' simvol'nuyu prirodu nekotorogo vyrazheniya. 2.3.2. Konstantnoe vyrazhenie Konstantnoe vyrazhenie - eto vyrazhenie, sostoyashchee iz od- nih konstant. Takie vyrazheniya obrabatyvayutsya vo vremya kompi- lyacii, a ne pri progone programmy, i sootvetstvenno mogut byt' ispol'zovany v lyubom meste, gde mozhno ispol'zovat' kon- stantu, kak, naprimer v #DEFINE MAXLINE 1000 CHAR LINE[MAXLINE+1]; ili SECONDS = 60 * 60 * HOURS; 2.3.3. Strochnaya konstanta Strochnaya konstanta - eto posledovatel'nost', sostoyashchaya iz nulya ili bolee simvolov, zaklyuchennyh v dvojnye kavychki, kak, naprimer, "I AM A STRING" /* ya - stroka */ ili "" /* NULL STRING */ /* nul'-stroka */ Kavychki ne yavlyayutsya chast'yu stroki, a sluzhat tol'ko dlya ee ogranicheniya. te zhe samye uslovnye posledovatel'nosti, ko- torye ispol'zovalis' v simvol'nyh konstantah, primenyayutsya i v strokah; simvol dvojnoj kavychki izobrazhaetsya kak \". S tehnicheskoj tochki zreniya stroka predstavlyaet soboj massiv, elementami kotorogo yavlyayutsya otdel'nye simvoly. CHto- by programmam bylo udobno opredelyat' konec stroki, kompilya- tor avtomaticheski pomeshchaet v konec kazhdoj stroki nul'-simvol \0. Takoe predstavlenie oznachaet, chto ne nakladyvaetsya konk- retnogo ogranicheniya na to, kakuyu dlinu mozhet imet' stroka, i chtoby opredelit' etu dlinu, programmy dolzhny prosmatrivat' stroku polnost'yu. Pri etom dlya fizicheskogo hraneniya stroki trebuetsya na odnu yachejku pamyati bol'she, chem chislo zaklyuchen- nyh v kavychki simvolov. Sleduyushchaya funkciya STRLEN(S) vychislya- et dlinu simvol'noj stroki S ne schitaya konechnyj simvol \0. STRLEN(S) /* RETURN LENGTH OF S */ CHAR S[]; { INT I; I = 0; WHILE (S[I] != '\0') ++I; RETURN(I); } Bud'te vnimatel'ny i ne putajte simvol'nuyu konstantu so strokoj, soderzhashchej odin simvol: 'X' - eto ne to zhe samoe, chto "X". Pervoe - eto otdel'nyj simvol, ispol'zovannyj s cel'yu polucheniya chislennogo znacheniya, sootvetstvuyushchego bukve h v mashinnom nabore simvolov. Vtoroe - simvol'naya stroka, sostoyashchaya iz odnogo simvola (bukva h) i \0. 2.4. Opisaniya Vse peremennye dolzhny byt' opisany do ih ispol'zovaniya, hotya nekotorye opisaniya delayutsya neyavno, po kontekstu. Opi- sanie sostoit iz specifikatora tipa i sleduyushchego za nim spiska peremennyh, imeyushchih etot tip, kak, naprimer, INT LOWER, UPPER, STEP; CHAR C, LINE[1000]; Peremennye mozhno raspredelyat' po opisaniyam lyubym obra- zom; privedennye vyshe spiski mozhno s tem zhe uspehom zapisat' v vide INT LOWER; INT UPPER; INT STEP; CHAR C; CHAR LINE[1000]; Takaya forma zanimaet bol'she mesta, no ona udobna dlya do- bavleniya kommentariya k kazhdomu opisaniyu i dlya posleduyushchih modifikacij. Peremennym mogut byt' prisvoeny nachal'nye znacheniya vnut- ri ih opisaniya, hotya zdes' imeyutsya nekotorye ogranicheniya. Esli za imenem peremennoj sleduyut znak ravenstva i konstan- ta, to eta konstanta sluzhit v kachestve inicializatora, kak, naprimer, v CHAR BACKSLASH = '\\'; INT I = 0; FLOAT EPS = 1.0E-5; Esli rassmatrivaemaya peremennaya yavlyaetsya vneshnej ili staticheskoj, to inicializaciya provoditsya tol'ko odin raz, soglasno koncepcii do nachala vypolneniya programmy. Iniciali- ziruemym yavno avtomaticheskim peremennym nachal'nye znacheniya prisvaivayutsya pri kazhdom obrashchenii k funkcii, v kotoroj oni opisany. Avtomaticheskie peremennye, ne inicializiruemye yav- no, imeyut neopredelennye znacheniya, (t.e. musor). Vneshnie i staticheskie peremennye po umolchaniyu inicializiruyutsya nulem, no, tem ne menee, ih yavnaya inicializaciya yavlyaetsya priznakom horoshego stilya. My prodolzhim obsuzhdenie voprosov inicializacii, kogda budem opisyvat' novye tipy dannyh. 2.5. Arifmeticheskie operacii Binarnymi arifmeticheskimi operaciyami yavlyayutsya +, -, *, / i operaciya deleniya po modulyu %. Imeetsya unarnaya operaciya -, no ne sushchestvuet unarnoj operacii +. Pri delenii celyh drobnaya chast' otbrasyvaetsya. Vyrazhenie X % Y daet ostatok ot deleniya X na Y i, sledovatel'no, ravno nulyu, kogda h delitsya na Y tochno. Naprimer, god yavlyaetsya visokos- nym, esli on delitsya na 4, no ne delitsya na 100, isklyuchaya to, chto delyashchiesya na 400 gody tozhe yavlyayutsya visokosnymi. Po- etomu IF(YEAR % 4 == 0 && YEAR % 100 != 0 \!\! YEAR % 400 == 0) god visokosnyj ELSE god nevisokosnyj Operaciyu % nel'zya ispol'zovat' s tipami FLOAT ili DOUBLE. Operacii + i - imeyut odinakovoe starshinstvo, kotoroe mladshe odinakovogo urovnya starshinstva operacij *, / i %, ko- torye v svoyu ochered' mladshe unarnogo minusa. Arifmeticheskie operacii gruppiruyutsya sleva napravo. (Svedeniya o starshinstve i associativnosti vseh operacij sobrany v tablice v konce etoj glavy). Poryadok vypolneniya associativnyh i kommutativ- nyh operacij tipa + i - ne fiksiruetsya; kompilyator mozhet pe- regruppirovyvat' dazhe zaklyuchennye v kruglye skobki vyrazhe- niya, svyazannye takimi operaciyami. takim obrazom, a+(B+C) mo- zhet byt' vychisleno kak (A+B)+C. |to redko privodit k kako- mu-libo rashozhdeniyu, no esli neobhodimo obespechit' strogo opredelennyj poryadok, to nuzhno ispol'zovat' yavnye promezhu- tochnye peremennye. Dejstviya, predprinimaemye pri perepolnenii i antipere- polnenii (t.e. Pri poluchenii slishkom malen'kogo po absolyut- noj velichine chisla), zavisyat ot ispol'zuemoj mashiny. 2.6. Operacii otnosheniya i logicheskie operacii Operaciyami otnosheniya yavlyayutsya => > =< < vse oni imeyut odinakovoe starshinstvo. Neposredstvenno za ni- mi po urovnyu starshinstva sleduyut operacii ravenstva i nera- venstva: == != kotorye tozhe imeyut odinakovoe starshinstvo. operacii otnoshe- niya mladshe arifmeticheskih operacij, tak chto vyrazheniya tipa I<LIM-1 ponimayutsya kak I<(LIM-1), kak i predpolagaetsya. Logicheskie svyazki && i \!\! bolee interesny. Vyrazheniya, svyazannye operaciyami && i \!\!, vychislyayutsya sleva napravo, prichem ih rassmotrenie prekrashchaetsya srazu zhe kak tol'ko sta- novitsya yasno, budet li rezul'tat istinoj ili lozh'yu. uchet etih svojstv ochen' sushchestvenen dlya napisaniya pravil'no rabo- tayushchih programm. Rassmotrim, naprimer, operator cikla v schi- tyvayushchej stroku funkcii GETLINE, kotoruyu my napisali v glave 1. FOR(I=0;I<LIM-1 && (C=GETCHAR()) != '\N' && C != EOF; ++I) S[I]=C; YAsno, chto pered schityvaniem novogo simvola neobhodimo proverit', imeetsya li eshche mesto v massive S, tak chto uslovie I<LIM-1 dolzhno proveryat'sya pervym. I esli eto uslovie ne vy- polnyaetsya, my ne dolzhny schityvat' sleduyushchij simvol. Tak zhe neudachnym bylo by sravnenie 'C' s EOF do obrashche- niya k funkcii GETCHAR : prezhde chem proveryat' simvol, ego nuzhno schitat'. Starshinstvo operacii && vyshe, chem u \!\!, i obe oni mladshe operacij otnosheniya i ravenstva. Poetomu takie vyrazhe- niya, kak I<LIM-1 && (C = GETCHAR()) != '\N' && C != EOF ne nuzhdayutsya v dopolnitel'nyh kruglyh skobkah. No tak kak operaciya != starshe operacii prisvaivaniya, to dlya dostizheniya pravil'nogo rezul'tata v vyrazhenii (C = GETCHAR()) != '\N' kobki neobhodimy. Unarnaya operaciya otricaniya ! Preobrazuet nenulevoj ili istinnyj operand v 0, a nulevoj ili lozhnyj operand v 1. Obychnoe ispol'zovanie operacii ! Zaklyuchaetsya v zapisi IF( ! INWORD ) Vmesto IF( INWORD == 0 ) Trudno skazat', kakaya forma luchshe. Konstrukcii tipa ! INWORD CHitayutsya dovol'no udobno ("esli ne v slove"). No v bolee slozhnyh sluchayah oni mogut okazat'sya trudnymi dlya ponimaniya. Uprazhnenie 2-1 --------------- Napishite operator cikla, ekvivalentnyj privedennomu vyshe operatoru FOR, ne ispol'zuya operacii &&. 2.7. Preobrazovanie tipov Esli v vyrazheniyah vstrechayutsya operandy razlichnyh tipov, to oni preobrazuyutsya k obshchemu tipu v sootvetstvii s nebol'- shim naborom pravil. V obshchem, avtomaticheski proizvodyatsya tol'ko preobrazovaniya, imeyushchie smysl, takie kak, naprimer, preobrazovanie celogo v plavayushchee v vyrazheniyah tipa F+I. Vy- razheniya zhe, lishennye smysla, takie kak ispol'zovanie pere- mennoj tipa FLOAT v kachestve indeksa, zapreshcheny. Vo-pervyh, tipy CHAR i INT mogut svobodno smeshivat'sya v arifmeticheskih vyrazheniyah: kazhdaya peremennaya tipa CHAR avto- maticheski preobrazuetsya v INT. |to obespechivaet znachitel'nuyu gibkost' pri provedenii opredelennyh preobrazovanij simvo- lov. Primerom mozhet sluzhit' funkciya ATOI, kotoraya stavit v sootvetstvie stroke cifr ee chislennyj ekvivalent. ATOI(S) /* CONVERT S TO INTEGER */ CHAR S[]; { INT I, N; N = 0; FOR ( I = 0; S[I]>='0' && S[I]<='9'; ++I) N = 10 * N + S[I] - '0'; RETURN(N); } KAK Uzhe obsuzhdalos' v glave 1, vyrazhenie S[I] - '0' imeet chislennoe znachenie nahodyashchegosya v S[I] simvola, potomu chto znachenie simvolov '0', '1' i t.d. obrazuyut vozrastayushchuyu posledovatel'nost' raspolozhennyh podryad celyh polozhitel'nyh chisel. Drugoj primer preobrazovaniya CHAR v INT daet funkciya LOWER, preobrazuyushchaya dannuyu propisnuyu bukvu v strochnuyu. Esli vystupayushchij v kachestve argumenta simvol ne yavlyaetsya propis- noj bukvoj, to LOWER vozvrashchaet ego neizmennym. Privodimaya nizhe programma spravedliva tol'ko dlya nabora simvolov ASCII. LOWER(C) /* CONVERT C TO LOWER CASE; ASCII ONLY */ INT C; { IF ( C >= 'A' && C <= 'Z' ) RETURN( C + '@' - 'A'); ELSE /*@ Zapisano vmesto 'A' strochnogo*/ RETURN(C); } |ta funkciya pravil'no rabotaet pri kode ASCII, potomu chto chislennye znacheniya, sootvetstvuyushchie v etom kode propisnym i strochnym bukvam, otlichayutsya na postoyannuyu velichinu, a kazhdyj alfavit yavlyaetsya sploshnym - mezhdu a i Z net nichego, krome bukv. |to poslednee zamechanie dlya nabora simvolov EBCDIC sistem IBM 360/370 okazyvaetsya nespravedlivym, v silu chego eta programma na takih sistemah rabotaet nepravil'no - ona preobrazuet ne tol'ko bukvy. Pri preobrazovanii simvol'nyh peremennyh v celye vozni- kaet odin tonkij moment. Delo v tom, chto sam yazyk ne ukazy- vaet, dolzhny li peremennym tipa CHAR sootvetstvovat' chislen- nye znacheniya so znakom ili bez znaka. Mozhet li pri preobra- zovanii CHAR v INT poluchit'sya otricatel'noe celoe? K sozhale- niyu, otvet na etot vopros menyaetsya ot mashiny k mashine, otra- zhaya rashozhdeniya v ih arhitekture. Na nekotoryh mashinah (PDP-11, naprimer) peremennaya tipa CHAR, krajnij levyj bit kotoroj soderzhit 1, preobrazuetsya v otricatel'noe celoe ("znakovoe rasshirenie"). Na drugih mashinah takoe preobrazo- vanie soprovozhdaetsya dobavleniem nulej s levogo kraya, v re- zul'tate chego vsegda poluchaetsya polozhitel'noe chislo. Opredelenie yazyka "C" garantiruet, chto lyuboj simvol iz standartnogo nabora simvolov mashiny nikogda ne dast otrica- tel'nogo chisla, tak chto eti simvoly mozhno svobodno ispol'zo- vat' v vyrazheniyah kak polozhitel'nye velichiny. No proizvol'- nye kombinacii dvoichnyh znakov, hranyashchiesya kak simvol'nye peremennye na nekotoryh mashinah, mogut dat' otricatel'nye znacheniya, a na drugih polozhitel'nye. Naibolee tipichnym primerom vozniknoveniya takoj situacii yavlyaetsya suchaj, kogda znachenie 1 ispol'zuetsya v kachestve EOF. Rassmotrim programmu CHAR C; C = GETCHAR(); IF ( C == EOF ) ... Na mashine, kotoraya ne osushchestvlyaet znakovogo rasshireniya, peremennaya 's' vsegda polozhitel'na, poskol'ku ona opisana kak CHAR, a tak kak EOF otricatel'no, to uslovie nikogda ne vypolnyaetsya. CHtoby izbezhat' takoj situacii, my vsegda pre- dusmotritel'no ispol'zovali INT vmesto CHAR dlya lyuboj pere- mennoj, poluchayushchej znachenie ot GETCHAR. Osnovnaya zhe prichina ispol'zovaniya INT vmesto CHAR ne svyazana s kakim-libo voprosom o vozmozhnom znakovom rasshire- nii. prosto funkciya GETCHAR dolzhna peredavat' vse vozmozhnye simvoly (chtoby ee mozhno bylo ispol'zovat' dlya proizvol'nogo vvoda) i, krome togo, otlichayushcheesya znachenie EOF. Sledova- tel'no znachenie EOF ne mozhet byt' predstavleno kak CHAR, a dolzhno hranit'sya kak INT. Drugoj poleznoj formoj avtomaticheskogo preobrazovaniya tipov yavlyaetsya to, chto vyrazheniya otnosheniya, podobnye I>J, i logicheskie vyrazheniya, svyazannye operaciyami && i \!\!, po op- redeleniyu imeyut znachenie 1, esli oni istinny, i 0, esli oni lozhny. Takim obrazom, prisvaivanie ISDIGIT = C >= '0' && C <= '9'; polagaet ISDIGIT ravnym 1, esli s - cifra, i ravnym 0 v pro- tivnom sluchae. (V proverochnoj chasti operatorov IF, WHILE, FOR i t.d. "Istinno" prosto oznachaet "ne nul'"). Neyavnye arifmeticheskie preobrazovaniya rabotayut v osnov- nom, kak i ozhidaetsya. V obshchih chertah, esli operaciya tipa + ili *, kotoraya svyazyvaet dva operanda (binarnaya operaciya), imeet operandy raznyh tipov, to pered vypolneniem operacii "nizshij" tip preobrazuetsya k "vysshemu" i poluchaetsya rezul'- tat "vysshego" tipa. Bolee tochno, k kazhdoj arifmeticheskoj operacii primenyaetsya sleduyushchaya posledovatel'nost' pravil preobrazovaniya. - Tipy CHAR i SHORT preobrazuyutsya v INT, a FLOAT v DOUBLE. - Zatem, esli odin iz operandov imeet tip DOUBLE, to drugoj preobrazuetsya v DOUBLE, i rezul'tat imeet tip DOUBLE. - V protivnom sluchae, esli odin iz operandov imeet tip LONG, to drugoj preobrazuetsya v LONG, i rezul'tat imeet tip LONG. - V protivnom sluchae, esli odin iz operandov imeet tip UNSIGNED, to drugoj preobrazuetsya v UNSIGNED i rezul'tat imeet tip UNSIGNED. - V protivnom sluchae operandy dolzhny byt' tipa INT, i rezul'tat imeet tip INT. Podcherknem, chto vse peremennye tipa FLOAT v vyrazheniyah pre- obrazuyutsya v DOUBLE; v "C" vsya plavayushchaya arifmetika vypolnya- etsya s dvojnoj tochnost'yu. Preobrazovaniya voznikayut i pri prisvaivaniyah; znachenie pravoj chasti preobrazuetsya k tipu levoj, kotoryj i yavlyaetsya tipom rezul'tata. Simvol'nye peremennye preobrazuyutsya v ce- lye libo so znakovym rasshireniem ,libo bez nego, kak opisano vyshe. Obratnoe preobrazovanie INT v CHAR vedet sebya horosho - lishnie bity vysokogo poryadka prosto otbrasyvayutsya. Takim ob- razom INT I; CHAR C; I = C; C = I; znachenie 's' ne izmenyaetsya. |to verno nezavisimo ot togo, vovlekaetsya li znakovoe rasshirenie ili net. Esli h tipa FLOAT, a I tipa INT, to kak h = I; tak i I = h; privodyat k preobrazovaniyam; pri etom FLOAT preobrazuetsya v INT otbrasyvaniem drobnoj chasti. Tip DOUBLE preobrazuetsya vo FLOAT okrugleniem. Dlinnye celye preobrazuyutsya v bolee ko- rotkie celye i v peremennye tipa CHAR posredstvom otbrasyva- niya lishnih bitov vysokogo poryadka. Tak kak argument funkcii yavlyaetsya vyrazheniem, to pri pe- redache funkciyam argumentov takzhe proishodit preobrazovanie tipov: v chastnosti, CHAR i SHORT stanovyatsya INT, a FLOAT stanovitsya DOUBLE. Imenno poetomu my opisyvali argumenty funkcij kak INT i DOUBLE dazhe togda, kogda obrashchalis' k nim s peremennymi tipa CHAR i FLOAT. Nakonec, v lyubom vyrazhenii mozhet byt' osushchestvleno ("prinuzhdeno") yavnoe preobrazovanie tipa s pomoshch'yu konstruk- cii, nazyvaemoj perevod (CAST). V etoj konstrukcii, imeyushchej vid (imya tipa) vyrazhenie Vyrazhenie preobrazuetsya k ukazannomu tipu po pravilam preobrazovaniya, izlozhennym vyshe. Fakticheski tochnyj smysl operacii perevoda mozhno opisat' sleduyushchim obrazom: vyrazhenie kak by prisvaivaetsya nekotoroj peremennoj ukazannogo tipa, kotoraya zatem ispol'zuetsya vmesto vsej konstrukcii. Napri- mer, bibliotechnaya procedura SQRT ozhidaet argumenta tipa DOUBLE i vydast bessmyslennyj otvet, esli k nej po nebrezh- nosti obratyatsya s chem-nibud' inym. takim obrazom, esli N - celoe, to vyrazhenie SQRT((DOUBLE) N) do peredachi argumenta funkcii SQRT preobrazuet N k tipu DOUBLE. (Otmetim, chto operaciya perevod preobrazuet znachenie N v nadlezhashchij tip; fakticheskoe soderzhanie peremennoj N pri etom ne izmenyaetsya). Operaciya perevoda imraciya perevoda ime- et tot zhe uroven' starshinstva, chto i drugie unarnye opera- cii, kak ukazyvaetsya v tablice v konce etoj glavy. Uprazhnenie 2-2 --------------- Sostav'te programmu dlya funkcii HTOI(S), kotoraya preob- razuet stroku shestnadcaterichnyh cifr v ekvivalentnoe ej ce- loe znachenie. Pri etom dopustimymi ciframi yavlyayutsya cifry ot 1 do 9 i bukvy ot a do F. 2.8. Operacii uvelicheniya i umen'sheniya V yazyke "C" predusmotreny dve neobychnye operacii dlya uvelicheniya i umen'sheniya znachenij peremennyh. Operaciya uveli- cheniya ++ dobavlyaet 1 k svoemu operandu, a operaciya umen'she- niya -- vychitaet 1. My chasto ispol'zovali operaciyu ++ dlya uvelicheniya peremennyh, kak, naprimer, v IF(C == '\N') ++I; Neobychnyj aspekt zaklyuchaetsya v tom, chto ++ i -- mozhno ispol'zovat' libo kak prefiksnye operacii (pered peremennoj, kak v ++N), libo kak postfiksnye (posle peremennoj: N++). |ffekt v oboih sluchayah sostoit v uvelichenii N. No vyrazhenie ++N uvelichivaet peremennuyu N do ispol'zovaniya ee znacheniya, v to vremya kak N++ uvelichivaet peremennuyu N posle togo, kak ee znachenie bylo ispol'zovano. |to oznachaet, chto v kontekste, gde ispol'zuetsya znachenie peremennoj, a ne tol'ko effekt uvelicheniya, ispol'zovanie ++N i N++ privodit k raznym re- zul'tatam. Esli N = 5, to h = N++; ustanavlivaet h ravnym 5, a h = ++N; polagaet h ravnym 6. V oboih sluchayah N stanovitsya ravnym 6. Operacii uvelicheniya i umen'sheniya mozhno primenyat' tol'ko k peremennym; vyrazheniya tipa h=(I+J)++ yavlyayutsya nezakonnymi. V sluchayah, gde nuzhen tol'ko effekt uvelicheniya, a samo znachenie ne ispol'zuetsya, kak, naprimer, v IF ( C == '\N' ) NL++; vybor prefiksnoj ili postfiksnoj operacii yavlyaetsya delom vkusa. no vstrechayutsya situacii, gde nuzhno ispol'zovat' imen- no tu ili druguyu operaciyu. Rassmotrim, naprimer, funkciyu SQUEEZE(S,C), kotoraya udalyaet simvol 's' iz stroki S, kazhdyj raz, kak on vstrechaetsya. SQUEEZE(S,C) /* DELETE ALL C FROM S */ CHAR S[]; INT C; { INT I, J; FOR ( I = J = 0; S[I] != '\0'; I++) IF ( S[I] != C ) S[J++] = S[I]; S[J] = '\0'; } Kazhdyj raz, kak vstechaetsya simvol, otlichnyj ot 's', on kopi- ruetsya v tekushchuyu poziciyu J, i tol'ko posle etogo J uvelichi- vaetsya na 1, chtoby byt' gotovym dlya postupleniya sleduyushchego simvola. |to v tochnosti ekvivalentno zapisi IF ( S[I] != C ) { S[J] = S[I]; J++; } Drugoj primer podobnoj konstrukcii daet funkciya GETLINE, kotoruyu my zaprogrammirovali v glave 1, gde mozhno zamenit' IF ( C == '\N' ) { S[I] = C; ++I; } bolee kompaktnoj zapis'yu IF ( C == '\N' ) S[I++] = C; V kachestve tret'ego primera rassmotrim funkciyu STRCAT(S,T), kotoraya pripisyvaet stroku t v konec stroki S, obrazuya konkatenaciyu strok S i t. Pri etom predpolagaetsya, chto v S dostatochno mesta dlya hraneniya poluchennoj kombinacii. STRCAT(S,T) /* CONCATENATE T TO END OF S */ CHAR S[], T[]; /* S MUST BE BIG ENOUGH */ { INT I, J; I = J = 0; WHILE (S[I] != '\0') / *FIND END OF S */ I++; WHILE((S[I++] = T[J++]) != '\0') /*COPY T*/ ; } Tak kak iz T v S kopiruetsya kazhdyj simvol, to dlya podgotovki k sleduyushchemu prohozhdeniyu cikla postfiksnaya operaciya ++ pri- menyaetsya k obeim peremennym I i J. Uprazhnenie 2-3 --------------- Napishite drugoj variant funkcii SQUEEZE(S1,S2), kotoryj udalyaet iz stroki S1 kazhdyj simvol, sovpadayushchij s kakim-libo simvolom stroki S2. Uprazhnenie 2-4 --------------- Napishite programmu dlya funkcii ANY(S1,S2), kotoraya naho- dit mesto pervogo poyavleniya v stroke S1 kakogo-libo simvola iz stroki S2 i, esli stroka S1 ne soderzhit simvolov stroki S2, vozvrashchaet znachenie -1. 2.9. Pobitovye logicheskie operacii V yazyke predusmotren ryad operacij dlya raboty s bitami; eti operacii nel'zya primenyat' k peremennym tipa FLOAT ili DOUBLE. & Pobitovoe AND \! Pobitovoe vklyuchayushchee OR ^ pobitovoe isklyuchayushchee OR << sdvig vlevo >> sdvig vpravo \^ dopolnenie (unarnaya operaciya) "\" immitiruet vertikal'nuyu chertu. Pobitovaya operaciya AND chasto ispol'zuetsya dlya maskirovaniya nekotorogo mnozhestva bitov; naprimer, operator C = N & 0177 peredaet v 's' sem' mladshih bitov N , polagaya ostal'nye rav- nymi nulyu. Operaciya 'e' pobitovogo OR ispol'zuetsya dlya vklyu- cheniya bitov: C = X e MASK ustanavlivaet na edinicu te bity v h , kotorye ravny edinice v MASK. Sleduet byt' vnimatel'nym i otlichat' pobitovye operacii & i 'e' ot logicheskih svyazok && i \!\! , Kotorye podrazume- vayut vychislenie znacheniya istinnosti sleva napravo. Naprimer, esli h=1, a Y=2, to znachenie h&Y ravno nulyu , v to vremya kak znachenie X&&Y ravno edinice./pochemu?/ Operacii sdviga << i >> osushchestvlyayut sootvetstvenno sdvig vlevo i vpravo svoego levogo operanda na chislo bitovyh pozicij, zadavaemyh pravym operandom. Takim obrazom , h<<2 sdvigaet h vlevo na dve pozicii, zapolnyaya osvobozhdayushchiesya bity nulyami, chto ekvivalentno umnozheniyu na 4. Sdvig vpravo velichiny bez znaka zapolnyaet osvobozhdayushchiesya bity na nekoto- ryh mashinah, takih kak PDP-11, zapolnyayutsya soderzhaniem zna- kovogo bita /"arifmeticheskij sdvig"/, a na drugih - nulem /"logicheskij sdvig"/. Unarnaya operaciya \^ daet dopolnenie k celomu; eto ozna- chaet , chto kazhdyj bit so znacheniem 1 poluchaet znachenie 0 i naoborot. |ta operaciya obychno okazyvaetsya poleznoj v vyrazhe- niyah tipa X & \^077 gde poslednie shest' bitov h maskiruyutsya nulem. Podcherknem, chto vyrazhenie X&\^077 ne zavisit ot dliny slova i poetomu predpochtitel'nee, chem, naprimer, X&0177700, gde predpolaga- etsya, chto h zanimaet 16 bitov. Takaya perenosimaya forma ne trebuet nikakih dopolnitel'nyh zatrat, poskol'ku \^077 yavlya- etsya konstantnym vyrazheniem i, sledovatel'no, obrabatyvaetsya vo vremya kompilyacii. CHtoby proillyustrirovat' ispol'zovanie nekotoryh operacij s bitami, rassmotrim funkciyu GETBITS(X,P,N), kotoraya vozvra- shchaet /sdvinutymi k pravomu krayu/ nachinayushchiesya s pozicii r pole peremennoj h dlinoj N bitov. my predpolagaem , chto krajnij pravyj bit imeet nomer 0, i chto N i r - razumno za- dannye polozhitel'nye chisla. naprimer, GETBITS(h,4,3) vozvra- shchaet sdvinutymi k pravomu krayu bity, zanimayushchie pozicii 4,3 i 2. GETBITS(X,P,N) /* GET N BITS FROM POSITION P */ UNSIGNED X, P, N; { RETURN((X >> (P+1-N)) & \^(\^0 << N)); } Operaciya X >> (P+1-N) sdvigaet zhelaemoe pole v pravyj konec slova. Opisanie argumenta X kak UNSIGNED garantiruet, chto pri sdvige vpravo osvobozhdayushchiesya bity budut zapolnyat'sya nu- lyami, a ne soderzhimym znakovogo bita, nezavisimo ot togo, na kakoj mashine propuskaetsya programma. Vse bity konstantnogo vyrazheniya \^0 ravny 1; sdvig ego na N pozicij vlevo s po- moshch'yu operacii \^0<<N sozdaet masku s nulyami v N krajnih pravyh bitah i edinicami v ostal'nyh; dopolnenie \^ sozdaet masku s edinicami v N krajnih pravyh bitah. Uprazhnenie 2-5 --------------- Peredelajte GETBITS takim obrazom, chtoby bity otschityva- lis' sleva napravo. Uprazhnenie 2-6 --------------- Napishite programmu dlya funkcii WORDLENGTH(), vychislyayushchej dlinu slova ispol'zuemoj mashiny, t.e. CHislo bitov v peremen- noj tipa INT. Funkciya dolzhna byt' perenosimoj, t.e. Odna i ta zhe ishodnaya programma dolzhna pravil'no rabotat' na lyuboj mashine. Uprazhnenie 2-7 --------------- Napishite programmu dlya funkcii RIGHTROT(N,B), sdvigayushchej ciklicheski celoe N vpravo na B bitovyh pozicij. Uprazhnenie 2-8 --------------- Napishite programmu dlya funkcii INVERT(X,P,N), kotoraya invertiruet (t.e. Zamenyaet 1 na 0 i naoborot) N bitov X, na- chinayushchihsya s pozicii P, ostavlyaya drugie bity neizmenennymi. 2.10. Operacii i vyrazheniya prisvaivaniya Takie vyrazheniya, kak I = I + 2 v kotoryh levaya chast' povtoryaetsya v pravoj chasti mogut byt' zapisany v szhatoj forme I += 2 ispol'zuya operaciyu prisvaivaniya vida +=. Bol'shinstvu binarnyh operacij (operacij podobnyh +, ko- torye imeyut levyj i pravyj operand) sootvetstvuet operaciya prisvaivaniya vida op=, gde op - odna iz operacij + - * / % << >> & \^ \! Esli e1 i e2 - vyrazheniya, to e1 op= e2 ekvivalentno e1 = (e1) op (e2) za isklyucheniem togo, chto vyrazhenie e1 vychislyaetsya tol'ko odin raz. Obratite vnimanie na kruglye skobki vokrug e2: X *= Y + 1 to X = X * (Y + 1) ne X = X * Y + 1 V kachestve primera privedem funkciyu BITCOUNT, kotoraya podschityvaet chislo ravnyh 1 bitov u celogo argumenta. BITCOUNT(N) /* COUNT 1 BITS IN N */ UNSIGNED N; ( INT B; FOR (B = 0; N != 0; N >>= 1) IF (N & 01) B++; RETURN(B); ) Ne govorya uzhe o kratkosti, takie operatory privaivaniya imeyut to preimushchestvo, chto oni luchshe sootvetstvuyut obrazu chelovecheskogo myshleniya. My govorim: "pribavit' 2 k I" ili "uvelichit' I na 2", no ne "vzyat' I, pribavit' 2 i pomestit' rezul'tat opyat' v I". Itak, I += 2. Krome togo, v gromozdkih vyrazheniyah, podobnyh YYVAL[YYPV[P3+P4] + YYPV[P1+P2]] += 2 Takaya operaciya prisvaivaniya oblegchaet ponimanie programmy, tak kak chitatel' ne dolzhen skrupulezno proveryat', yavlyayutsya li dva dlinnyh vyrazheniya dejstvitel'no odinakovymi, ili za- dumyvat'sya, pochemu oni ne sovpadayut. Takaya operaciya prisvai- vaniya mozhet dazhe pomoch' kompilyatoru poluchit' bolee effektiv- nuyu programmu. My uzhe ispol'zovali tot fakt, chto operaciya prisvaivaniya imeet nekotoroe znachenie i mozhet vhodit' v vyrazheniya; samyj tipichnyj primer WHILE ((C = GETCHAR()) != EOF) prisvaivaniya, ispol'zuyushchie drugie operacii prisvaivaniya (+=, -= i t.d.) takzhe mogut vhodit' v vyrazheniya, hotya eto slucha- etsya rezhe. Tipom vyrazheniya prisvaivaniya yavlyaetsya tip ego levogo operanda. Uprazhnenie 2-9 --------------- V dvoichnoj sisteme schisleniya operaciya X&(X-1) obnulyaet samyj pravyj ravnyj 1 bit peremennoj X.(pochemu?) ispol'zujte eto zamechanie dlya napisaniya bolee bystroj versii funkcii BITCOUNT. 2.11. Uslovnye vyrazheniya Operatory IF (A > B) Z = A; ELSE Z = B; konechno vychislyayut v Z maksimum iz a i v. Uslovnoe vyrazhenie, zapisannoe s pomoshch'yu ternarnoj operacii "?:", predostavlyaet druguyu vozmozhnost' dlya zapisi etoj i analogichnyh konstruk- cij. V vyrazhenii e1 ? E2 : e3 snachala vychislyaetsya vyrazhenie e1. Esli ono otlichno ot nulya (istinno), to vychislyaetsya vyrazhenie e2, kotoroe i stanovitsya znacheniem uslovnogo vyrazheniya. V protivnom sluchae vychislyaet- sya e3, i ono stanovitsya znacheniem uslovnogo vyrazheniya. Kazh- dyj raz vychislyaetsya tol'ko odno iz vyrazheniya e2 i e3. Takim obrazom, chtoby polozhit' Z ravnym maksimumu iz a i v, mozhno napisat' Z = (A > B) ? A : B; /* Z = MAX(A,B) */ Sleduet podcherknut', chto uslovnoe vyrazhenie dejstvitel'- no yavlyaetsya vyrazheniem i mozhet ispol'zovat'sya tochno tak zhe, kak lyuboe drugoe vyrazhenie. Esli e2 i e3 imeyut raznye tipy, to tip rezul'tata opredelyaetsya po pravilam preobrazovaniya, rassmotrennym ranee v etoj glave. naprimer, esli F imeet tip FLOAT, a N - tip INT, to vyrazhenie (N > 0) ? F : N Imeet tip DOUBLE nezavisimo ot togo, polozhitel'no li N ili net. Tak kak uroven' starshinstva operacii ?: ochen' nizok, pryamo nad prisvaivaniem, to pervoe vyrazhenie v uslovnom vy- razhenii mozhno ne zaklyuchat' v kruglye skobki. Odnako, my vse zhe rekomenduem eto delat', tak kak skobki delayut uslovnuyu chast' vyrazheniya bolee zametnoj. Ispol'zovanie uslovnyh vyrazhenij chasto privodit k korot- kim programmam. Naprimer, sleduyushchij nizhe operator cikla pe- chataet N elementov massiva, po 10 v stroke, razdelyaya kazhdyj stolbec odnim probelom i zakanchivaya kazhduyu stroku (vklyuchaya poslednyuyu) odnim simvolom perevoda stroki. OR (I = 0; I < N; I++) PRINTF("%6D%C",A[I],(I%10==9 \!\! I==N-1) ? '\N' : ' ') Simvol perevoda stroki zapisyvaetsya posle kazhdogo desyatogo elementa i posle N-go elementa. Za vsemi ostal'nymi elemen- tami sleduet odin probel. Hotya, vozmozhno, eto vyglyadit mud- renym, bylo by pouchitel'nym popytat'sya zapisat' eto, ne is- pol'zuya uslovnogo vyrazheniya. Uprazhnenie 2-10 --------------- Perepishite programmu dlya funkcii LOWER, kotoraya perevodit propisnye bukvy v strochnye, ispol'zuya vmesto konstrukcii IF-ELSE uslovnoe vyrazhenie. 2.12. Starshinstvo i poryadok vychisleniya V privodimoj nizhe tablice svedeny pravila starshinstva i as- sociativnosti vseh operacij, vklyuchaya i te, kotorye my eshche ne obsuzhdali. Operacii, raspolozhennye v odnoj stroke, imeyut odin i tot zhe uroven' starshinstva; stroki raspolozheny v po- ryadke ubyvaniya starshinstva. Tak, naprimer, operacii *, / i % imeyut odinakovyj uroven' starshinstva, kotoryj vyshe, chem uro- ven' operacij + i -. OPERATOR ASSOCIATIVITY () [] -> . LEFT TO RIGHT ! \^ ++ -- - (TYPE) * & SIZEOF RIGHT TO LEFT * / % LEFT TO RIGHT + - LEFT TO RIGHT << >> LEFT TO RIGHT < <= > >= LEFT TO RIGHT == != LEFT TO RIGHT & LEFT TO RIGHT ^ LEFT TO RIGHT \! LEFT TO RIGHT && LEFT TO RIGHT \!\! LEFT TO RIGHT ?: RIGHT TO LEFT = += -= ETC. RIGHT TO LEFT , (CHAPTER 3) LEFT TO RIGHT Operacii -> i . Ispol'zuyutsya dlya dostupa k elementam struk- tur; oni budut opisany v glave 6 vmeste s SIZEOF (razmer ob®ekta). V glave 5 obsuzhdayutsya operacii * (kosvennaya adre- saciya) i & (adres). Otmetim, chto uroven' starshinstva pobitovyh logicheskih opera- cij &, ^ i e nizhe urovnya operacij == i !=. |to privodit k tomu, chto osushchestvlyayushchie pobitovuyu proverku vyrazheniya, po- dobnye IF ((X & MASK) == 0) ... Dlya polucheniya pravil'nyh rezul'tatov dolzhny zaklyuchat'sya v kruglye skobki. K