




























































































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
Knjiga iz C-a.
Tipologija: Ispiti
1 / 141
Ova stranica nije vidljiva u pregledu
Ne propustite važne delove!





























































































Programski jezik C Predgovor
Od izdavanja "Programskog jezika C" 1978. godine, svijet računala doživio je veliki napredak. Veliki računalni sustavi postali su još snažniji, a osobna računala dobila su mogućnosti koje se do desetak godina nisu mogle nazrijeti. Za to vrijeme i sam C se mijenjao, mada neznatno, i razvijao sve dalje od svojih začetaka kao jezika UNIX operativnog sistema. Rastuća popularnost C-a, promjene u jeziku tokom godina i kreiranje prevoditelja od strane onih kojima nije bitan izgled, kombinirano je s potrebom za preciznijom i suvremenijom definicijom jezika od one koja je bila prezentirana u prvom izdanju ove knjige. Godine 1983. American National Standard Institute (ANSI) zasniva udrugu čija je svrha bila napraviti "nedvosmislenu i od računala nezavisnu definiciju C jezika". Rezultat svega je ANSI standard za C. Standard formalizira konstrukcije koje su bile najavljene, ali ne i opisane u prvom izdanju, kao što su dodjela strukture i nizovi dobiveni pobrojavanjem. Standard određuje novi način deklariranja funkcije koji omogućuje provjeru definicije u praksi. Određuje, također i standardnu biblioteku, s proširenim skupom funkcija za pripremu ulaza i izlaza, upravljanje memorijom, rad s nizovima i sl. Standard precizira vladanja atributa koji nisu bili u originalnoj definiciji i istovremeno jasno pokazuje koji su aspekti jezika ostali zavisni o računalu. Drugo izdanje "Programskog jezika C" opisuje C onako kako ga definira ANSI standard (Za vrijeme pisanja ove knjige, standard je bio u završnom stadiju usvajanja; očekivalo se da bude usvojen krajem 1988.god. Razlike između onoga što piše ovdje i konačne forme standarda su minimalne.). Iako smo naznačili mjesta na kojima se jezik proširio i razvio, odlučili smo ga ekskluzivno predstaviti u novom obliku. U velikom dijelu razlike su neznatne; najuočljivija izmjena je novi način deklaracije i definicije funkcije. Moderni prevoditelji već podržavaju najveći dio standarda. Pokušali smo zadržati suštinu prvog izdanja. C nije opširan jezik, pa ga nije potrebno opisivati opširnim knjigama. Doradili smo predstavljanje kritičnih faktora kao što su pokazivači, koji su suština C programiranja. Pročistili smo originalne primjere i dodali nove, u većini poglavlja. Na primjer, dio koji opisuje komplicirane deklaracije proširen je programima koji pretvaraju deklaracije u riječi i obratno. Kao i u prethodnom slučaju i ovdje su svi primjeri provjereni direktno iz teksta, prepoznatljivog računalu. Dodatak A, uputa za rad, nije standard već samo pokušaj prikazivanja njegove suštine u kraćem obliku. Poradi lakšeg razumijevanja vrlo je značajno da posebna uloga pripadne samom standardu. Dodatak B predstavlja pregled karakteristika standardne biblioteke. Ovo je bitna napomena za programera, a ne za korisnika. Dodatak C pruža kratak pregled izmjena u odnosu na originalno izdanje. Kao što smo rekli u predgovoru prvom izdanju, C je "korisniji što je veće iskustvo u radu s njim". Poslije desetogodišnjeg iskustva, to i dalje tvrdimo. Nadamo se da će vam ova knjiga pomoći da naučite i primijenite programski jezik C. Jako su nas zadužili prijatelji koji su pomogli pri radu na drugom izdanju. Jon Bentley, Doug Gwyn, Doug McIlroy, Peter Nelson i Rob Pike komentirali suoriginalni rukopis. Zahvaljujemo Alu Ahou, Dennisu Allisonu, Joeu Campbellu, G. R. Emlinu, Karen Fortgang, Allenu Holubu, Andrewu Humeu, Daveu Kristolu, Johnu Lindermanu, Davidu Prosseru, Gene Spafford i Chrisu Van Wyku na pažljivoj kontroli napisanog teksta. Svojim primjedbama pomogli su nam i Bill Chestwick, Mark Kernighan, Andy Koenig, Robin Lake, Tom London, Jim Reeds, Clovis Tondo i Peter Weinberger. Dave Prosser je detaljno odgovorio na mnoga pitanja glede ANSI standarda. Koristili smo Bjarne Stroustrupov C++ translator za lokalno testiranje naših programa, a Dave Kristol nam je nabavio C prevoditelj kojim su obavljena završna testiranja. Rich Drechsler nam je mnogo pomogao pri pisanju teksta na računalu. Svima im iskreno zahvaljujemo. Brian W. Kernighan Dennis M. Ritchie
Programski jezik C Uvod
C je programski jezik opće namjene. Tijesno je povezan s operativnim sistemom UNIX na kojemu je razvijen, jer su i sistem i većina programa koji rade na UNIX-u napisani baš u C-u. Jezik, ipak, nije vezan samo za jedan operativni sistem ili računalo; iako je nazvan "jezikom za sistemsko programiranje" zato što se koristi pri pisanju prevoditelja i operativnih sistema, podjednako se dobro koristi za programiranje u drugim područjima. Većina bitnih ideja C-a potječe od jezika BCPL koji je razvio Martin Richards. Utjecaj BCPL-a na C ostvaren je indirektno preko B jezika koji je 1970. napisao Ken Thompson za prvi UNIX sistem na DEC PDP-7 računalu. BCPL i B su jezici bez "tipova podataka". Nasuprot njemu, C nudi mnoštvo različitih tipova podataka. Osnovni tipovi su znaci, cjelobrojne vrijednosti i vrijednosti iz područja realnih brojeva (vrijednosti s pomičnim zarezom) u više veličina. Uz to postoji hijerarhija izvedenih tipova podataka kreiranih pokazivačima, poljima, strukturama i unijama. Izrazi se sastoje od operatora i operanda; bilo koji izraz, uključujući i dodjelu vrijednosti ili pozivanje funkcije, može biti naredba. Pokazivači omogućuju nezavisnu adresnu aritmetiku. C nudi osnovne konstrukcije za kontrolu toka koje traže dobro strukturirani programi: grupiranje naredbi, donošenje odluka (if-else), izbor (switch), petlje s uvjetima na početku (while) i na kraju (do), te izlaz iz petlje prije kraja (break). Funkcije mogu vraćati vrijednosti osnovnih tipova, struktura, unija ili pokazivača. Bilo koja funkcija može se rekurzivno pozivati. Lokalne varijable su tipično "automatske" (gube vrijednost pri izlasku iz funkcije) ili se kreiraju svakim novim pozivanjem. Definicije funkcija ne moraju se umetati, a varijable se mogu deklarirati u blokovima. Funkcije C programa mogu se nalaziti u različitim izvornim datotekama koje se posebno prevode. Varijable mogu biti unutrašnje, vanjske (za koje se zna samo unutar jedne izvorne datoteke) ili dostupne cijelom programu (globalne). Preprocesorska faza obavlja makrosupstitucije na izvornom tekstu programa, uključivanje ostalih izvornih datoteka i uvjetno prevođenje. C je jezik relativno "niskog nivoa". Ovakav epitet nije nedostatak, već govori da C radi s istim vrstama objekata s kojima rade i sama računala, a to su znakovi, brojevi i adrese. Ovi objekti se mogu kombinirati i premještati pomoću aritmetičkih i logičkih operatora kojima su opremljena postojeća računala. C ne radi direktno sa složenim objektima kao što su nizovi znakova, skupovi, liste ili matrice. Ne postoje operacije koje obrađuju cijelu matricu ili niz, iako strukture mogu biti kopirane kao jedinka. C ne definira ni jednu drugu mogućnost memoriranja lokacija osim statičke definicije i discipline stoga, koja je omogućena lokalnim varijablama funkcija; ovdje nema nagomilavanja ili skupljanja nebitnih elemenata. Na kraju, sam C ne nudi ulazno/izlazne olakšice; u njemu ne postoje READ ili WRITE stanja, te nema ugrađenih metoda za pristup datotekama. Svi ovi mehanizmi "višeg nivoa" moraju biti određeni funkcijama koje se zovu eksplicitno. Manje-više sve implementacije C-a imaju standardnu kolekciju takovih funkcija. Shodno tomu, C zapravo nudi samo jednoznačni kontrolni tok: uvjeta, petlji, grupiranja i potprograma, ali ne i multiprogramiranje, paralelne operacije ili sinkronizaciju. Iako nepostojanje neke od ovih karakteristika može izgledati kao ozbiljan nedostatak ("Znači da bih usporedio dva znakovna niza moram pozivati funkciju?"), održavanje jezika na umjerenoj razini ima svoju stvarnu korist. Pošto je C relativno mali jezik, dade se opisati na relativno malo prostora i naučiti brzo. Programer s punim pravom može očekivati lako učenje i razumijevanje korektne upotrebe cijelog jezika. Dugi niz godina, jedina definicija C-a je bio referentni priručnik prvog izdanja ove knjige. American National Standards Institute (ANSI) je 1983.god. osnovao udrugu koja se skrbila za modernu i cjelovitu definiciju C-a. Očekuje se da će ANSI standard, ili ANSI C biti odobren u 1988.god. (odobren je op.prev). Sve karakteristike standarda već su podržane preko novih prevoditelja. Standard se bazira na originalnom referentnom priručniku. Jezik je razmjerno malo mijenjan; jedan od ciljeva standarda je bio osigurati da većina postojećih programa ostane primjenjiva, ili, ako to ne uspije, prevoditelji moraju dati upozorenje o drugačijem načinu rada. Za većinu programera, najbitnija promjena je u novoj sintaksi za deklariranje i definiranje funkcija. Deklaracija funkcije može sada imati opis argumenata funkcije; sintaksa definicije je na određen način izmijenjena. Ova dodatna informacija pomaže da prevoditelji mnogo lakše otkrivaju greške nastale neslaganjem argumenata; po našem iskustvu, to je vrlo koristan dodatak jeziku. Postoje i još neke, manje izmjene. Dodjela struktura i nizova dobivenih pobrojavanjem, koji su se naširoko primjenjivali, postali su i zvanično dio jezika. Izračunavanje realnih brojeva može se obaviti i jednostrukom točnošću. Aritmetička svojstva, posebice za neoznačene tipove su razjašnjena. Preprocesor je savršeniji. Većina od ovih promjena ipak neće previše zanimati programere. Drugi značajan doprinos standarda je definicija biblioteke koja prati C jezik. Ona određuje funkcije za pristup operativnom sistemu (npr. za čitanje i pisanje datoteka), formatira ulaz i izlaz, određuje položaj u memoriji, radi s nizovima i sl. Zbirka standardnih zaglavlja osigurava jednoznačan pristup deklaracijama
Programski jezik C Uvod
funkcija i tipovima podataka. Da bi mogli biti u vezi sa osnovnim sistemom, programi što koriste ovu biblioteku su zasigurno kompatibilni i portabilni. Mnoge biblioteke su vrlo slične modelu standardne ulaz/izlaz biblioteke UNIX sistema. Ova biblioteka je opisana u prvom izdanju i masovno je bila u upotrebi na drugim sistemima. Kažimo ipak, još jednom, da mnogi programeri neće uočiti bitnije izmjene. Zbog toga što tipove podataka i kontrolnih struktura određenih C-om podržava veliki broj računala, radna biblioteka koja je snabdjevena vlastitim programima jest mala. Jedino se funkcije iz standardne biblioteke pozivaju eksplicitno, a i one se mogu zaobići. Te funkcije daju se napisati u C-u, te prenijeti s računala na računalo, osim onih koje neposredno oslikavaju konkretno računalo na kojemu se radi i njegov operativni sistem. Iako C odgovara mogućnostima većine računala, on je nezavisan od konkretne arhitekture računalskog sustava. Sa malo pažnje lako je napisati prenosive programe, što znači programe koje možemo pokrenuti bez zahtjeva za sklopovskim promjenama. Standard čini sve aspekte prenosivosti eksplicitnim, a propisuje i skup konstanti koje karakteriziraju računalo na kojem se program vrti. C nije strogo tipiziran, ali kako se razvijao, njegova kontrola tipova je jačala. Originalna definicija C-a ne odobrava, ali dopušta zamjenu pokazivača i cijelih brojeva; nakon dužeg razdoblja i to je riješeno, i standard zahtjeva točne deklaracije i jasne konverzije koje su činili dobri prevoditelji. Nove deklaracije funkcija su slijedeći korak u tom smjeru. Prevoditelji će upozoriti na većinu tipskih grešaka, mada ne postoji automatsko pretvaranje neusuglašenih tipova podataka. C ipak zadržava osnovnu filozofiju koju programeri poznaju; on samo zahtjeva jasno definiranje ciljeva. C, kao i svaki drugi jezik ima svoje nedostatke. Neki operatori imaju pogrešan prioritet; neki dijelovi sintakse mogli bi biti bolji. Pored svega, C se dokazao kao jedan od najkorisnijih i najsadržajnijih za veliki broj različitih aplikacija. Knjiga je organizirana na slijedeći način: Poglavlje 1 je udžbenik osnovnih svojstava C-a. Prvotna namjera bila je pripremanje čitatelja za početak rada u najkraćem mogućem roku, jer vjerujemo da je najbolji način učenja jezika pisanje različitih programa u njemu. Baza podrazumijeva znanje stečeno radom s osnovnim elementima programiranja; ovdje nema pojašnjavanja u vezi s računalom, prevođenjem, ali ni značenja izraza kao što je n=n+1. Iako smo pokušali da, gdje god je takvo što bilo moguće, prikažemo korisne programske tehnike, knjiga nema namjeru biti udžbenik za strukture podataka i algoritme; kad god smo bili prinuđeni birati, usredotočili bismo se na jezik. Poglavlja 2 i 6 razmatraju različite aspekte jezika C s više detalja, te mnogo formalnije negoli Pogavlje 1, iako je naglasak još uvijek na cjelovitim programskim primjerima, a ne na izoliranim fragmentima. Poglavlje 2 bavi se osnovnim tipovima podataka, operatorima i izrazima. Poglavlje 3 obrađuje kontrolu toka: if-else, switch, while, for itd. Poglavlje 4 pokriva funkcije i programske strukture - vanjske varijable, pravila područja, umnožene izvorne datoteke, itd., a također se dotiče i preprocesora. Poglavlje 5 bavi se pokazivačima i aritmetičkim adresama. Poglavlje 6 govori o strukturama i unijama. Poglavlje 7 opisuje standardnu biblioteku, koja oprema operativni sistem jednostavnim interface-om. Ova biblioteka je definirana ANSI standardom kojeg podržava C program na svakom računalu, pa se programi koji ga koriste za ulaz, izlaz i pristup drugom operativnom sistemu mogu prenijeti s sistema na sistem bez izmjena. Poglavlje 8 opisuje vezu između C programa i operativnog sistema UNIX, usredotočivši se na ulaz/izlaz, sistem datoteka i raspodjelu memorije. Iako je dio ovog poglavlja specifičan za UNIX sisteme, programeri koji koriste ostale sisteme još uvijek mogu naći koristan materijal, uključujući i neke detaljnije uvide u to kako je jedna verzija standardne biblioteke opremljena, te primjedbe o prenosivosti. Dodatak A sadrži referentni priručnik. Zvanični prikaz sintakse i semantike C-a je sam ANSI standard. Ovaj dokument je najprije namijenjen piscima prevoditelja. Referentni priručnik ovdje prikazuje definiciju jezika konciznije, a ne na uobičajeni, klasičan način. Dodatak B daje sadržaj standardne biblioteke, koji je potrebniji za korisnike programa nego za one koji ih prave. Dodatak C daje kratak pregled izmjena originalnog jezika. U slučaju sumnje, pak, standard i vlastiti prevoditelj ostaju najkompetentniji autoriteti za jezik.
Programski jezik C Osnovne napomene
funkcijama imena po želji, ali ime "main" ima specijalnu namjenu - program se izvršava od početka funkcije main. Ovo znači da svaki program mora imati "main" negdje. Funkcija main će obično pozivati ostale funkcije da omoguće njeno odvijanje, i to neke koje ste vi napisali, a druge iz ponuđenih biblioteka. Prva linija programa,
#include <stdio.h>
govori računalu da uključi informacije o standardnoj ulazno/izlaznoj biblioteci; ova linija se pojavljuje na početku mnogih C izvornih datoteka. Standardna biblioteka je opisana u Poglavlju 7 i Dodatku B. Jedan način razmjene podataka između funkcija jest određivanje funkcijske liste vrijednosti, koje se zovu argumentima pozivne funkcije. U ovom slučaju main je definirana kao funkcija koja ne očekuje nikakve argumente, što je predstavljeno praznom listom (). Naredbe funkcije su ograđene velikim zagradama {}. Funkcija main ima samo jednu naredbu
printf("Hello, World\n");
Funkcija se poziva njenim imenom, a popraćena je listom argumenata u zagradi. Tako, pozivamo funkciju printf argumentom "Hello, World\n". Funkcija printf je iz biblioteke koja ispisuje izlaz, u ovom slučaju niz znakova između navodnika. Niz znakova između dvostrukih navodnika, kao što je "Hello, World\n", zove se znakovni niz. U početku ćemo koristiti znakovne nizove samo kao argumente za printf i ostale funkcije. Dio \n u nizu je oznaka u C-u za znak novog reda, koji kada se ispisuje pomiče ispis do kraja ulijevo na slijedećoj liniji. Ako izostavite \n (potencijalno koristan eksperiment, a nikako štetan), uočit ćete da nakon ispisa izlaza ne postoji više nijedna linija. Morate upotrijebiti \n da bi se znak novog reda priključio printf argumentu; ako napišete nešto kao
printf("Hello, World ");
C prevoditelj će prijaviti grešku. Nikada printf ne određuje novu liniju automatski, već se iz višestrukih pozivanja može postupno formirati izlazna linija. Naš prvi program mogao je biti ovako napisan
#include <stdio.h> main(){ printf("Hello, "); printf("World"); printf("\n"); }
da bi načinio isti izlaz. Primijetimo da \n predstavlja samo jedan znak. Niz kakav je \n osigurava opći mehanizam za predstavljanje znakova koje nazivamo specijalnima (zato što su nevidljivi ili se ne daju otipkati). Između ostalih nizova koje C definira su \t za tabulator, \b za povratnik (backspace), " za dvostruke navodnike i \ za obrnutu kosu crtu (slash). Potpuna lista postoji u dijelu pod naslovom 2.3.
Vježba 1-1. Pokrenite "Hello, World" program na vašem računalu. Igrajte se s izostavljanjem dijelova koda, samo radi poruka o greškama koje ćete dobivati.
Vježba 1-2. Što se događa kad niz argumenata za printf sadrži \c, gdje je c neki znak koji nije na listi navedenoj gore?
Slijedeći program koristi formulu oC=(5/9)( oF-32) da ispiše tablicu temperatura u Fahrenheitovim stupnjevima i njihove ekvivalente u Celsiusovim:
0 - 20 - 40 4 60 15
Programski jezik C Osnovne napomene
Program se sastoji od definicije funkcije pod imenom main. On je duži od prvog, koji ispisuje "Hello, World", ali ne i složeniji. Program uvodi dosta novih ideja, uključujući komentare, deklaracije, varijable, aritmetičke izraze, petlje i formatirani izlaz.
#include <stdio.h>
/* Ispiši Fahrenheit-Celsius tablicu za fahr=0, 20, 40, ..., 300 / main(){ int fahr, celsius; int lower, upper, step; lower=0; / donja granica tablice tempreratura / upper=300; / gornja granica / step=20; fahr=lower; while(fahr<=upper){ celsius=5(fahr-32)/9; printf("%d\t%d\n", fahr, celsius); fahr=fahr+step; } }
Linija
/* Ispiši Fahrenheit-Celsius tablicu za fahr=0, 20, 40, ..., 300 */
predstavlja komentar, koji u prikazanom primjeru pojašnjava što program točno radi. Bilo koje znakove između /* i */ prevoditelj ne prepoznaje; oni se slobodno mogu upotrebljavati kako bi se program lakše razumijevao. Komentari može imati i razmak (pusti znak?!), tabulator ili znak nove linije. U C-u sve varijable moraju biti prijavljene prije upotrebe, najčešće na početku funkcije, prije bilo koje izvršne naredbe. Deklaracija prijavljuje osobine varijabli; ona se sastoji iz imena tipa i liste varijabli kao npr.:
int fahr, celsius; int lower, upper, step;
Tip int znači da su navedene varijable cijeli brojevi, za razliku od funkcije float, koja označava realne brojeve, tj. brojeve koji mogu imati decimalni dio. Opseg obaju tipova, i int i float, zavisi o mogućnostima računala na kojemu radite: 16-bitni int brojevi imaju opseg [-32768 - 32767], a jednostavni su kao i 32-bitni. Tipična dužina float broja je 32 bita, sa posljednjih šest značajnih znamenki i najčešćom vrijednošću između [10-38 - 10+38]. C nudi i druge osnovne tipove podataka pored int i float tipa, uključujući:
char znakovni tip dužine jednog okteta short kraći oblik cijelog broja long duži oblik cijelog broja double realan broj s dvostrukom točnošću
Veličine ovih objekata isto tako variraju od računala do računala. Tu su i polja, strukture i unije ovih osnovnih tipova, pokazivači na njih, te funkcije koje ih vraćaju, a koje ćemo tek upoznati.
Programski jezik C Osnovne napomene
printf("%3d %6d\n", fahr, celsius);
da bi se ispisao prvi broj svake linije u polju širokom tri znamenke, te drugi u polju širokom šest znamenki, kao:
0 - 20 - 40 4 60 15 80 26 100 37 ...
Ozbiljniji problem nalazi se u činjenici da koristimo aritmetiku cijelih brojeva, pa Celsiusove temperature nisu baš egzaktne; npr., 0 oF jest zapravo -17.8oC, a ne -17. U cilju rješavanja ovakovih problema, potrebno je upotrebljavati aritmetiku realnih brojeva (brojevi s pokretnom decimalnom točkom) umjesto cijelih. To zahtjeva određene izmjene u programu. Postoji i druga verzija:
#include <stdio.h> /* Ispiši Fahrenheit-Celsius tablicu za fahr=0, 20, 40, ..., 300 / main(){ float fahr, celsius; int lower, upper, step; lower=0; / donja granica tablice tempreratura / upper=300; / gornja granica / step=20; fahr=lower; while(fahr<=upper){ celsius=(5.0/9.0)(fahr-32); printf("%3.0f %6.1f\n", fahr, celsius); fahr=fahr+step; } }
Ova verzija je jako slična prethodnoj, osim što su varijable fahr i celsius tipa float, a izraz za konverziju napisan prirodnije. Nismo bili u prilici koristiti 5/9 u ranijoj verziji, jer bi dijeljenje cijelih brojeva dalo vrijednost nula. Decimalna točka u konstanti kaže da je to realan broj, pa 5.0/9.0 ne daje nulu jer je to odnos dviju realnih veličina. Ako aritmetički operator ima cjelobrojne operande, obavlja se operacija sa cijelim brojevima. Ako aritmetički operator ima jedan realan broj kao operand, a cijeli broj kao drugi, cijeli će se broj pretvoriti u realni da bi se operacija izvela kako treba. Ako napišemo fahr-32, 32 će biti pretvoreno u realan broj. Ipak, blaga je preporuka da se realni brojevi pišu s eksplicitnim decimalnim točkama čak i kad imaju vrijednosti kao cjelobrojni, jer tako naglašavamo njihovu realnu prirodu. Detaljnija pravila o pretvaranju cjelobrojnih varijabli u one s pomičnim zarezom dani su u Poglavlju 2. Za sada primijetimo da naredba
fahr=lower;
i uvjet
while(fahr<=upper)
djeluje na isti način - int se pretvara u float prije negoli se obavi operacija. Parametar printf pretvorbe %3.0f nam kaže da realni broj (u ovom slučaju fahr) treba biti ispisan sa zauzećem najviše tri znaka, bez decimalne točke i decimalnih znamenki. Parametar %6.1 opisuje drugi broj (celsius) koji treba biti ispisan u najviše šest znakova, s jednom znamenkom iza decimalne točke. Izlaz izgleda ovako:
0 -17. 20 -6.
Programski jezik C Osnovne napomene
Širina i točnost ne moraju biti navedene: %6f kaže da broj mora biti širok najviše šest znakova; %.2f određuje dva znaka iza decimalne točke, ali širina nije naglašena; %f jedino kazuje kako se radi o broju s pomičnom decimalnom točkom.
%d ispiši kao decimalni cijeli broj %6d ispiši kao decimalni cijeli broj, širok najviše šest znakova %f ispiši kao realan broj %6f ispiši kao realan broj, širok najviše šest znakova %.2f ispiši kao realan broj, sa dva znaka iza decimalne točke %6.2f ispiši kao realan broj, širok najviše šest znakova, sa dva znaka iza decimalne točke
Između ostalog, printf prepoznaje %0 kao oktalni broj, %x kao heksadecimalni, %c kao znak, %s kao niz znakova i %% kao %.
Vježba 1-3. Izmijenite program pretvorbe temperature da ispisuje zaglavlje iznad tablice.
Vježba 1-4. Napišite odgovarajući program za ispis tablice pretvorbe Celsiusovih u Fahrenheitove stupnjeve.
Mnogo je različitih načina na koje se dade napisati program određene namjene. Pokušat ćemo napraviti promjenu na pretvaraču temperature
#include <stdio.h> /* ispiši Fahrenheit-Celsius tablicu / main(){ int fahr; for(fahr=0;fahr<=300;fahr=fahr+20) printf("%3d %6.1f\n", fahr, (5.0/9.0)(fahr-32)); }
Ovo daje iste rezultate, ali svakako izgleda drugačije. Najbitnija izmjena je eliminacija najvećeg broja varijabli; ostaje samo fahr, koja postaje int. Donje i gornje granice i korak pojavljuju se samo kao konstante u for petlji, koji je konstruiran drugačije, a izraz koji izračunava temperaturu u Celsiusovim stupnjevima se sada javlja kao treći argument u printf funkciji umjesto kao posebna naredba dodjele. Ova zadnja izmjena je primjer općeg pravila - u bilo kojem kontekstu gdje je dopušteno koristiti vrijednost varijable određenog tipa, možete upotrijebiti kompliciraniji izraz tog tipa. Kako treći argument funkcije printf mora biti realna vrijednost, tu možemo umetnuti bilo koji realan izraz. Naredba for je petlja koja predstavlja generalizirani while oblik. Ako ih usporedimo uočit ćemo da for ima efikasnije djelovanje. U zagradama , postoje tri dijela odvojena točka-zarezom. Prvi dio, početak
fahr=
se izvrši jednom, prije negoli se uđe u petlju. Drugi dio je uvjet koji kontrolira petlju:
fahr<=
Taj uvjet se izračunava; u slučaju da je istinit, tijelo petlje (u ovom slučaju printf) se izvršava. Zatim se varijabla iz uvjeta inkrementira (povećava) za korak
fahr=fahr+
a uvjet se ponovo računa. Petlja se završava ako je uvjet postao lažan. Kao i kod while, tijelo petlje može biti jedna naredba ili grupa naredbi uokvirena vitičastim zagradama. Inicijalizacija, uvjet i inkrementacija mogu biti bilo koji izrazi.
Programski jezik C Osnovne napomene
1.5.1 Kopiranje datoteka
Pomoću getchar-a i putchar-a, možete napisati uistinu mnogo korisnog koda bez većeg znanja o ulazu i izlazu. Najprostiji primjer je program koji kopira ulaz na izlaz, znak po znak:
pročitaj a znak while(znak nije indikator kraja datoteke) ispiši upravo pročitani znak pročitaj a znak
Pretvorivši ovo u C kod, dobije se
#include <stdio.h> /* kopiranje ulaza na izlaz; prva verzija */ main(){ int c; c=getchar(); while(c!=EOF){ putchar(c); c=getchar(); } }
Relacijski operator != znači "nije jednako". Ono što se pojavljuje na tipkovnici ili ekranu je, naravno, smješteno kao niz bitova. Tip char je, kako mu ime kaže, namijenjen za spremanje podataka znakovnog tipa, ali može biti uzeta i bilo koja vrijednost cjelobrojnog tipa. Ovdje koristimo int iz značajnog razloga. Problem koji bi se nametnuo jest razlikovanje znaka kraja ulaza od normalnih podataka. Funkcija getchar radi tako da uzima podatke dok na ulaz ne stigne znak za kraj ulaza (datoteke), vrijednost koja ne može biti nijedan drugi znak. Ova vrijednost se naziva EOF, zbog "End Of File". U tom pravcu, moramo prikazati varijablu c kao tip dovoljno velik da prihvati bilo koju vrijednost koju mu getchar vrati. Ne možemo koristiti char, jer c mora moći dohvatiti EOF i sve moguće znakove. Stoga koristimo int. EOF je cjelobrojna vrijednost definirana u <stdio.h>, ali se razlikuju od sistema do sistema. Koristeći simboličku konstantu, sigurni smo da ništa u programu ne zavisi od neke specifične numeričke vrijednosti. Program za kopiranje mogli bi ljepše napisati iskusniji programeri C-a. U C-u, bilo koja dodjela vrijednosti, kao
c=getchar()
je izraz koji ima vrijednost, a to je vrijednost lijeve strane nakon dodjele. To zapravo znači da se dodjela vrijednosti može pojaviti kao dio većeg izraza. Ako dodjelu znaka u c preselimo unutar zagrade za provjeru while petlje, program se dade napisati ovako:
#include <stdio.h> /* kopiranje ulaza na izlaz; druga verzija */ main(){ int c; while((c=getchar())!=EOF) putchar(c); }
while dobije znak, prenosi ga u c, a zatim provjerava da li je znak bio signal kraja datoteke. Ako nije, tijelo petlje se izvršava, ispisujući znak. Tada se cijela priča ponavlja. Kad se napokon dosegne kraj ulaza, while se završava, a onda i main. Ova verzija koncentrira ulaz na samo jednu referencu za getchar, pa je program kompaktniji i manji. Vi ćete se često susretati s ovim stilom pisanja (moguće je pisati i nečitljiv kod, ali to je ipak tendencija koja nije poželjna). Zagrade oko naredbe dodjele unutar uvjeta su neizostavne. Prioritet != je veći nego od =, što znači da će bez zagrada relacijski uvjet != biti odrađen prije dodjele. Tako je izraz
c=getchar()!=EOF
Programski jezik C Osnovne napomene
potpuno ekvivalentan sa
c=(getchar()!=EOF)
To ima neželjeni efekt u postavljanju c na 0 ili 1, zavisno od toga da li je getchar naišao na kraj datoteke (više o ovoj temi u Poglavlju 2.)
Vježba 1-6. Provjeriti vrijednost izraza getchar()!=EOF.
Vježba 1-7. Napišite program za ispis vrijednosti EOF-a.
1.5.2 Brojanje znakova
Slijedeći program broji znakove, slično programu kopiranja
#include <stdio.h> /* brojanje znakova na ulazu; prva verzija */ main(){ long nc; nc=0; while(getchar()!=EOF) nc++; printf("%ld\n", nc); }
Izraz
++nc;
uvodi novi operator, ++, čije je značenje "povećaj za jedan". Možemo, dakle, umjesto nc=nc+1 pisati nc++ što je konciznije i vrlo često mnogo efikasnije. Postoji i odgovarajući operator za smanjivanje, --. Operatori ++ i -- mogu biti i prefiks (++nc) i sufiks operatori (nc++); ove dvije formulacije mogu dati različite vrijednosti u izrazima, kao što će biti obrađeno u Poglavlju 2, iako i jedna i druga povećavaju nc za jedan. Za ovaj trenutak ćemo se posvetiti prefiks formulaciji. Program za brojanje znakova sumira svoje rezultate u varijabli tipa long umjesto u int. Cjelobrojne vrijednosti long imaju barem 32 bita. Iako su na nekim računalima int i long iste veličine, na drugim int ima 16 bitova, s maksimalnom vrijednošću od 32767 što je relativno mala gornja granica za brojač. Specifikacija pretvorbe %ld kaže printf-u da je odgovarajući argument long cjelobrojna vrijednost. Moguće je, dakako, operirati i s većim brojevima ako se upotrijebi double (float s dvostrukom točnošću). Koristit ćemo for petlju umjesto while radi ilustracije drugačijeg pisanja petlje.
#include <stdio.h> /* brojanje znakova na ulazu; druga verzija */ main(){ double nc; for(nc=0; getchar()!=EOF; ++nc) ; printf("%.0f", nc); }
Funkcija printf koristi %f za ispis i float i double brojeva; %.0f ne dopušta ispis decimalne točke i decimala. Tijelo ove for petlje je prazno, zato što se cijeli posao napravi u dijelu za provjeru uvjeta i uvećavanje. Međutim, gramatika C jezika zahtjeva da for naredba ima tijelo. Zasebna točka-zarez, nazvana nultom naredbom, tu je da bi udovoljila tom zahtjevu. Mi smo je umetnuli u posebnu liniju kako bi bila uočljivija. Prije nego programčić za brojanje znakova prepustimo ropotarnici povijesti, zamijetimo da ako ulazni niz nema nijednog znaka, while i for provjere jave grešku pri prvom pozivanju funkcije getchar, a program daje nulu što je ispravan odgovor. Ovo je bitno. Jedna od komparativnih prednosti ovih petlji jest činjenica da one testiraju uvjete na vrhu petlje, prije ulaska u tijelo. Programi bi se trebali inteligentno ponašati kad im se zada ulaz nulte duljine. Naredbe while i for omogućuju dobar rad programima i za te granične slučajeve.
Programski jezik C Osnovne napomene
++nl; if(c==' '||c=='\n'||c=='\t') state=OUT; else if(state==OUT){ state=IN; ++nw; } } printf("%d %d %d\n", nl, nw, nc); }
Pri svakom susretu s prvim znakom u riječi, on odbroji jednu riječ više. Varijabla state kaže da li je program trenutno unutar riječi ili ne; njeno početno stanje je OUT, odnosno "van riječi". Simboličke konstante IN i OUT nam više odgovaraju negoli brojčane vrijednosti 0 i 1, jer, kako smo već spomenuli, čine program čitljivijim. U ovako malom programu to nam nije odviše bitno, ali u većim programima, povećanje jasnoće programa je vrijedno dodatnog napora da se program napiše od početka u tom stilu. Vi ćete isto tako zaključiti da je lakše mijenjati programe gdje se brojevi pojavljuju kao simboličke konstante. Linija
nl=nw=nc=0;
postavlja sve tri varijable na nulu. To nije nikakva iznimka, već posljedica činjenice da je dodjeljivanje izraz koji ima vrijednost i obavlja se zdesna nalijevo. Umjesto toga mogli smo pisati
nl=(nw=(nc=0));
Operator || znači ILI (OR), pa linija
if(c==' '||c=='\n'||c=='\t')
znači "ako je c razmak ili znak za novi red ili tabulator" (prisjetimo se \t oznaka za tabulator). Postoji odgovarajući operator && za I (AND); njegov je prioritet veći nego za ||. Izrazi vezani s && ili || računaju se slijeva nadesno (!!!!), a garantira se prekid računanja čim bude poznata točnost ili netočnost. Tako, ako je c razmak, nije potrebno ispitivati da li je znak za novu liniju i tabulator, pa se ti testovi preskaču. To nije previše bitno za prikazani slučaj, ali jest za kompliciranije slučajeve što ćemo ubrzo vidjeti. Primjer također pokazuje else, naredbu koja obavlja "nešto drugo" ako je uvjetni dio if naredbe neistinit. Opća forma je
if(izraz) naredba else naredba
Izvodi se jedna i samo jedna naredba od dviju pridruženih if-else konstrukciji. Ako je izraz istinit, izvodi se naredba1; ako nije izvodi se naredba2. Svaka naredba može biti jedna ili više njih u vitičastim zagradama. U programu za brojanje riječi, nakon else imamo if koji kontrolira dvije naredbe unutar vitičastih zagrada.
Vježba 1-11. Kako biste provjerili program za brojanje riječi? Koje su vrste ulaznih podataka najpogodnije za otkrivanje netočnosti ako postoje?
Vježba 1-12. Napišite program koji ispisuje svoj ulaz tako da bude po jedna riječ u svakoj liniji.
Napišimo program koji zbraja pojavljivanja svake znamenke, specijalnih znakova (razmaci, tabulatori i sl.) i svih drugih znakova. To nije upotrebljiv primjer, ali nam omogućuje ilustraciju višeznačnu snagu C-a u jednom programu. Postoji dvanaest kategorija ulaza, pa je pogodnije koristiti jedno polje koje sadrži broj pojavljivanja svake znamenke, nego deset posebnih varijabli. Evo jedne verzije programa:
Programski jezik C Osnovne napomene
#include <stdio.h> /* zbraja znamenke, specijalne znakove i sve druge */ main(){ int c, i, nwhite, nother; int ndigit[10]; nwhite=nother=0; for(i=0;i<10;++i) ndigit[i]=0; while((c=getchar())!=EOF) if(c>='0'&&c<='9') ++ndigit[c-'0']; else if(c==' '||c=='\n'||c=='\t') ++nwhite; else ++nother; printf("znamenke="); for(i=0;i<10;++i) printf(" %d", ndigit[i]); printf(", specijalni znakovi=%d, ostalo=%d\n", nwhite, nother); }
Izlaz gore napisanog izvornog koda programa je
znamenke=9 3 0 0 0 0 0 0 0 1, specijalni znakovi=123, ostalo=
Deklaracija
int ndigit[10];
proglašava ndigit poljem od 10 cijelih brojeva. Indeksi polja uvijek počinju od nule u C-u, pa su elementi polja ndigit[0], ndigit[1], ..., ndigit[9]. To odražava u for petljama, koje inicijaliziraju i ispisuju polje. Indeks može biti bilo koji cjelobrojni izraz, koji ima cjelobrojnu vrijednost, kao i u našem programu, te cjelobrojne konstante. Ovaj program se isto tako zasniva na znakovnom predstavljanju brojeva. Npr. uvjet
if(c>='0'&&c<='9')
određuje da li je znak c znamenka. Ako jest, brojčana vrijednost te znamenke je
c-'0'
To radi samo ako '0', '1', ... , '9' imaju dosljedno rastuće vrijednosti. Srećom, to vrijedi za sve skupove znakova. Po definiciji, char su vrlo mali cijeli brojevi, pa su char varijable i konstante istovrsne int varijablama i konstantama u aritmetičkim izrazima. To je prirodno i korisno; primjerice, c-'0' je cjelobrojni izraz s vrijednošću između 0 i 9 ovisno o znaku '0' do '9', pohranjenom u c, te je to indeks za polje ndigit. Odluka o tipu znaka (znamenka, specijalni znak ili nešto drugo) donosi se u slijedećem dijelu programa
if(c>='0'&&c<='9') ++ndigit[c-'0']; else if(c==' '||c=='\n'||c=='\t') ++nwhite; else ++nother;