Andrej Bogatyrev. Rukovodstvo polnogo idiota
po programmirovaniyu (na yazyke Si) © Copyright Andrej Bogatyrev Email: abs@opentech.olvit.ru ¡ mailto:abs@opentech.olvit.ru --------------------------------------------------------------- PEREMENNYE Peremennaya - eto takoj "yashchichek" s imenem, v kotorom mozhet hranit'sya nekoe ZNACHENIE. Imya u peremennoj postoyanno i neizmenno, znachenie zhe mozhet menyat'sya. Naprimer, pust' u nas est' peremennaya s imenem "x". ----- / x / --------------- | Tekushchee | | znachenie, | | naprimer 12 | --------------- Peremennuyu mozhno izmenyat' pri pomoshchi operacii PRISVAIVANIYA. V yazyke Si ona oboznachaetsya znakom ravenstva. x = 12 ; |to chitaetsya ne kak "iks ravno 12", a kak "prisvoit' peremennoj iks znachenie 12", to est' "Polozhit' v yashchik s nadpis'yu IKS chislo 12". Takaya stroka yavlyaetsya prostejshim OPERATOROM, to est' DEJSTVIEM. V konce operatorov stavitsya tochka s zapyatoj. Rassmotrim operator x = x + 3; |to ne uravnenie. Esli rassmatrivat' etu stroku kak matematicheskoe uravnenie, ono ne imeet reshenij. Na samom dele tut napisano: 1) "vzyat' znachenie peremennoj IKS" 2) "pribavit' k nemu 3" 3) "polozhit' novoe znachenie v peremennuyu IKS", sterev v nej prezhnee znachenie. U opreatora prisvaivaniya est' dve chasti: LEVAYA i PRAVAYA. LEVAYA_CHASTX = PRAVAYA_CHASTX ; V levoj chasti obychno stoit prosto imya peremennoj V KOTORUYU zapisyvaetsya vychislennyj sprava rezul'tat. Esli imya peremennoj vstrechaetsya v PRAVOJ chasti, to eto oznachaet "podstavit' syuda tekushchee znachenie etoj peremennoj". Pri etom tekushchee znachenie samoj peremennoj NE izmenyaetsya, beretsya ego kopiya. To est', "vynutoe iz yashchika znachenie" ne ostavlyaet yashchik pustym! Vynimaetsya kopiya, drugaya kopiya ostaetsya v yashchike. Itak: x = x + 3 ; Pust' sejchas znachenie x est' 12 Snachala vychislyaetsya PRAVAYA chast' operatora prisvaivaniya. x + 3 ----- / x / --------------- | 12 | | --------|------ | | | VZYATX kopiyu znacheniya (to est' chislo 12) iz yashchika s imenem "IKS" | V Vmesto x podstavlyaem chislo 12 + 3 ----> SKLADYVAEM. Slozhenie daet 15. V etot moment x vse eshche ravno 12 (v yashchike lezhit chislo 12) Teper' vychislyaetsya samo prisvaivanie: x = 15 ; | | | POLOZHITX rezul'tat v yashchik s imenem "IKS" | (istrebiv v nem prezhnee znachenie, esli bylo) -----| / x / | --------|------ | 12 V | --------------- Stalo: ----- / x / --------------- | 15 | --------------- V peremennoj s tem zhe imenem teper' nahoditsya novoe znachenie, ona izmenilas'. Potomu i "peremennaya". V nekotoryh yazykah programmirovaniya, naprimer v Pascal ili Modula, operaciya prisvaivaniya oboznachaetsya simvolom := a ne = |to umen'shaet putanicu, no k smyslu = mozhno privyknut' dovol'no bystro. Ne ogorchajtes'. V pravoj chasti znachenie peremennoj mozhet ispol'zovat'sya neskol'ko raz: z = x * x + 2 * x; Tut est' dve peremennye: z - dlya rezul'tata. x - uzhe imeyushchaya kakoe-to znachenie. x * x oznachaet "umnozhit' iks na iks" (pri etom samo znachenie, lezhashchee v yashchike iks ne izmenyaetsya!) x * 2 oznachaet "vzyat' dva znacheniya iks" + oznachaet slozhenie. Peremennye nado OB¬YAVLYATX. |to neobhodimo potomu, chto inache, esli by peremennye vvodilis' prosto ispol'zovaniem imeni peremennoj, i my vdrug dopustili by OPECHATKU, naprimer: idneks = 1; vmesto indeks = 1; to u nas poyavilas' by "lishnyaya" peremennaya "idneks", a ozhidaemoe dejstvie ne proizoshlo by. Takuyu oshibku najti cherezvychajno tyazhelo. Esli zhe peremennye nado ob®yavlyat', to neob®yavlennye peremennye budut vyyavleny eshche na stadii kompilyacii programmy. Peremennye, kotorye budut hranit' celye chisla ( ..., -2, -1, 0, 1, 2, 3, ...), ob®yavlyayut tak: int peremennaya1; int peremennaya2; Ili srazu neskol'ko v odnoj stroke: int peremennaya1, peremennaya2; int oznachaet sokrashchenie ot slova integer - "celyj". PROGRAMMA Programma sostoit iz OPERATOROV, to est' dejstvij. Operatory vypolnyayutsya posledovatel'no v tom poryadke, v kotorom oni zapisany. /* OB¬YAVLYAEM DVE PEREMENNYE */ int x, y; /* 0 */ /* |to eshche ne operatory, hotya pri etom sozdayutsya 2 yashchika dlya celyh chisel */ /* A TEPERX - OPERATORY. */ /* My nachnem s prostyh operatorov prisvaivaniya i arifmetiki */ x = 3; /* 1 */ y = 4; /* 2 */ x = x + y; /* 3 */ y = y - 1; /* 4 */ x = y; /* 5 */ Znacheniya peremennyh (to, chto lezhit v yashchikah) menyayutsya takim obrazom: x y /* 0 */ musor musor /* posle 1 */ 3 musor /* posle 2 */ 3 4 /* posle 3 */ 7 4 /* posle 4 */ 7 3 /* posle 5 */ 3 3 Kak vy vidite, peremennye, kotorye ne uchastvuyut v levoj chasti operatora prisvaivaniya, etim operatorom NE MENYAYUTSYA. Poslednyaya operaciya x = y; NE delaet imena x i y sinonimami. Takoj veshchi, kak "pereveshivanie tablichek s imenami s yashchika na yashchik" ne proishodit. Vmesto etogo, dva yashchika s imenami x i y soderzhat odinakovye znacheniya, to est' dve kopii odnogo i togo zhe chisla. ----- ----- / x / / y / --------------- --------------- | 3 *<--|--------<----|-- 3 | --------------- 1) --------------- 2), 3) 4) 1) Iz yashchika y beretsya KOPIYA chisla 3 (bezymyannoe znachenie). 2) Staroe soderzhimoe yashchika x unichtozhaetsya. 3) CHislo 3 kladetsya v yashchik x. 4) V ishodnom yashchike y poprezhnemu ostalos' 3. Znachenie celoj peremennoj mozhno vyvesti na ekran operatorom pechati: printf("%d\n", x); Poka budem rassmatrivat' ego kak "magicheskij". Nad celymi chislami mozhno proizvodit' takie arifmeticheskie operacii: x + y slozhenie x - y vychitanie x * y umnozhenie x / y delenie nacelo (to est' s ostatkom; rezul'tat - celoe) x % y vychislit' ostatok ot deleniya nacelo 5 / 2 dast 2 5 % 2 dast 1 V operatorah prisvaivaniya ispol'zuyutsya takie sokrashcheniya: DLINNAYA ZAPISX SMYSL SOKRASHCHAETSYA DO x = x + 1; "uvelichit' na 1" x++; (ili ++x; ) x = x - 1; "umen'shit' na 1" x--; (ili --x; ) x = x + y; "pribavit' y" x += y; x = x * y; "umnozhit' na y" x *= y; x = x / y; "podelit' na y" x /= y; V tom chisle x++; mozhno zapisat' kak x += 1;  * STRUKTURY UPRAVLENIYA *  Obychno operatory vypolnyayutsya posledovatel'no, v tom poryadke, v kotorom oni zapisany v programme. operator1; | operator2; | operator3; | operator4; V USLOVNYJ OPERATOR if(uslovie) operator; ...prodolzhenie... Rabotaet tak: Vychislyaetsya uslovie. Esli ono istinno, to vypolnyaetsya operator, zatem vypolnyaetsya prodolzhenie. Esli ono lozhno, to srazu vypolnyaetsya prodolzhenie, a operator ne vypolnyaetsya. Esli nam nado vypolnit' pri istinnosti usloviya neskol'ko operatorov, my dolzhny zaklyuchit' ih v skobki { ... } - eto tak nazyvaemyj "sostavnoj operator". if(uslovie) { operator1; operator2; ... } prodolzhenie Posle } tochka s zapyatoj NE STAVITSYA (mozhno i postavit', no ne nuzhno). Uslovnyj operator izobrazhayut na shemah tak: | | | ---------------- ---| ESLI uslovie |---- | ---------------- | | | V V istinno lozhno | | V | ------------ | | operator | | ------------ | | | ------->-------<------- | | V prodolzhenie | Imeetsya vtoraya forma, s chast'yu "inache": if(uslovie) operator_esli_istinno; else operator_esli_lozhno; "ili to, ili drugoe" (no ne oba srazu) | | | ---------------- ---| ESLI uslovie |----------- | ---------------- | | | V V istinno lozhno | | V | ------------------------- ----------------------- | operator_esli_istinno | | operator_esli_lozhno | ------------------------- ----------------------- | | ------->-------<-------------- | | V prodolzhenie | Primer1: if(x > 10) printf("Iks bol'she desyati\n"); Primer2: int x, y, z; if(x < y) z = 1; else z = 2; Usloviya: V kachestve uslovij mogut ispol'zovat'sya operatory SRAVNENIYA (sravnivat' mozhno peremennye, vyrazheniya, konstanty) x < y men'she x > y bol'she x <= y men'she ili ravno x >= y bol'she ili ravno x == y ravno x != y ne ravno Vse eti operatory v kachestve rezul'tata operacii sravneniya vydayut 1, esli sravnenie istinno 0, esli ono lozhno. Takim obrazom, na samom dele uslovnyj operator rabotaet tak: if(uslovie) .... Esli uslovie est' NOLX - to uslovie schitaetsya lozhnym. Esli uslovie est' NE NOLX a ... -2, -1, 1, 2, 3, ... - to uslovie istinno. |to opredelenie. Iz nego v chastnosti vytekaet, chto sravnenie s celym nulem mozhno opuskat': if(x != 0) ... ; sokrashchaetsya do if(x) ... ; if(x == 0) ... ; sokrashchaetsya do if(!x) ... ; ------------------------------------------------------------------------- Primer: int x, y, z; if(x == 1){ y = 2; z = x + y; } else { y = 1; z = x - y; } ------------------------------------------------------------------------- Primer so vlozhennymi uslovnymi operatorami: if(x == 1){ printf("Iks raven 1\n"); if(y == 2){ printf("Igrek raven 2\n"); } } else { printf("Iks ne raven 1\n"); } ------------------------------------------------------------------------- CHasto primenyaetsya posledovatel'nost' uslovnyh operatorov, perebirayushchaya razlichnye varianty: if(x == 1) printf("Iks raven 1\n"); else if(x == 2) printf("Iks raven 2\n"); else if(x == 3){ printf("Iks raven 3\n"); y = 1; } else printf("Nepredusmotrennoe znachenie iks\n"); ------------------------------------------------------------------------- Samoe slozhnoe - privyknut' k tomu, chto sravnenie oboznachaetsya znakom ==, a ne = Znak = oznachaet "prisvoit' znachenie", a ne "sravnit' na ravenstvo". CIKL while ("do teh por, poka istinno") while(uslovie) operator; ...prodolzhenie... ili while(uslovie){ operatory; ... } ...prodolzhenie... | V | +------>--+ | | | V P | --------------------- o | | proverit' USLOVIE |-------> esli lozhno (nul') v A --------------------- | t | | | o | V | r | esli istinno (ne nul') | i | | | t | V | ' | operator V | | | | | | +-----<---+ | | +-------<---------------------+ | V prodolzhenie Primer: int x; x = 10; while(x > 0){ printf("x=%d\n", x); x = x - 1; } printf("Konec.\n"); printf("x stalo ravno %d.\n", x); /* pechataet 0 */ "Cikl" on potomu, chto ego telo povtoryaetsya neskol'ko raz. CHtoby cikl okonchilsya, operator-telo cikla dolzhen menyat' kakuyu-to peremennuyu, ot kotoroj zavisit istinnost' usloviya povtorenij. OPERATORY "I, ILI, NE" Usloviya mogut byt' slozhnymi. ESLI krasnyj I ves < 10 TO ...; ESLI krasnyj ILI sinij TO ...; ESLI NE krasnyj TO ...; Na yazyke Si takie usloviya zapisyvayutsya tak: if(uslovie1 && uslovie2) ...; /* "I" */ if(uslovie1 || uslovie2) ...; /* "ILI" */ if(! uslovie1) ...; /* "NE" */ Naprimer: if(4 < x && x <= 12) ...; Bylo by nepravil'no zapisat' if(4 < x <= 12) ...; ibo yazyk programmirovaniya Si NE PONIMAET dvojnoe sravnenie! Eshche primery: if(x < 3 || y > 4) ...; if( ! (x < 3 || y > 4)) ...; CIKL for ("dlya kazhdogo") |tot cikl yavlyaetsya prosto inoj zapis'yu odnogo iz variantov cikla while. On sluzhit obychno dlya vypolneniya operedelennogo dejstviya neskol'ko raz, ne "poka istinno uslovie", a "vypolnit' N-raz". U takogo cikla est' "peremennaya cikla" ili "schetchik povtorenij". int i; i = a; /* nachal'naya inicializaciya */ while(i < b){ telo_cikla; i += c; /* uvelichenie schetchika */ } ...prodolzhenie... perepisyvaetsya v vide int i; for(i=a; i < b; i += c) telo_cikla; telo_cikla budet vypolneno dlya znachenij i a a+c a+c+c ... poka i < b V prostejshem sluchae for(i=1; i <= N; i++) printf("i=%d\n", i); i oznachaet "nomer povtoreniya". Takoj cikl sluzhit dlya povtoreniya SHOZHIH dejstvij NESKOLXKO raz s raznym znacheniem parametra. OPERATOR break ("vyvalit'sya iz cikla") Operator break zastavlyaet prervat' vypolnenie tela cikla i srazu perejti k prodolzheniyu programmy. while(uslovie1){ operatory1; if(uslovie2) break; ------->----+ | operatory2; | } | ...prodolzhenie...<--------<---------+ i for(i=0; uslovie1; i++){ operatory1; if(uslovie2) break; ------->----+ | operatory2; | } | ...prodolzhenie...<--------<---------+ |tot operator pozvolyaet organizovyvat' dopolnitel'nye tochki vyhoda iz cikla (pri dopolnitel'nyh usloviyah). Primer: for(i=0; i < 20; i++){ printf("i=%d\n", i); if(i == 7){ printf("break loop!\n"); break; /* vyvalit'sya iz cikla */ } printf("more\n"); } printf("finished, i=%d\n", i); /* pechataet 7 */ V chastnosti, s ego pomoshch'yu mozhno organizovyvat' beskonechnyj cikl: for(;;){ /* zagolovok beskonechnogo cikla */ operatory1; if(uslovie2) break; ------->----+ | operatory2; | } | ...prodolzhenie...<--------<---------+ Zdes' v samom zagolovke cikla NE PROVERYAETSYA NIKAKIH USLOVIJ, takoj cikl prodolzhaetsya beskonechno. Uslovie prodolzheniya schitaetsya vsegda istinnym. Edinstvennyj sposob vyjti iz nego - eto sdelat' break (pri kakom-to uslovii) v tele cikla, chto i napisano. Beskonechnyj cikl mozhno takzhe organizovat' pri pomoshchi while(1){ ... } OPERATOR VYVODA (PECHATI) printf("tekst"); Pechataet na ekran tekst. printf("tekst\n"); Pechataet na ekran tekst i perehodit k novoj stroke. printf("slovo1 slovo2 "); printf("slovo3\n"); pechataet slovo1 slovo2 slovo3 i perehodit na novuyu stroku. Esli perehod na novuyu stroku ne zadan yavno, simvolom \n, to tekst prodolzhaet pechatat'sya v tekushchej stroke. printf("%d", x); Pechataet v tekstovom vide ZNACHENIE peremennoj x. Special'naya konstrukciya %d oznachaet "vzyat' peremennuyu iz spiska posle zapyatoj i napechatat' ee znachenie v ivde celogo chisla". printf("iks raven %d - ogo-go\n", x); Pechataet snachala tekst iks raven zatem znachenie peremennoj x kak celoe chislo, zatem tekst - ogo-go i perehodit na novuyu stroku (poskol'ku ukazan simvol \n). |tot operator mozhet pechatat' i neskol'ko znachenij peremennyh: int x, y; x = 12; y = 15; printf("iks est' %d, igrek est' %d, vse.\n", x, y); ~~~~~~ Dannyj operator rabotaet tak. Stroka "iks est' %d, igrek est' %d\n" nazyvaetsya FORMATOM. Komp'yuter chitaet format sleva napravo i pechataet tekst do teh por, poka ne vstretit simvol %d. Kursor izobrazhen simvolom _ iks est' _ Dalee on beret PERVUYU peremennuyu iz spiska ~~~~ i pechataet ee kak celoe chislo. iks est' 12_ dalee on snova pechataet tekst poka ne vstretit %d iks est' 12, igrek est' _ Teper' on beret VTORUYU peremennuyu iz spiska i pechataet ee: iks est' 12, igrek est' 15_ Snova pechataet tekst, vklyuchaya perevod stroki \n. Kak tol'ko stroka formata konchilas', operator printf zavershen. iks est' 12, igrek est' 15, vse. _ Pechatat' mozhno ne tol'ko znacheniya peremennyh, no i znacheniya arifmeticheskih vyrazhenij: printf("ravno: %d\n", 12 + 3 * 5); Kontrol'nyj vopros, chto pechataetsya: int x, y, z; x = 13; y = 23; z = 34; printf("x=%d xx=%d\nzzz=%d\n", x, y - 1, z * 2 + 1); Tut v formate est' DVA perevoda stroki, poetomu budet napechatano: x=13 xx=22 zzz=69 _ Zamet'te, chto pered tem kak byt' napechatannymi, vyrazheniya v spiske posle formata VYCHISLYAYUTSYA. CHto napechataet printf("x=%d\n y=%d\n", x, y); x=13 y=23 _ Probel pered y voznik potomu, chto on SODERZHITSYA v stroke formata posle simvola \n !!! Bud'te vnimatel'ny. FUNKCII Funkciej nazyvaetsya fragment programmy, v kotoryj peredayutsya PARAMETRY, i kotoryj VOZVRASHCHAET znachenie (ili nichego). Prelest' funkcii v tom, chto ee mozhno vypolnit' mnogo raz iz raznyh tochek programmy. Funkciya sostoit iz OB¬YAVLENIYA - opisaniya togo, kak ona chto-to vychislyaet Ob®yavlenie byvaet rovno odno. VYZOVOV - s konkretnymi znacheniyami parametrov, chto imenno ona dolzhna na etot raz vychislit'. Vyzovov mozhet byt' skol'ko ugodno. Ob®yavlenie prostejshej funkcii vyglyadit tak: int func(int x){ /* Odin ili neskol'ko operatorov, zavershayushchihsya operatorom return(nechto); */ return x+1; } ------------------------------------------------------------------------- int func(... zadaet funkciyu s imenem func (imya vydumyvaet programmist, kak i imena peremennyh). int oznachaet, chto funkciya vozvrashchaet celoe znachenie. ------------------------------------------------------------------------- ...(int x)... zadaet spisok argumentov (ili parametrov) funkcii. ------------------------------------------------------------------------- ...){ ... } zadaet telo funkcii - nekuyu posledovatel'nost' ob®yavlenij peremennyh i operatorov. ------------------------------------------------------------------------- return vyrazhenie; zadaet operator vyhoda iz funkcii v tochku ee vyzova s vozvratom znacheniya vyrazheniya. ------------------------------------------------------------------------- Pokazhem prostoj primer VYZOVA etoj funkcii: int y; ... y = func(5); /* a */ ...prodolzhenie... /* b */ |tot fragment rabotaet sleduyushchim obrazom: y = func(5); V etoj tochke my 1) "zapisyvaem na bumazhke", chto vyzov proizoshel v takoj-to stroke, takom-to meste nashej programmy. 2) Smotrim na OPREDELENIE funkcii func. int func(int x){... My vyzvali funkciyu kak func(5). |to znachit, chto v tele funkcii x poluchaet nachal'noe znachenie 5. To est' DLYA DANNOGO VYZOVA nasha funkciya (ee telo) prevrashchaetsya v int x; x = 5; return x+1; 3) x+1 est' 6. Dalee dolzhen vypolnit'sya operator return. On vypolnyaetsya tak: My "chitaem s bumazhki" - otkuda byla vyzvana funkciya func, i smotrim na eto mesto. |to bylo y = func(5); Vycherkivaem func(5) i zamenyaem ego ZNACHENIEM vyrazheniya, vychislennogo v operatore return; y = 6; 4) Vypolnyaem etot operator i perehodim k prodolzheniyu. ------------------------------------------------------------------------- int y, z, w; y = func(5); z = func(6); w = func(7) + func(8) + 1; Prevratitsya v y = 6; z = 7; w = 8 + 9 + 1; Pri etom my chetyre raza "prygnem" na opredelenie funkcii func(), projdem vse ee operatory s raznymi znacheniyami parametra x i vernemsya obratno v tochku vyzova. PROGRAMMA V CELOM Programma v celom sostoit iz funkcij. Odna iz funkcij dolzhna imet' imya main(), S FUNKCII main NACHINAETSYA VYPOLNENIE PROGRAMMY. (na samom dele etomu predshestvuet otvedenie i inicializaciya global'nyh peremennyh; smotri posleduyushchie lekcii). CHasto main() - edinstvennaya funkciya v programme. ------------------------------------------------------------------------- Struktura programmy takova: #include <stdio.h> /* magicheskaya stroka */ /* GLOBALXNYE PEREMENNYE (o nih pozzhe) */ int a = 7; int b; /* po umolchaniyu 0 */ /* FUNKCII */ f1(){....} f2(){....} /* NACHALXNAYA (GLAVNAYA) FUNKCIYA */ void main(){ ... } ------------------------------------------------------------------------- Primer programmy: #include <stdio.h> int f1(int x, int y){ return (x + y*2); } int f2(int x){ int z; z = x+7; return 2*z; } void main(){ /* Ob®yavleniya peremennyh */ int a, b, c; /* Operatory */ a = 5; b = 6; c = f1(a, b+3); b = f1(1, 2); a = f2(c); printf("A est' %d B est' %d C est' %d\n", a, b, c); } Ona pechataet: A est' 60 B est' 5 C est' 23 KAK NE NADO PROGRAMMIROVATX CIKLY int i; for(i=0; i < 4; i++){ if(i == 0) func0(); else if(i == 1) func1(); else if(i == 2) func2(); else if(i == 3) func3(); } V dannom primere cikl ABSOLYUTNO NE NUZHEN. To, chto tut delaetsya, est' prosto POSLEDOVATELXNOSTX operatorov: func0(); func1(); func2(); func3(); Cikl imeet smysl lish' togda, kogda mnogo raz vyzyvaetsya ODNO I TO ZHE dejstvie, no mozhet byt' zavisyashchee ot parametra, vrode func(i). No ne raznye funkcii dlya raznyh i. Analogichno, rassmotrim takoj primer: int i; for(i=0; i < 10; i++){ if(i==0) func0(); else if(i == 1) func1(); else if(i == 2) func2(); else funcN(i); } Tut funcN(i) beret na sebya rol' "a v ostal'nyh sluchayah". Odnako, etot primer bolee estestvenno mozhet byt' zapisan tak: int i; func0(); func1(); func2(); for(i = 3; i < 10; i++) funcN(i); Zamet'te, chto cikl teper' nachinaetsya s indeksa 3. A teper' - sluchaj, gde smes' cikla i uslovnogo operatora opravdana: int i; for(i=0; i < 100; i++){ if((i % 2) == 0) even(); /* chetnyj */ else odd(); /* nechetnyj */ } Tut v cikle proveryaetsya chetnost' indeksa i. 03.c /* Treugol'nik iz zvezdochek */ #include <stdio.h> /* putchar('c') - pechataet odinokij simvol c */ /* simvol \n - perevodit stroku */ /* nstars - skol'ko zvezdochek napechatat' */ /* Funkciya risovaniya odnoj stroki treugol'nika */ void drawOneLine(int nstars){ int i; /* nomer pechataemoj zvezdochki, schetchik */ for(i=0; i < nstars; i++) /* Risuem nstars zvezdochek podryad */ putchar('*'); putchar('\n'); /* I perehodim na sleduyushchuyu stroku */ } void main(){ /* OB¬YAVLENIE PEREMENNYH */ int nline; /* nomer stroki */ /* VYPOLNYAEMYE OPERATORY (DEJSTVIYA) */ for(nline=1; nline <= 25; nline++) drawOneLine(nline); /* skol'ko zvezdochek? stol'ko zhe, kakov nomer stroki */ } 04.c /* Treugol'nik iz zvezdochek */ /* Tot zhe primer so vlozhennym ciklom, a ne s funkciej */ #include <stdio.h> void main(){ /* OB¬YAVLENIE PEREMENNYH */ int nline; /* nomer stroki */ int i; /* nomer pechataemoj zvezdochki, schetchik */ /* VYPOLNYAEMYE OPERATORY (DEJSTVIYA) */ for(nline=1; nline <= 25; nline++){ /* skol'ko zvezdochek? stol'ko zhe, kakov nomer stroki */ for(i=0; i < nline; i++) putchar('*'); putchar('\n'); } } 05.c /* Treugol'nik iz zvezdochek */ /* Teper' treugol'nik dolzhen byt' ravnobedrennym */ #include <stdio.h> /* nstars - skol'ko zvezdochek napechatat' */ /* nspaces - skol'ko probelov napechatat' pered zvezdochkami */ void drawOneLine(int nspaces, int nstars){ int i; /* nomer pechataemoj zvezdochki, schetchik */ /* on zhe - nomer pechataemogo probela */ for(i=0; i < nspaces; i++) putchar(' '); for(i=0; i < nstars; i++) putchar('*'); putchar('\n'); } /* n (nomer stroki) ...* 1 ..*** 2 .***** 3 ******* 4 Vsego strok: LINES CHislo zvezdochek v n-oj stroke: n*2 - 1 CHislo probelov speredi (oboznacheny tochkoj): LINES - n Vse eti chisla podschityvayutsya s kartinki... Ih my budem peredavat' v funkciyu drawOneLine v tochke _vyzova_, a ne vychislyat' v samoj funkcii. Funkciya dlya togo i zavedena, chtoby ne vychislyat' nichego KONKRETNOGO - vse parametry ee peremennye, i dolzhny PEREDAVATXSYA v nee iz tochki vyzova. V kachestve parametra v tochke vyzova mozhno peredavat' ne tol'ko znachenie peremennoj, no i znachenie vyrazheniya, to est' formuly. */ void main(){ /* OB¬YAVLENIE PEREMENNYH */ int LINES = 25; /* vsego strok. |to opisanie peremennoj srazu s ee inicializaciej */ int nline; /* nomer stroki */ /* VYPOLNYAEMYE OPERATORY (DEJSTVIYA) */ for(nline=1; nline <= LINES; nline++) drawOneLine(LINES - nline, /* chislo probelov --> nspaces */ nline*2 - 1 /* chislo zvezdochek --> nstars */ ); } 06.c /* Treugol'nik iz zvezdochek */ /* Teper' treugol'nik dolzhen byt' ravnobedrennym */ #include <stdio.h> void drawOneLine(int nspaces, int nstars){ int i; /* nomer pechataemoj zvezdochki, schetchik */ /* on zhe - nomer pechataemogo probela */ for(i=0; i < nspaces; i++) putchar(' '); for(i=0; i < nstars; i++) putchar('*'); putchar('\n'); } void main(){ /* OB¬YAVLENIE PEREMENNYH */ int LINES = 25; /* vsego strok. */ int nline; /* nomer stroki */ /* Dlya cheloveka estestvenno schitat' s 1. Dlya mashiny zhe pervoe chislo - eto NULX. Poetomu cikl for(nline=1; nline <= LINES; nline++) Sleduet zapisat' v vide for(nline=0; nline < LINES; nline++) On tozhe vypolnitsya 25 raz, no znachenie peremennoj-schetchika nline budet na kazhdoj iteracii na 1 men'she. Poetomu nado pomenyat' raschet parametrov dlya funkcii risovaniya. n (nomer stroki) ...* 0 ..*** 1 .***** 2 ******* 3 Vsego strok: LINES CHislo zvezdochek v n-oj stroke: n*2 + 1 CHislo probelov speredi (oboznacheny tochkoj): LINES - n - 1 */ /* VYPOLNYAEMYE OPERATORY (DEJSTVIYA) */ for(nline=0; nline < LINES; nline++) drawOneLine(LINES - nline - 1, nline*2 + 1); } 07.c /* Tip peremennyh dlya hraneniya BUKV nazyvaetsya char (ot slova character). Bukvy izobrazhayutsya v odinochnyh kavychkah 'a' 'b' '+'. Primer: char letter; letter = 'a'; putchar(letter); letter = 'b'; putchar(letter); letter = '\n'; putchar(letter); Simvol '\n' oboznachaet "nevidimuyu bukvu" - perehod na novuyu stroku, new line. Est' neskol'ko takih special'nyh bukv, o nih - pozzhe. Zato srazu sdelaem ogovorku. CHtoby izobrazit' samu bukvu \ sleduet ispol'zovat' '\\' putchar('\'); ili printf ("\"); oshibochny. Nado: putchar('\\'); printf("\\"); Delo v tom, chto simvol \ nachinaet posledovatel'nost' iz DVUH bukv, izobrazhayushchih ODNU bukvu, inogda vyzyvayushchuyu special'nye dejstviya na ekrane ili na printere. */ /* CHislo delitsya na n, esli OSTATOK ot deleniya ego na n raven 0, to est' esli (x % n) == 0 V chastnosti, tak mozhno proveryat' chisla na chetnost'/nechetnost', berya x%2. Ostatki ot deleniya chisla x na n eto 0 1 2 ... n-1. V sluchae deleniya na 2 ostatok 0 sootvetstvuet chetnomu x 1 sootvetstvuet nechetnomu x */ /* Zadacha: Narisovat' treugol'nik iz zvezdochek v nechetnyh strokah iz plyusikov v chetnyh strokah *--------------------------------------------------------* Reshenie: ispol'zuem prezhnyuyu programmu, dobaviv v funkciyu drawOneLine eshche odin argument - symbol - kakim simvolom risovat' stroku. Dalee v osnovnom cikle ispol'zuem uslovnyj operator i proverku nomera stroki na chetnost'. */ #include <stdio.h> void drawOneLine(int nspaces, int nsymbols, char symbol){ int i; /* schetchik */ for(i=0; i < nspaces; i++) putchar(' '); for(i=0; i < nsymbols; i++) putchar(symbol); putchar('\n'); } /* My vynesem ob®yavlenie etoj peremennoj iz funkcii, sdelav ee "global'noj", to est' vidimoj vo VSEH funkciyah. */ int LINES = 25; /* vsego strok. */ void main(){ /* OB¬YAVLENIE PEREMENNYH */ int nline; /* nomer stroki */ /* VYPOLNYAEMYE OPERATORY (DEJSTVIYA) */ for(nline=0; nline < LINES; nline++){ if((nline % 2) == 0) /* chetnoe ? */ drawOneLine(LINES - nline - 1, nline*2 + 1, '+'); else drawOneLine(LINES - nline - 1, nline*2 + 1, '*'); } } 08.c /* To zhe samoe, no teper' nuzhno eshche i pechatat' nomer stroki. */ #include <stdio.h> /* Voobshche-to global'nye peremennye prinyato ob®yavlyat' v samom nachale fajla s programmoj. */ int LINES = 25; /* vsego strok. */ /* Dobavim k funkcii eshche odin argument, ukazatel' - pechatat' li nomer stroki. Nazovem ego drawLineNumber. Ne vpadite v zabluzhdenie po analogii s imenem FUNKCII drawOneLine() ! V dannom sluchae - eto imya PEREMENNOJ - ARGUMENTA FUNKCII. Operator if(x) .....; RABOTAET TAKIM OBRAZOM (tak on ustroen): v kachestve usloviya on prinimaet celoe chislo (tipa int). Uslovie istinno, esli x != 0, i lozhno, esli x == 0. Vtoroj dobavlennyj argument - sobstvenno nomer stroki. */ void drawOneLine(int nspaces, int nsymbols, char symbol, /* a eto my dobavili */ int drawLineNumber, int linenum ){ int i; /* schetchik */ if(drawLineNumber) printf("%d\t", linenum); /* bez perevoda stroki */ /* Na samom dele eto uslovie bolee polno nado zapisyvat' kak if(drawLineNumber != 0) no v yazyke Si eto to zhe samoe. */ /* Tut my snova vidim novyj special'nyj simvol \t - TABULYACIYA. Ves' ekran (ili list bumagi) uslovno podelen na kolonki shirinoj po 8 pozicij. Primerno tak: | | | | | | | | | ... Simvol tabulyacii vyzyvaet perehod iz tekushchej pozicii v nachalo sleduyushchej kolonki. Naprimer | | | | | | | | | ... ^ otsyuda | | | | | | | | | ... ^ v eto mesto */ for(i=0; i < nspaces; i++) putchar(' '); for(i=0; i < nsymbols; i++) putchar(symbol); putchar('\n'); } void main(){ /* OB¬YAVLENIE PEREMENNYH */ int nline; /* nomer stroki */ /* VYPOLNYAEMYE OPERATORY (DEJSTVIYA) */ for(nline=0; nline < LINES; nline++){ if((nline % 2) == 0) /* chetnoe ? */ drawOneLine(LINES - nline - 1, nline*2 + 1, '+', 1, nline); else drawOneLine(LINES - nline - 1, nline*2 + 1, '*', 9, nline); } /* A pochemu imenno 1 ili imenno 9 ? * A vse chto popalo, lish' by ne 0. * Mozhno 3, 333, 666, -13445, itp * * Vopros: chto budet, esli tut napisat' 0 ? */ } 09.c /* Sleduyushchaya zadacha budet kasat'sya togo, chtoby kazhdaya stroka treugol'nika pechatalas' v vide: *+*+*+*.....*+* Tut nam uzhe pridetsya modificirovat' funkciyu risovaniya stroki. */ #include <stdio.h> int LINES = 25; /* vsego strok. */ void drawOneLine(int nspaces, int nsymbols){ int i; for(i=0; i < nspaces; i++) putchar(' '); /* v cikle my budem proveryat' na chetnost' NOMER pechataemogo simvola. */ for(i=0; i < nsymbols; i++){ if((i % 2) == 0) putchar('*'); else putchar('+'); } putchar('\n'); } void main(){ int nline; /* nomer stroki */ for(nline=0; nline < LINES; nline++) { drawOneLine(LINES - nline - 1, nline*2 + 1); } } 10.c /* Zadacha narisovat' ROMB: * *** ***** *** * */ #include <stdio.h> int LINES = 10; /* vsego strok v polovine romba. */ void drawOneLine(int nspaces, int nsymbols){ int i; for(i=0; i < nspaces; i++) putchar(' '); for(i=0; i < nsymbols; i++) putchar('+'); putchar('\n'); } void main(){ int nline; /* nomer stroki */ for(nline=0; nline < LINES; nline++) drawOneLine(LINES - nline - 1, nline*2 + 1); /* My narisovali treugol'nik. Teper' nam nuzhen perevernutyj treugol'nik. Pishem cikl po ubyvaniyu indeksa. S dannogo mesta nomera strok otschityvayutsya v obratnom poryadke: ot LINES-2 do 0 */ for(nline=LINES-2; nline >= 0; nline--) drawOneLine(LINES - nline - 1, nline*2 + 1); } 11.c /* A teper' risuem romb, ispol'zuya matematicheskie formuly. */ #include <stdio.h> void draw(int nspaces, int nstars, char symbol){ int i; for(i=0; i < nspaces; i++) putchar(' '); for(i=0; i < nstars; i++) putchar(symbol); putchar('\n'); } void main(){ int LINES = 21; int MIDDLELINE = LINES/2 + 1; /* seredina romba */ int nline; for(nline=0; nline < MIDDLELINE; nline++) draw(MIDDLELINE - nline -1, nline*2+1, 'A'); /* U sleduyushchego cikla for() net inicializacii nachal'nogo znacheniya indeksa. Nachal'noe nline nasleduetsya iz predydushchego cikla, takim, kakim ono ostalos' posle ego okonchaniya, to est' ravnym MIDDLELINE. */ for( ; nline < LINES; nline++) draw(nline - MIDDLELINE + 1, (LINES - 1 - nline) * 2 + 1, 'V'); }  * 12_ARRAYS.txt *  MASSIVY Massiv - eto neskol'ko pronumerovannyh peremennyh, ob®edinennyh obshchim imenem. Vse peremennye imeyut ODIN I TOT ZHE TIP. Rassmotrim POLKU s N yashchikami, pust' imya polki - var. Togda kazhzhdyj yashchik-yachejka imeet imya var[0] var[1] ... var[N-1] Numeraciya idet s NULYA. -------- / var / / / ------------------------------------------- ------------------ | | | | | | | | | | .... ... | | | | | | | | ------------------------------------------- ------------------ / var[0] / / var[1] / / var[2] / / var[N-1] / --------- --------- --------- ----------- Massiv ob®yavlyaetsya tak: int var[N]; zdes' N - ego razmer, chislo yacheek. |to opisanie kak by ob®yavlyaet N peremennyh tipa int s imenami var[0] ... var[N-1]; V operatorah dlya obrashcheniya k n-omu yashchichku (gde 0 <= n < N) ispol'zuetsya imya yashchika var[n] gde n - celoe znachenie (ili znachenie celoj peremennoj, ili celochislennogo vyrazheniya), "indeks v massive". |ta operaciya [] nazyvaetsya "indeksaciya massiva". Indeksaciya - est' VYBOR odnogo iz N yashchikov pri pomoshchi ukazaniya celogo nomera. var - massiv (N yacheek) n - vyrazhenie (formula), vydayushchaya celoe znachenie v intervale 0..N-1 var[n] - vzyat odin iz elementov massiva. Odin iz vseh. n - nomer yashchika - nazyvaetsya eshche i "indeksom" etoj peremennoj v massive. Primer: int var[5]; /* 1 */ var[0] = 2; /* 2 */ var[1] = 3 + var[0]; /* 3 */ var[2] = var[0] * var[1]; /* 4 */ var[3] = (var[0] + 4) * var[1]; /* 5 */ printf("var tret'e est' %d\n", var[3]); V hode etoj programmy elementy massiva menyayutsya takim obrazom: var[0] var[1] var[2] var[3] var[4] ------------------------------------------------ /* 1 */ musor musor musor musor musor /* 2 */ 2 musor musor musor musor /* 3 */ 2 5 musor musor musor /* 4 */ 2 5 10 musor musor /* 5 */ 2 5 10 30 musor Kak vidim, kazhdyj operator izmenyaet lish' ODNU yachejku massiva za raz. Massiv - nabor peremennyh, kotorye ne IMENOVANY raznymi imenami, vrode var0, var1, var2, ... a PRONUMEROVANY pod odnim imenem: var[0], var[1], var[2], ... Indeks - chast' IMENI PEREMENNOJ. Na samom dele indeksaciya - eto 1) vybor elementa v massive 2) sprava ot prisvaivanij i v vyrazheniyah - eshche i razymenovanie, to est' vzyatie vmesto imeni peremennoj - znacheniya, v nej hranyashchegosya. --------------------------------------------------------------------- Esli v peremennuyu ne bylo zaneseno znachenie, a my ispol'zuem etu peremennuyu, to v nej lezhit MUSOR (lyuboe, nepredskazuemoe znachenie). printf("var4 est' %d\n", var[4]); napechataet vse chto ugodno. Poetomu peremennye nado vsegda inicializirovat' (davat' im nachal'noe znachenie). Global'nye peremennye avtomaticheski inicializiruyutsya nulem, esli my ne zadali inache. Lokal'nye peremennye ne inicializiruyutsya avtomaticheski, i soderzhat MUSOR. --------------------------------------------------------------------- Massivy NELXZYA prisvaivat' celikom, yazyk Si etogo ne umeet. int a[5]; int b[5]; a = b; /* oshibka */ Takzhe nel'zya prisvoit' znachenie srazu vsem elementam (yachejkam) massiva: a = 0; /* oshibka */ ne delaet togo, chto nami ozhidalos', a yavlyaetsya oshibkoj. Dlya obnuleniya vseh yacheek sleduet ispol'zovat' cikl: int i; for(i=0; i < 5; i++) /* dlya kazhdogo i prisvoit' a[i] = 0; */ a[i] = 0; --------------------------------------------------------------------- SVYAZX MASSIVOV I CIKLOV ======================= Vsledstvie etogo massivy prihoditsya kopirovat' (i inicializirovat') poelementno, v cikle perebiraya vse (ili chast') yachejki massiva. int i; for(i=0; i < 5; i++) a[i] = b[i]; V dannom sluchae indeks cikla sluzhit takzhe i indeksom v massive. Indeksy v massive idut s NULYA. Primer inicializacii: int index, array[5]; for(index=0; index < 5; index++) array[index] = index * 2 + 1; ili int index, array[5]; index = 0; while(index < 5){ array[index] = index * 2 + 1; index++; } /* V massive budet: { 1, 3, 5, 7, 9 } */ INDEKS dlya massivov - nomer "yashchika/yachejki" v massive. dlya ciklov - nomer povtoreniya cikla, schetchik. My dolzhny izmenyat' ego SAMI. Obychno massivy i cikly sovmeshchayutsya tak: indeks cikla est' indeks v massive; to est' indeks cikla ispol'zuetsya dlya perebora vseh elementov massiva: int a[N], i; for(i=0; i < N; i++) ...a[i]... --------------------------------------------------------------------- Primery: int a[5]; a[0] = 17; a[0] += 4; a[0]++; --------------------------------------------------------------------- Primer: chisla Fibonachchi. Zadayutsya matematicheskimi formulami: f[1] = 1 f[2] = 1 f[n+2] = f[n+1] + f[n] Vot programma: ------------------ #include <stdio.h> /* magicheskaya stroka */ #define N 20 /* skol'ko pervyh chisel poschitat' */ void main(){ int fibs[N], index; fibs[0] = 1; /* indeksy otschityvayutsya s nulya!!! */ fibs[1] = 1; /* Tut pokazano, chto indeks elementa massiva mozhet vychislyat'sya */ for(index=2; index < N; index++) fibs[index] = fibs[index-1] + fibs[index-2]; /* Raspechatka v obratnom poryadke */ for(index = N-1; index >= 0; index--) printf("%d-oe chislo Fibonachchi est' %d\n", index+1, fibs[index]); } Zdes' my vidim novyj dlya nas operator #define On zadaet tekstual'nuyu ZAMENU slova N na slovo 20, v dannom sluchae prosto yavlyayas' ekvivalentom const int N = 20; K neschast'yu razmer massiva ne mozhet byt' zadan pri pomoshchi peremennoj, a vot pri pomoshchi imeni, opredelennogo v #define - mozhet. STROKI Stroki est' massivy BUKV - tipa char, okanchivayushchiesya specsimvolom \0 char string[20]; string[0] = 'P'; string[1] = 'r'; string[2] = 'i'; string[3] = 'v'; string[4] = 'e'; string[5] = 't'; string[6] = '\0'; printf("%s\n", string); %s - format dlya pechati STROK. Nikakie drugie massivy ne mogut byt' napechatany celikom odnim operatorom. char string[20]; string[0] = 'P'; string[1] = 'r'; string[2] = 'i'; string[3] = 'v'; string[4] = 'e'; string[5] = 't'; string[6] = '\n'; /* Perevod stroki - tozhe bukva */ string[7] = '\0'; printf("%s", string); ili dazhe prosto printf(string); Takie massivy mozhno zapisat' v vide stroki bukv v "" char string[20] = "Privet\n"; Ostavshiesya neispol'zovannymi simvoly massiva ot string[8] do string[19] soderzhat MUSOR. POCHEMU DLYA STROK IZOBRELI SIMVOL "PRIZNAK KONCA"? ================================================= Stroka - eto CHASTX massiva bukv. V raznoe vremya chislo bukv v stroke mozhet byt' razlichnym, lish' by ne prevyshalo razmer massiva (togda sluchitsya sboj programmy). Znachit, sleduet gde-to hranit' tekushchuyu dlinu stroki (chislo ispol'zovannyh simvolov). Est' tri resheniya: (1) V otdel'noj peremennoj. Ee sleduet peredavat' vo vse funkcii obrabotki dannoj stroki (prichem ona mozhet izmenyat'sya). char str[32]; /* massiv dlya stroki */ int slen; /* brat' pervye slen bukv v etom massive */ ... func(str, &slen); /* DVA argumenta dlya peredachi ODNOJ stroki */ ... |tot podhod rabotosposoben, no stroka razbivaetsya na dva ob®ekta: sam massiv i peremennuyu dlya ego dliny. Neudobno. (2) Hranit' tekushchuyu dlinu v elemente str[0], a bukvy - v str[1] ... itd. Ploho tem, chto v str[0] mozhno hranit' lish' chisla ot 0 do 255, i esli stroka dlinnee - to takoj podhod neprimenim. (3) Ne hranit' dlinu NIGDE, a vvesti simvol-priznak konca stroki. Teper' v func(str); /* ODIN argument - sam massiv */ peredaetsya tol'ko sam massiv, a ego tekushchaya dlina mozhet byt' pri nuzhde vychislena pri pomoshchi nekoej funkcii, vrode takoj: int strlen(char s[]){ /* funkciya ot massiva bukv */ int counter = 0; /* schetchik i odnovremenno indeks */ while(s[counter] != '\0') /* poka ne vstretilsya priznak konca teksta */ cou