




























































































Studirajte zahvaljujući brojnim resursima koji su dostupni na Docsity-u
Zaradite bodove pomažući drugim studentima ili ih kupite uz Premium plan
Pripremite ispite
Studirajte zahvaljujući brojnim resursima koji su dostupni na Docsity-u
Nabavite poene za preuzimanje
Zaradite bodove pomažući drugim studentima ili ih kupite uz Premium plan
Sve što vam je potrebno da znate o programskom jeziku C
Tipologija: Skripte
1 / 176
Ova stranica nije vidljiva u pregledu
Ne propustite važne delove!





























































































Deo I
Pod razvojem softvera ˇcesto se ne misli samo na neposredno pisanje programa, ve´c i na procese koji mu prethode i slede. U tom, ˇsirem smislu razvoj softvera se naziva i ˇzivotni ciklus razvoja softvera. Razvoj softvera se razlikuje od sluˇcaja do sluˇcaja, ali u nekoj formi obiˇcno ima slede´ce faze i podfaze:
Planiranje: Ova faza obuhvata prikupljanje i analizu zahteva od naruˇcioca softvera, razreˇsavanje nepot- punih, viˇsesmislenih ili kontradiktornih zahteva i kreiranje precizne specifikacije problema i dizajna softverskog reˇsenja. Podfaze ove faze su:
∙ Analiza i specifikovanje problema (obiˇcno je sprovodi analitiˇcar, koji nije nuˇzno informatiˇcar, ali mora da poznaje relevantne poslovne ili druge procese); ∙ Modelovanje reˇsenja (obiˇcno je sprovodi projektant, koji mora da razume i specifikaciju prob- lema i da je u stanju da izabere adekvatna softverska reˇsenja, npr. programski jezik, bazu podataka, relevantne biblioteke, strukture podataka, matematiˇcku reprezentaciju problema, algoritamska reˇsenja, itd); ∙ Dizajn softverskog reˇsenja (sprovode je programeri).
Realizacija: Ova faza obuhvata implementiranje dizajniranog softverskog reˇsenja u nekom konkretnom programskom jeziku. Implementacija treba da sledi opˇste preporuke, kao i preporuke specifiˇcne za realizatora ili za konkretan projekat. Pouzdanost i upotrebljivost softverskog proizvoda proverava se analizom efikasnosti i ispravnosti, a za naruˇcioca se priprema i dokumentacija. Podfaze ove faze su:
∙ Implementiranje (kodiranje, pisanje programa) (o nekim aspektima ove faze govori glava 2); ∙ Analiza efikasnosti i ispravnosti (o nekim aspektima ovih faza govore redom glava 3 i glava 4); ∙ Izrada dokumentacije (obiˇcno korisniˇcke dokumentacije – koja opisuje koriˇs´cenje programa i tehniˇcke dokumentacije — dokumentacije koja opisuje izvorni kˆod);
Eksploatacija: Ova faza poˇcinje nakon ˇsto je ispravnost softvera adekvatno proverena i nakon ˇsto je softver odobren za upotrebu. Puˇstanje u rad ukljuˇcuje instaliranje, podeˇsavanja u skladu sa specifiˇcnim potrebama i zahtevima korisnika ali i testiranje u realnom okuˇzenju i sveukupnu eval- uaciju sistema. Organizuje se obuka za osnovne i napredne korisnike i obezbeduje odrˇzavanje kroz koje se ispravljaju greˇske ili dodaju nove manje funkcionalnosti. U odrˇzavanje se obiˇcno uloˇzi viˇse od tri ˇcetvrtine ukupnog rada u ˇcitavom ˇzivotnom ciklusu softvera. Podfaze ove faze su:
∙ Obuka i tehniˇcka podrˇska; ∙ Puˇstanje u rad; ∙ Odrˇzavanje.
Postoje i medunarodni standardi, kao ˇsto je ISO/IEC 12207 i ISO/IEC 15504, koji opisuju ˇzivotni ciklus softvera, kros precizno opisane postupke izbora, implementacije i nadgledanja razvoja softvera. Kvalitet razvijenog posla ˇcesto se ocenjuje prema nivou uskladenosti sa ovim standardima.
Slika 1.1: Faze razvoja softvera ilustrovane na ˇsaljiv naˇcin
Faze razvoja softvera i mogu´ce probleme na ˇsaljiv naˇcin ilustruje ˇcuvena karikatura prikazana na slici 1.1. Za razvoj softvera relevantni su i procesi istraˇzivanja trˇziˇsta, nabavke softvera, naruˇcivanja softvera, razmatranje ponuda i sliˇcni, ali u ovom tekstu ´ce biti reˇci samo o razvoju softvera od faze planiranja. Faza planiranja ´ce biti ukratko opisana u nastavku ovog poglavlja, dok ´ce faza realizacije i njene podfaze biti opisane detaljnije u narednim glavama.
1.1 Metodologije razvoja softvera
I u teoriji i u praksi postoje mnoge metodologije razvoja softvera. U praksi su one ˇcesto pomeˇsane i ˇcesto je teˇsko razvrstati stvarne projekte u postoje´ce metodologije. U nastavku je opisano nekoliko bitnih metodologija i ideja na kojima su zasnovane.
Metodologija vodopada. U strogoj varijanti metodologije vodopada (eng. waterfall), na slede´cu fazu u razvoju softvera prelazi se tek kada je jedna kompletno zavrˇsena. Ova metodologija ilustrovana je slikom 1.2. Metodologija se smatra primenljivom ako su ispunjeni slede´ci uslovi:
∙ svi zahtevi poznati su unapred; ∙ zahtevi nemaju nerazreˇsene, potencijalno riziˇcne faktore (npr. rizike koji se odnose na cenu, tempo rada, efikasnost, bezbednost, itd); ∙ priroda zahteva se ne menja bitno u toku razvoja; ∙ zahtevi su u skladu sa oˇcekivanjima svih relevantnih strana (investitori, korisnici, realizatori, itd.); ∙ pogodna arhitektura reˇsenja moˇze biti opisana i podrobno shva´cena; ∙ na raspolaganju je dovoljno vremena za rad u etapama.
Ovaj model ne predvida modifikovanje prethodnih faza jednom kada su zavrˇsene i ova osobina je predmet najˇceˇs´cih kritika. Naime, izrada aplikacija ˇcesto traje toliko dugo da se zahtevi promene u meduvremenu i zavrˇsni proizvod viˇse nije sasvim adekvatan a ponekad ni uopˇste upotrebljiv.
segmente aplikacije (kao ˇsto su, na primer, poslovni procesi i obrada podataka). Cak i mnogiˇ razvojni alati privilegovano mesto u razvoju softvera daju razvoju grafiˇckih interfejsa. Ovakav razvoj aplikacije ˇcesto dovodi do niza prototipova sa razradenim korisniˇckim interfejsom, ali bez adekvatnih obrada koje stoje iza njega.
Spiralna metodologija. Ova metodologija kombinuje analizu rizika sa nekim osobinama metodologije vodopada i metodologije rapidnog razvoja. Spirala koja ilustruje ovu metodologiju prolazi u iteraci- jama kroz nekoliko faza kao ˇsto su planiranje i dizajn, implementacija, analiza rizika, evaluacija teku´ce verzije implementacije. U svakoj iteraciji analiziraju se relevantni faktori rizika (npr. rizici koji se odnose na cenu, tempo rada, efikasnost, bezbednost, itd). Ako neki rizik ne moˇze biti eliminisan, naruˇcilac mora da odluˇci da li se sa projektom nastavlja ili ne. Ukoliko se sa projektom nastavlja, ulazi se u slede´cu iteraciju. Ova metodologija se obiˇcno ne koristi u projektima u kojima analiza rizika moˇze da ugrozi isplativost projekta.
Metodologija ekstremnog razvoja. Ovo je jedna od tzv. agilnih metodologija u kojima se koristi pojednostavljeni iterativni razvoj u ˇcijem centru su pojedinaˇcni programeri. U metodologiji ek- stremnog programiranja, faze se sprovode u veoma malim koracima i prva iteracija moˇze da dovede, do svesno nepotpune ali funkcionalne celine, ve´c za jedan dan ili nedelju (za razliku od nekoliko meseci ili ˇcak godina u metodologiji vodopada). Osnovni principi koje agilni razvoj prati su slede´ci:
∙ Ljudi i njihova komunikacija su bitniji od procesa i alata – samoorganizovanje, motivacija programera i njihove medusobne interakcije su jako vaˇzne (podstiˇce se, na primer, rad u parovima). Projekat ne moˇze da uspe ako ljudi nisu kvalitetni. Izgradnja tima vaˇznija je od izgradnje okruˇzenja. ∙ Naruˇciocu je mnogo bolje tokom sastanaka prikazivati softver koji neˇsto radi (ne sve i ne savrˇseno) nego mu prikazivati samo planove i dokumente. Dokumentacija mora da postoji, ali previˇse dokumentacije moˇze biti gore nego premalo (naime, osnovni cilj projekta obiˇcno nije dokumentacija, ve´c softver). ∙ Zahtevi ne mogu da se u potpunosti utvrde na samom poˇcetku pa naruˇcilac treba da kon- stantno bude ukljuˇcen u razvojni tim. ∙ Zahtevi se menjaju i mnogo je bolje na njih odgovarati odmah nego se striktno drˇzati unapred zacrtanog plana. Vizija i glavni ciljevi moraju da postoje, ali treba biti fleksibilan i dopustiti promene tokom razvoja.
Agilni razvoj vezan je ˇcesto i za automatizovane testove kojima se sa jedne strane preciziraju zahtevi koje odredene softverske komponente (na primer, funkcije) treba da zadovolje, a sa druge strane proverava se koliko te komponente zaista ispunjavaju date zahteve. Testovi se ˇcesto piˇsu pre implementacije samih funkcija, u njima se vrˇse pozivi funkcija za odredene karakteristiˇcne ulaze i proverava se da li se vra´ceni rezultati poklapaju sa oˇcekivanim izlazima. Testovi se piˇsu na osnovu opˇstih pravila za testiranje softvera o kojima ´ce biti viˇse reˇci u narednim poglavljima. U ekstremnom razvoju, programiranje se obiˇcno vrˇsi u paru. Dva programera istovremeno reˇsavaju problem, jedan piˇse kˆod dok drugi proverava njegovo pisanje i pokuˇsava da pronade i ukaˇze na eventualne greˇske i nedostatke. Tokom razvoja svi unapred zadati testovi treba da budu zadovoljeni, ali tokom razvoja programeri mogu i da dodaju nove testove. Implementacija se zavrˇsava tek kada oba programera budu zadovoljna kodom, kada kˆod prolazi sve testove i kada programeri ne mogu da se sete novih testova koje bi trebalo dodati. Dizajn i arhitektura koda se ne zadaju unapred, ve´c se oni javljaju tokom samog kodiranja i kasnije, u procesu refaktorisanja koda (izmene njegove unutraˇsnje strukture bez izmene funkcional- nosti). Dakle, isti ljudi koji rade kodiranje brinu o dizajnu. Nekompletni, ali funkcionalni sistem se konstantno demonstrira naruˇciocu (i ostatku tima, ali i krajnjim korisnicima). Tada se kre´ce sa pisanjem testova za naredni vaˇzan deo sistema.
1.2 Planiranje
Faza planiranja moˇze da obuhvati slede´ce zadatke:
∙ analizu i specifikovanje problema,
∙ modelovanje reˇsenja,
∙ dizajn softverskog reˇsenja.
Analiza se pre svega bavi samim problemom tj. njegovom preciznom postavkom i formulacijom, dok se modelovanje i dizajn bave reˇsenjem problema koji je definisan u fazi analize. U fazi planiranja sistema obiˇcno se koriste razliˇcite dijagramske tehnike i specijalizovani alati koji podrˇzavaju kreiranje ovakvih dijagrama (tzv. CASE alati – Computer Aided Software Engineering).
S obzirom na to da se softver obiˇcno piˇse za naruˇcioce, u procesu analize i specifikovanja problema vrˇsi se intenzivna komunikacija analitiˇcara sa njima. Kada se softver pravi po narudˇzbini, naruˇcioci mogu da budu krajnji korisnici ili njihovi predstavnici, ali ˇcest sluˇcaj u velikim kompanijama je da ulogu naruˇcioca preuzimaju radnici zaposleni u odeljenju prodaje ili marketinga (koji imaju ideju kakav proizvod bi kasnije mogli da prodaju). Tokom sastanaka ˇcesto se najpre vrˇsi analiza postoje´cih reˇsenja (na primer, postoje´ceg poslovnog procesa u kompaniji koja uvodi informacioni sistem) i razmatraju se mogu´cnosti njihovog unapredenja uvodenjem novog softvera. Kako naruˇcioci obiˇcno nemaju potrebno informatiˇcko obrazovanje, oni ˇcesto nisu svesni svih mogu´cnosti koje novi softver moˇze da im pruˇzi. Jedan od zadataka analitiˇcara je da ovakve mogu´cnosti uoˇcava i da o njima komunicira sa naruˇciocima. Naruˇcioci formuliˇsu zahteve (engl. requirements) koje softver koji se proizvodi treba da zadovolji. Zahtevi su ˇcesto neprecizni, pa ˇcak i kontradiktorni, i zadatak analitiˇcara je da ih u saradnji sa naruˇciocima precizira i uobliˇci. Pored precizne analize zahteva, zadatak analize je i da se naprave procene obima, cene i vremena potrebnog da se projekat realizuje. Preciznije, analiza treba da proceni:
∙ obim posla koji ´ce da se radi (potrebno je precizno definisati ˇsta projekat treba da obuhvati, a ˇsta ne);
∙ rizike koji postoje (i da definiˇse odgovaraju´ce reakcije projektnog tima u sluˇcaju da neˇsto pode drugaˇcije nego ˇsto je planirano);
∙ resurse (ljudske, materijalne) koji su potrebni;
∙ oˇcekivanu cenu realizacije projekta i njegovih delova;
∙ plan rada (po fazama) koji je neophodno poˇstovati.
Obim posla ˇcesto se izraˇzava u terminima broja potrebnih ˇcovek-meseci (1 ˇcovek-mesec podrazumeva da jedan ˇcovek na projektu radi mesec dana). Rezultat analize je precizna specifikacija problema. Specifikacijom je potrebno ˇsto preciznije opisati problem, prirodu ulaznih podataka i oblik u kome se ˇzele reˇsenja — izlazni rezultati. Specifikacija programa bavi se pitanjem ˇsta program treba da uradi, kojom brzinom, koja mu je maksimalna dozvoljena veliˇcina, itd. Kada je problem precizno specifikovan, prelazi se na slede´ce faze u kojima se modeluje i dizajnira reˇsenje specifikovanog problema.
Modelovanje reˇsenja obiˇcno sprovodi projektant, koji mora da razume specifikaciju problema i da je u stanju da izradi matematiˇcke modele problema i da izabere adekvatna softverska reˇsenja, npr. programski jezik, bazu podataka, relevantne biblioteke, strukture podataka, matematiˇcku reprezentaciju problema, algoritamska reˇsenja, itd).
razumeti i bez prethodne obuke. Mnogi alati koji se koriste tokom dizajna softvera (npr. CASE alati) omogu´cuju kreiranje tokovnika. Notacija koja se koristi u tokovnicima nije standardizovana, ali razliˇcite notacije su ˇcesto sliˇcne. Na primer,
∙ Zaobljeni pravougaonici predstavljaju funkcije koje transformiˇsu ulaz u izlaz. Ime u pravougaoniku oznaˇcava ime funkcije.
∙ Pravougaonici oznaˇcavaju skladiˇsta podataka. I njima je poˇzeljno dati ilustrativna imena.
∙ Krugovi predstavljaju korisnike koji interaguju sa sistemom daju´ci ulaz ili prihvataju´ci izlaz.
∙ Strelice oznaˇcavaju smer toka podataka. Na strelicama se oznaˇcava vrsta podataka koja se prenosi.
Tokovnici opisuju funkcijske transformacije, ali ne sugeriˇsu kako se one mogu implementirati. Na primer, sistem opisan na ovaj naˇcin moˇze biti implementiran u obliku jednog programa sa viˇse funkcija ili u obliku niza programa koji medusobno komuniciraju.
Funkcijski-orijentisan dizajn
U funkcijski-orijentisanom dizajnu svaki softverski modul je odgovoran samo za jedan zadatak i sprovodi ga sa minimalnim uticajem na druge delove. Ovo omogu´cava jednostavnije odrˇzavanje sistema. U funkcijski-orijentisanom dizajnu sistem se dekomponuje na skup funkcija koje intereaguju sa za- jedniˇckim centralnim stanjem sistema. Funkcije takode mogu da imaju i svoje lokalno stanje, ali ono se ˇcuva samo tokom izvrˇsavanja funkcije. Ovakav dizajn se neformalno praktikovao joˇs od poˇcetaka programiranja. Programi su dekomponovani u potprograme koji su funkcijski po svojoj prirodi. Funkcijski orijentisan dizajn sakriva detalje algoritama u same funkcije, ali stanje sistema nije sakriveno. Ovo moˇze da dovede do problema jer funkcije (svojim sporednim efektima) mogu da promene stanje na naˇcin na koji to druge funkcije ne oˇcekuju. Izmena jedne funkcije i naˇcina na koji ona menja stanje sistema moˇze da proizvede efekat na ponaˇsanje ostalih funkcija. Dakle, funkcijski-orijentisan dizajn moˇze da bude uspeˇsan u sluˇcajevima kada je stanje sistema minimalno i kada je razmena informacija izmedu funkcija eksplicitna. Sistemi u kojima odgovor zavisi od pojedinaˇcnog unosa (u kojima istorija ranijih uslova nije relevantna da bi se dobio odgovor na teku´ci unos) su prirodno funkcijski-orijentisani. Na primer, softver koji kontroliˇse bankomate je takav – usluga koja se pruˇza korisniku ne zavisi od prethodnih transakcija. Sistem se implementira kao neprekidna petlja u kojoj se akcije pokre´cu kada se ubaci kartica. Sistem je implementiran kroz funkcije poput izbaci_novac, proveri_broj_racuna, izdaj_potvrdu i sliˇcno. Stanje programa je minimalno.
Strukturna dekompozicija
Ponekad strukturna i funkcionalna podela nisu isto iako tradicionalno raˇcunarstvo ˇcesto polazi od pretpostavke da jesu. Strukturna dekompozicija se odnosi na podelu sistema na „delove” tj. na kom- ponente. Funkcionalna dekompozicija se odnosi na podelu funkcija sistema. Cesto postoji tesna vezaˇ izmedu delova i funkcionalnosti koju svaki od delova pruˇza, ali ponekad strukturna i funkcionalna podela ipak nisu isto. Pored dijagrama toka podataka ˇcesto je korisno napraviti i strukturni model sistema koji prikazuje kako je funkcija implementirana time ˇsto se prikazuje koje druge funkcije ona poziva. Strukturni di- jagrami predstavljaju grafiˇcki naˇcin da se predstavi ovakva hijerarhija. Funkcije se ˇcesto predstavljaju pravougaonicima, dok se hijerarhija prikazuje povezivanjem pravougaonika linijama. Ulaz i izlaz (koji se implementiraju kao parametri funkcija ili kao deljene globalne promenljive) prikazuju se oznaˇcenim strelicama. Strelica koja ulazi u pravougaonik predstavlja ulaz, a ona koja izlazi iz njega predstavlja izlaz. Skladiˇsta podataka se predstavljaju kao zaokruˇzeni pravougaonici, a korisniˇcki ulazi kao krugovi. Kreiranje strukturnog dijagrama na osnovu tokovnika zahteva elaboraciju, inventivnost i kreativnost dizajnera. Ipak postoje neka opˇsta uputstva, neka od njih su nabrojana u nastavku.
∙ Mnogi sistemi, naroˇcito poslovni, za koje funkcionalni dizajn ima smisla mogu da se shvate kao trofazni sistemi. U prvoj fazi se prihvata ulaz i vrˇsi se njegova provera, zatim se u drugoj fazi vrˇsi obrada ulaza, a zatim se u tre´coj fazi generiˇse izlaz (ˇcesto u formi nekog izveˇstaja koji se smeˇsta u neku datoteku). Usput se obiˇcno modifikuje i neko globalno skladiˇste podataka. U prvoj iteraciji
strukturnog dijagrama obiˇcno se prepoznaju 3-4 funkcije koje odgovaraju ulazu, obradi, promeni globalnog skladiˇsta podataka i izlazu.
∙ Ako se zahteva provera podataka, funkcije koje vrˇse proveru su podredene funkciji koja prihvata ulaz. Funkcije za formatiranje izlaza, ˇstampu i pisanje na disk podredene su funkciji koja generiˇse izlaz.
∙ Funkcije pri vrhu hijerarhije imaju zadatak da kontroliˇsu i koordiniˇsu skup funkcija niˇzeg nivoa.
∙ Cilj procesa dizajna je da se dode do veoma slabo spregnutih funkcija, od kojih svaka ima visok stepen unutraˇsnje kohezije. Funkcije zato treba da rade jednu i samo jednu stvar.
∙ Svakom ˇcvoru u strukturnom dijagramu odgovara izmedu 2 i 7 podredenih ˇcvorova. Ako postoji samo jedan, to govori da funkcija predstavljena tim ˇcvorom ima slab stepen kohezije. Podredena komponenta moˇzda ne treba da bude zasebna funkcija. Ako postoji previˇse podredenih funkcija to moˇze da znaˇci da je dizajn previˇse razraden za ovaj nivo apstrakcije.
Pitanja i zadaci za veˇzbu
Pitanje 1.1. Navesti faze razvoja softvera i ko ih obiˇcno sprovodi?
Pitanje 1.2. Opisati metodologije razvoja softvera.
Pitanje 1.3. Koje dve vrste dokumentacije treba da postoje?
U modernim programskim jezicima duˇzina reda programa nije ograniˇcena. Ipak, predugi redovi mogu da stvaraju probleme. Na primer, predugi redovi mogu da zahtevaju horizontalno „skrolovanje“ kako bi se video njihov kraj, ˇsto moˇze da drastiˇcno oteˇza ˇcitanje i razumevanje programa. Takode, ukoliko se program ˇstampa, dugi redovi mogu da budu preseˇceni i da naruˇse formatiranje. Zbog ovih i ovakvih problema, preporuˇcuje se pridrˇzavanje nekog ograniˇcenja – obiˇcno 80 karaktera u redu. Konkretna preporuka za 80 karaktera u redu je istorijska i potiˇce od ograniˇcenja na starim ekranima i ˇstampaˇcima i drugim uredajima. Ipak, ona je i danas ˇsiroko prihva´cena kao pogodna. Ukoliko red programa ima viˇse od 80 karaktera, to najˇceˇs´ce ukazuje na to da kˆod treba reorganizovati uvodenjem novih funkcija ili promenljivih. Broj 80 (ili bilo koji drugi) kao ograniˇcenje za broj karaktera u redu ne treba shvatati kruto ve´c kao naˇcelnu preporuku koja moˇze biti naruˇsena ako se tako postiˇze bolja ˇcitljivost.
Red programa moˇze da bude prazan ili da sadrˇzi jednu ili viˇse naredbi. Prazni redovi mogu da izdvajaju blokove blisko povezanih naredbi (na primer, blok naredbi za koje se moˇze navesti komentar o tome ˇsta je njihova svrha). Ako se prazni redovi koriste neoprezno, mogu da naruˇse umesto da poprave ˇcitljivost. Naime, ukoliko ima previˇse praznih linija, smanjen je deo kˆoda koji se moˇze videti i sagledavati istovremeno na ekranu. Po jednoj konvenciji, zagrade koje oznaˇcavaju poˇcetak i kraj bloka navode se u zasebnim redovima (u istoj koloni), a po drugoj, otvorena zagrada se navodi u nastavku prethodne naredbe, a zatvorena u zasebnom redu ili u redu zajedno sa kljuˇcnom reˇcju while ili else. Torvalds preporuˇcuje ovu drugu konvenciju, uz izuzetak da se otvorena vitiˇcasta zagrada na poˇcetku definicije funkcije piˇse u zasebnom redu. Naredni primer prikazuje deo koda napisan sa ve´cim brojem praznih redova i prvom konvencijom za zagrade:
do {
printf("Unesi ceo broj: "); scanf("%d", &i);
if (duzina == alocirano) {
alocirano += KORAK; a = realloc(a, alocirano*sizeof(int)); if (a == NULL) return 1; }
a[duzina++] = i; } while (i != -1);
Isti deo kˆoda moˇze biti napisan sa manjim brojem praznih redova i drugom konvencijom za zagrade. Ovaj primer prikazuje kompaktnije zapisan kˆod koji je verovatno ˇcitljiviji ve´cini iskusnih C programera:
do { printf("Unesi ceo broj: "); scanf("%d", &i); if (duzina == alocirano) { alocirano += KORAK; a = realloc(a, alocirano*sizeof(int)); if (a == NULL) return 1;
a[duzina++] = i; } while (i != -1);
Jedan red moˇze da sadrˇzi i viˇse od jedne naredbe. To je prihvatljivo samo (a tada moˇze da bude i preporuˇcljivo) ako se radi o jednostavnim inicijalizacijama ili jednostavnim dodelama vrednosti ˇclanovima strukture, na primer:
... int i=10; double suma=0; tacka.x=0; tacka.y=0;
Ukoliko je u petlji ili u if bloku samo jedna naredba, onda nisu neophodne zagrade koje oznaˇcavaju poˇcetak i kraj bloka i mnogi programeri ih ne piˇsu. Medutim, iako nisu neophodne one mogu olakˇsati razumevanje kˆoda u kojem postoji viˇsestruka if naredba. Dodatno, ukoliko se u blok sa jednom nared- bom i bez vitiˇcastih zagrada u nekom trenutku doda druga naredba lako moˇze da se previdi da postaje neophodno navesti i zagrade. Veliˇcina blokova kˆoda je takode vaˇzna za preglednost, pa je jedna od preporuka da vertikalno rasto- janje izmedu otvorene vitiˇcaste zagrade i zatvorene vitiˇcaste zagrade koja joj odgovara ne bude ve´ce od jednog ekrana. Obiˇcno se preporuˇcuje navodenje razmaka oko kljuˇcnih reˇci i oko binarnih operatora, izuzev. i ->. Ne preporuˇcuje se koriˇs´cenje razmaka kod poziva funkcija, unarnih operatora, izuzev operatora sizeof i operatora kastovanja. Ne preporuˇcuje se navodenje nepotrebnih zagrada, posebno u okviru povratne vrednosti. Na primer:
if (uslov) { *a = -b + c + sizeof (int) + f(x); return -1; }
Nazubljivanje teksta programa nebitno je kompilatoru, ali je skoro neophodno programeru. Nazublji- vanje naglaˇsava strukturu programa i olakˇsava njegovo razumevanje. Red programa moˇze biti uvuˇcen u odnosu na poˇcetnu kolonu za nekoliko blanko karaktera ili nekoliko tab karaktera. Tab karakter moˇze da se u okviru editora interpretira na razliˇcite naˇcine (tj. kao razliˇcit broj belina), te je preporuˇcljivo u programu sve tab karaktere zameniti razmacima (za ˇsta u ve´cini editora postoji mogu´cnost) i ˇcuvati ga u tom obliku. Na taj naˇcin, svako ´ce videti program (na ekranu ili odˇstampan) na isti naˇcin. Ne postoji kruto pravilo za broj karaktera za jedan nivo uvlaˇcenja. Neki programeri koriste 4, a neki 2 – sa motivacijom da u redovima od 80 karaktera moˇze da stane i kˆod sa dubokim nivoima. Torvalds, sa druge strane, preporuˇcuje broj 8, jer omogu´cava bolju preglednost. Za programe koji imaju viˇse od tri nivoa nazubljivanja, on kaˇze da su ionako sporni i zahtevaju prepravku.
2.3 Imenovanje promenljivih i funkcija
Imenovanje promenljivih i funkcija veoma je vaˇzno za razumljivost programa i sve je vaˇznije ˇsto je program duˇzi. Pravila imenovanja mogu da olakˇsaju i izbor novih imena tokom pisanja programa. Imena promenljivih i funkcija (pa i datoteka programa) treba da sugeriˇsu njihovu ulogu i tako olakˇsaju razumevanje programa. Globalne promenljive, strukture i funkcije treba da imaju opisna imena, potencijalno saˇcinjena od viˇse reˇci. U kamiljoj notaciji (popularnoj medu Java i C++ programerima), imena od viˇse reˇci zapisuju se tako ˇsto svaka nova reˇc (sem eventualno prve) poˇcinje velikim slovom, na primer, brojKlijenata. U notaciji sa podvlakama (popularnoj medu C programerima), sve reˇci imena se piˇsu malim slovima a reˇci su razdvojene podvlakama, na primer, broj_klijenata. Imena makroa i konstanti piˇsu se obiˇcno svim velikim slovima, a imena globalnih promenljivih poˇcinju velikim slovom. Lokalne promenljive, a posebno promenljive koje se koriste kao brojaˇci u petljama treba da imaju kratka i jednostavna, a ˇcesto najbolje, jednoslovna imena – jer se razumljivost lakˇse postiˇze saˇzetoˇs´cu.
(c >= ’0’ && c <= ’9’)
Zagrade, ˇcak i ako nisu neophodne, mogu da olakˇsaju ˇcitljivost. Prethodni primer moˇze da se zapiˇse na slede´ci naˇcin:
((c >= ’0’) && (c <= ’9’))
a naredba:
prestupna = g % 4 == 0 && g % 100 != 0 || g % 400 == 0;
moˇze ˇcitljivije da se napiˇse na slede´ci naˇcin:
prestupna = ((g%4 == 0) && (g % 100 != 0)) || (g % 400 == 0);
Razmak oko operatora ne mora da se piˇse i njegovo uklanjanje moˇze da naglasti prioritet i popravi ˇcitljivost. U navedenom primeru zato piˇse g%4 == 0 umesto g % 4 == 0. Suviˇse komplikovane izraze treba zameniti jednostavnijim i razumljivijim. Kernigen i Pajk navode primer:
x += (xp=(2*k < (n-m)? c[k+1] : d[k--]));
i bolju, jednostavniju varijantu:
if (2*k < n-m) *xp = c[k+1]; else *xp = d[k--]; *x += *xp;
Kernigen i Pajk navode i primer u kojem je mogu´ce i poˇzeljno pojednostaviti komplikovana izraˇcunavanja. Umesto:
subkey = subkey >> (bitoff - ((bitoff >> 3) << 3));
bolji je (ekvivalentan) kˆod:
subkey = subkey >> (bitoff & 0x7);
Zbog komplikovanih, a u nekim situacijama i nedefinisanih, pravila poretka izraˇcunavanja i dejstva sporednih efekata (kao, na primer, kod operatora inkrementiranja i dekrementiranja), dobro je pojed- nostaviti kˆod kako bi njegovo izvrˇsavanje bilo jednoznaˇcno i jasno. Na primer, umesto:
str[i++] = str[i++] = ’ ’;
bolje je:
str[i++] = ’ ’; str[i++] = ’ ’;
Pouˇcan je i slede´ci ˇcuveni primer: nakon dodele a[a[1]]=2;, element a[a[1]] nema nuˇzno vrednost 2 (ako je na poˇcetku vrednost a[1] bila jednaka 1, a vrednost a[2] razliˇcita od 2). Navedeni primer pokazuje da treba biti veoma oprezan sa koriˇs´cenjem indeksa niza koji su i sami elementi niza ili neki komplikovani izrazi.
2.5 Koriˇs´cenje idioma
Idiomi su ustaljene jeziˇcke konstrukcije koje predstavljaju celinu. Idiomi postoje u svim jezicima, pa i u programskim. Tipiˇcan idiom u jeziku C je slede´ci oblik for-petlje:
for (i = 0 ; i < n; i++) ...
Kernigen i Pajk zagovaraju koriˇs´cenje idioma gde god je to mogu´ce. Na primer, umesto varijanti
i=0; while (i <= n-1) a[i++] = 1.0;
for (i = 0; i<n; ) a[i++] = 1.0;
for (i = n; --i >= 0; ) a[i] = 1.0;
smatraju da je bolja varijanta:
for (i = 0 ; i < n; i++) a[i] = 1.0;
jer je najˇceˇs´ca i najprepoznatljivija. Staviˇˇ se, Kernigen i Pajk predlaˇzu, delom i ekstremno, da se, bez dobrog razloga i ne koristi nijedna forma for-petlji osim navedene, zatim slede´ce, za prolazak kroz listu (videti poglavlje 7.1):
for (p = list ; p != NULL ; p = p->next) ...
i slede´ce, za beskonaˇcnu petlju:
for (;;) ...
Glavni argument za koriˇs´cenje idioma je da se kˆod brzo razume a i da svaki drugi („neidiomski“) konstrukt privlaˇci dodatnu paˇznju ˇsto je dobro, jer se bagovi ˇceˇs´ce kriju u njima. Kao dodatne primere idioma, Kernigen i Pajk navode:
while ((c = getchar()) != EOF) ...
i
p = malloc(strlen(buf)+1); strcpy(p, buf);
2.6 Koriˇs´cenje makroa
Makroe sa argumentima obraduje pretprocesor i u fazi izvrˇsavanja, za razliku od funkcija, nema kreiranja stek okvira, prenosa argumenata i sliˇcno. Zbog uˇstede memorije i raˇcunarskog vremena, makroi sa argumentima su nekada smatrani poˇzeljnom alternativom funkcijama. Danas, u svetu mnogo brˇzih raˇcunara nego nekada, smatra se da loˇse strane makroa sa argumentima prevazilaze dobre strane i da makroe treba izbegavati. U loˇse strane makroa sa argumentima spada to ˇsto ih obraduje pretprocesor (a ne kompilator), nema provera tipova argumenata, debager ne moˇze da prati definiciju makroa i sliˇcno. Postoje i dodatne loˇse strane ilustrovane primerima u nastavku (od kojih su neki diskutovani i u prvom delu ove knjige, u poglavlju ??). Ukoliko se u tekstu zamene neki argument pojavljuje viˇse puta, postoji mogu´cnost da on bude izraˇcunat viˇse puta. Na primer, ukoliko postoji makro definicija: