Programski jezik C

predavanja
ak. g. 2003/04.
M. Jurak
Sadrˇzaj
1 Uvod 8
1.1 Programski jezici . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2 Osnove pisanja programa u Unix okruˇzenju . . . . . . . . . . 10
1.2.1 Editor teksta . . . . . . . . . . . . . . . . . . . . . . . 10
1.2.2 Compiler . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2.3 man . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.2.4 Debugger . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.2.5 Linker . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.2.6 make . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.2.7 Programske biblioteke . . . . . . . . . . . . . . . . . . 12
2 Osnove programskog jezika C 13
2.1 Prvi program . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.2 Varijable. while petlja . . . . . . . . . . . . . . . . . . . . . . 16
2.3 for petlja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.4 if naredba . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.5 Funkcije . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.5.1 Funkcija main . . . . . . . . . . . . . . . . . . . . . . . 30
2.5.2 Prototip funkcije . . . . . . . . . . . . . . . . . . . . . 30
2.6 Polja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.6.1 <math.h> . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.7 Pokazivaˇci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.8 Polja znakova . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3 Konstante i varijable 46
3.1 Znakovi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
3.2 Komentari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
3.3 Identifikatori i kljuˇcne rijeˇci . . . . . . . . . . . . . . . . . . . 47
3.4 Osnovni tipovi podataka . . . . . . . . . . . . . . . . . . . . . 49
3.4.1 Cjelobrojni podaci . . . . . . . . . . . . . . . . . . . . 49
3.4.2 Znakovni podaci . . . . . . . . . . . . . . . . . . . . . 52
2
SADR
ˇ
ZAJ 3
3.4.3 Logiˇcki podaci . . . . . . . . . . . . . . . . . . . . . . . 53
3.4.4 Realni podaci . . . . . . . . . . . . . . . . . . . . . . . 53
3.4.5 Kompleksni podaci . . . . . . . . . . . . . . . . . . . . 55
3.5 Varijable i deklaracije . . . . . . . . . . . . . . . . . . . . . . . 55
3.5.1 Polja . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.5.2 Pokazivaˇci . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.6 Konstante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.7 Inicijalizacija varijabli . . . . . . . . . . . . . . . . . . . . . . 63
3.8 Enumeracije . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4 Operatori i izrazi 66
4.1 Aritmetiˇcki operatori . . . . . . . . . . . . . . . . . . . . . . . 66
4.1.1 Konverzije . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.1.2 Prioriteti i asocijativnost . . . . . . . . . . . . . . . . . 68
4.2 Unarni operatori . . . . . . . . . . . . . . . . . . . . . . . . . 69
4.2.1 Inkrementiranje, dekrementiranje . . . . . . . . . . . . 70
4.2.2 sizeof operator . . . . . . . . . . . . . . . . . . . . . . 71
4.3 Relacijski i logiˇcki operatori . . . . . . . . . . . . . . . . . . . 72
4.4 Operatori pridruˇzivanja . . . . . . . . . . . . . . . . . . . . . . 74
4.5 Uvjetni operator : ? . . . . . . . . . . . . . . . . . . . . . . 75
4.6 Prioriteti i redoslijed izraˇcunavanja . . . . . . . . . . . . . . . 76
5 Ulaz i izlaz podataka 78
5.1 Funkcije getchar i putchar . . . . . . . . . . . . . . . . . . . 78
5.2 Funkcije iz datoteke <ctype.h> . . . . . . . . . . . . . . . . . 80
5.3 Funkcije gets i puts . . . . . . . . . . . . . . . . . . . . . . . 81
5.4 Funkcija scanf . . . . . . . . . . . . . . . . . . . . . . . . . . 82
5.4.1 Uˇcitavanje cijelih brojeva . . . . . . . . . . . . . . . . . 83
5.4.2 Uˇcitavanje realnih brojeva . . . . . . . . . . . . . . . . 85
5.4.3 Drugi znakovi u kontrolnom nizu . . . . . . . . . . . . 85
5.4.4 Uˇcitavanje znakovnih nizova . . . . . . . . . . . . . . . 86
5.4.5 Prefiks * . . . . . . . . . . . . . . . . . . . . . . . . . 87
5.4.6 Maksimalna ˇsirina polja . . . . . . . . . . . . . . . . . 88
5.4.7 Povratna vrijednost . . . . . . . . . . . . . . . . . . . . 89
5.5 Funkcija printf . . . . . . . . . . . . . . . . . . . . . . . . . . 90
5.5.1 Ispis cijelih brojeva . . . . . . . . . . . . . . . . . . . . 91
5.5.2 Ispis realnih brojeva . . . . . . . . . . . . . . . . . . . 92
5.5.3
ˇ
Sirina i preciznost . . . . . . . . . . . . . . . . . . . . . 93
5.5.4 Ispis znakovnih nizova . . . . . . . . . . . . . . . . . . 94
5.5.5 Zastavice . . . . . . . . . . . . . . . . . . . . . . . . . 95
4 SADR
ˇ
ZAJ
6 Kontrola toka programa 96
6.1 Izrazi i naredbe . . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.1.1 Izrazi . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.1.2 Naredbe . . . . . . . . . . . . . . . . . . . . . . . . . . 97
6.1.3 Sloˇzene naredbe . . . . . . . . . . . . . . . . . . . . . . 98
6.1.4 Popratne pojave i sekvencijske toˇcke . . . . . . . . . . 98
6.2 Naredbe uvjetnog grananja . . . . . . . . . . . . . . . . . . . . 100
6.2.1 if naredba . . . . . . . . . . . . . . . . . . . . . . . 100
6.2.2 if-else naredba . . . . . . . . . . . . . . . . . . . . . 101
6.2.3 Primjer: Djelitelji broja . . . . . . . . . . . . . . . . . 102
6.2.4 Viˇsestruka if-else naredba . . . . . . . . . . . . . . . 104
6.2.5 Primjer. Viˇsestruki izbor . . . . . . . . . . . . . . . . . 106
6.2.6 Sparivanje if i else dijela . . . . . . . . . . . . . . . . 106
6.3 switch naredba . . . . . . . . . . . . . . . . . . . . . . . . . . 107
6.4 while petlja . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
6.5 for petlja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
6.5.1 Operator zarez . . . . . . . . . . . . . . . . . . . . . . 113
6.5.2 Datoteka zaglavlja <stdlib.h> . . . . . . . . . . . . . 113
6.6 do - while petlja . . . . . . . . . . . . . . . . . . . . . . . . 114
6.7 Naredbe break i continue . . . . . . . . . . . . . . . . . . . . 115
6.8 goto naredba . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
7 Funkcije 119
7.1 Definicija funkcije . . . . . . . . . . . . . . . . . . . . . . . . . 119
7.2 Deklaracija funkcije . . . . . . . . . . . . . . . . . . . . . . . . 123
7.3 Prijenos argumenata . . . . . . . . . . . . . . . . . . . . . . . 126
7.4 Inline funkcije . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
7.5 Rekurzivne funkcije . . . . . . . . . . . . . . . . . . . . . . . . 130
7.6 Funkcije s varijabilnim brojem argumenata . . . . . . . . . . . 132
8 Preprocesorske naredbe 134
8.1 Naredba #include . . . . . . . . . . . . . . . . . . . . . . . . 134
8.2 Naredba #define . . . . . . . . . . . . . . . . . . . . . . . . . 135
8.3 Parametrizirana #define naredba . . . . . . . . . . . . . . . . 136
8.4 Uvjetno ukljuˇcivanje . . . . . . . . . . . . . . . . . . . . . . . 138
8.5 Predefinirani makroi . . . . . . . . . . . . . . . . . . . . . . . 141
8.6 assert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
9 Struktura programa 144
9.1 Doseg varijable . . . . . . . . . . . . . . . . . . . . . . . . . . 144
9.1.1 Lokalne varijable . . . . . . . . . . . . . . . . . . . . . 144
SADR
ˇ
ZAJ 5
9.1.2 Globalne varijable . . . . . . . . . . . . . . . . . . . . . 145
9.1.3 Argumenti funkcijskog prototipa . . . . . . . . . . . . . 148
9.1.4 Lokalne varijable i standard C99 . . . . . . . . . . . . . 148
9.1.5 Funkcijski doseg . . . . . . . . . . . . . . . . . . . . . . 149
9.2 Vijek trajanja varijable . . . . . . . . . . . . . . . . . . . . . . 149
9.2.1 Automatske varijable . . . . . . . . . . . . . . . . . . . 149
9.2.2 Identifikatori memorijske klase . . . . . . . . . . . . . . 150
9.2.3 auto . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
9.2.4 register . . . . . . . . . . . . . . . . . . . . . . . . . 150
9.2.5 Statiˇcke varijable . . . . . . . . . . . . . . . . . . . . . 151
9.2.6 Statiˇcke lokalne varijable . . . . . . . . . . . . . . . . . 151
9.3 Vanjski simboli . . . . . . . . . . . . . . . . . . . . . . . . . . 152
9.3.1 Funkcije . . . . . . . . . . . . . . . . . . . . . . . . . . 153
9.3.2 Globalne varijable . . . . . . . . . . . . . . . . . . . . . 155
9.3.3 Vanjska imena . . . . . . . . . . . . . . . . . . . . . . . 156
10 Polja 157
10.1 Definicija i inicijalizacija polja . . . . . . . . . . . . . . . . . . 157
10.2 Polja znakova . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
10.3 Funkcije za rad sa stringovima . . . . . . . . . . . . . . . . . . 161
10.4 sscanf(), sprintf() . . . . . . . . . . . . . . . . . . . . . . 164
10.5 Polje kao argument funkcije . . . . . . . . . . . . . . . . . . . 165
10.6 Viˇsedimenzionalna polja . . . . . . . . . . . . . . . . . . . . . 166
10.7 Polja varijabilne duljine . . . . . . . . . . . . . . . . . . . . . 170
11 Pokazivaˇci 173
11.1 Deklaracija pokazivaˇca . . . . . . . . . . . . . . . . . . . . . . 173
11.2 Pokazivaˇci i funkcije . . . . . . . . . . . . . . . . . . . . . . . 175
11.3 Operacije nad pokazivaˇcima . . . . . . . . . . . . . . . . . . . 177
11.3.1 Poveˇcavanje i smanjivanje . . . . . . . . . . . . . . . . 177
11.3.2 Pokazivaˇci i cijeli brojevi . . . . . . . . . . . . . . . . . 179
11.3.3 Usporedivanje pokazivaˇca . . . . . . . . . . . . . . . . 179
11.3.4 Oduzimanje pokazivaˇca . . . . . . . . . . . . . . . . . . 179
11.3.5 Primjer . . . . . . . . . . . . . . . . . . . . . . . . . . 180
11.3.6 Generiˇcki pokazivaˇc . . . . . . . . . . . . . . . . . . . . 181
11.4 Pokazivaˇci i jednodimenzionalna polja . . . . . . . . . . . . . . 183
11.5 Pokazivaˇci i const . . . . . . . . . . . . . . . . . . . . . . . . 185
11.6 Polja pokazivaˇca . . . . . . . . . . . . . . . . . . . . . . . . . 186
11.7 Pokazivaˇci i viˇsedimenzionalna polja . . . . . . . . . . . . . . . 187
11.7.1 Matrica kao pokazivaˇc na pokazivaˇc . . . . . . . . . . . 188
11.8 Dinamiˇcka alokacija memorije . . . . . . . . . . . . . . . . . . 189
6 SADR
ˇ
ZAJ
11.9 Pokazivaˇc na funkciju . . . . . . . . . . . . . . . . . . . . . . . 191
11.10Argumenti komandne linije . . . . . . . . . . . . . . . . . . . . 192
11.11Sloˇzene deklaracije . . . . . . . . . . . . . . . . . . . . . . . . 194
12 Strukture 196
12.1 Deklaracija strukture . . . . . . . . . . . . . . . . . . . . . . . 196
12.1.1 Varijable tipa strukture . . . . . . . . . . . . . . . . . . 196
12.1.2 Inicijalizacija strukture . . . . . . . . . . . . . . . . . . 197
12.1.3 Struktura unutar strukture . . . . . . . . . . . . . . . . 198
12.2 Rad sa strukturama . . . . . . . . . . . . . . . . . . . . . . . . 198
12.2.1 Operator toˇcka . . . . . . . . . . . . . . . . . . . . . . 198
12.2.2 Struktura i funkcije . . . . . . . . . . . . . . . . . . . . 200
12.3 Strukture i pokazivaˇci . . . . . . . . . . . . . . . . . . . . . . 200
12.3.1 Operator strelica (->) . . . . . . . . . . . . . . . . . . 200
12.3.2 Sloˇzeni izrazi . . . . . . . . . . . . . . . . . . . . . . . 202
12.4 Samoreferentne strukture . . . . . . . . . . . . . . . . . . . . . 203
12.4.1 Jednostruko povezana lista . . . . . . . . . . . . . . . . 203
12.5 typedef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
12.6 Unija . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
13 Datoteke 213
13.1 Vrste datoteka . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
13.1.1 Tekstualne i binarne datoteke . . . . . . . . . . . . . . 214
13.1.2 Razine ulaza/izlaza . . . . . . . . . . . . . . . . . . . . 214
13.1.3 Standardne datoteke . . . . . . . . . . . . . . . . . . . 215
13.2 Otvaranje i zatvaranje datoteke . . . . . . . . . . . . . . . . . 216
13.2.1 Standardne datoteke . . . . . . . . . . . . . . . . . . . 217
13.3 Funkcije za ˇcitanje i pisanje . . . . . . . . . . . . . . . . . . . 218
13.3.1
ˇ
Citanje i pisanje znak po znak . . . . . . . . . . . . . . 218
13.3.2
ˇ
Citanje i pisanje liniju po liniju . . . . . . . . . . . . . 220
13.3.3 Prepoznavanje greˇske . . . . . . . . . . . . . . . . . . . 221
13.3.4 Formatirani ulaz/izlaz . . . . . . . . . . . . . . . . . . 222
13.3.5 Binarni ulaz/izlaz . . . . . . . . . . . . . . . . . . . . . 222
13.3.6 Direktan pristup podacima . . . . . . . . . . . . . . . . 224
14 Operacije nad bitovima 227
14.1 Operatori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
14.2 Polja bitova . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
SADR
ˇ
ZAJ 7
A 234
A.1 ANSI zaglavlja . . . . . . . . . . . . . . . . . . . . . . . . . . 234
A.2 ASCII znakovi . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
A.3 Matematiˇcke funkcije . . . . . . . . . . . . . . . . . . . . . . . 236
Poglavlje 1
Uvod
1.1 Programski jezici
Mikroprocesor i drugi logiˇcki sklopovi raˇcunala imaju svoj vlastiti pro-
gramski jezik koji se naziva strojni jezik, a sastoji se od nizova binarnih rijeˇci
koje predstavljaju instrukcije logiˇckim sklopovima i podatke koje treba obra-
diti. Program napisan u strojnom jeziku nazivamo izvrˇsni program ili izvrˇsni
kˆ od budu´ci da ga raˇcunalo moˇze neposredno izvrˇsiti. Strojni jezik je odreden
arhitekturom raˇcunala, a definira ga proizvodaˇc hardwarea. Izvrˇsni program
je strojno zavisan, ˇsto znaˇci da se kˆ od napisan na jednom raˇcunalu moˇze
izvrˇsavati jedino na raˇcunalima istog tipa.
Pisanje instrukcija u binarnom kˆ odu posve je nepraktiˇcno pa su razvijeni
simboliˇcki jezici u kojima su binarne instrukcije zamijenjene mnemoniˇckim
oznakama. Programer unosi program napisan u mnemoniˇckim oznakama
u tekstualnu datoteku pomo´cu editora teksta i zatim poziva program koji
mnemoniˇcke oznake prevodi u binarne instrukcije strojnog jezika. Program
koji vrˇsi konverziju naziva se asembler (eng. assembler) a sam se program-
ski jezik naziva asemblerski jezik ili jednostavno asembler. Program napisan
u asemblerskom jeziku nazivamo izvorni program (eng. source code). Pisa-
nje programa time postaje dvostepeni proces koji ˇcine pisanje izvornog pro-
grama i prevodenje izvornog programa u izvrˇsni program. Programer se tako
oslobada mukotrponog pisanja binarnih instrukcija te se dobiva do odredene
mjere strojna neovisnost izvornog programa.
Sljede´ci primjer pokazuje dio izvrˇsnog kˆ oda (strojni jezik) i njemu ekviva-
lentan izvorni, asemblerski kˆ od. Rad se o asembleru za Intelov mikroprocesor
8086 ([5]).
8
1.1. PROGRAMSKI JEZICI 9
Strojni jezik Ekvivalentan asemblerski kˆ od
00011110 PUSH DS
00101011 SUB AX,AX
11000000
10111000 PUSH AX
10111000 MOV AX,MYDATA
00000001
10001110
11011000 MOV DS,AX
Pisanje programa u asemblerskom jeziku daje programeru potpunu kon-
trolu nad svim komponentama raˇcunala. Programer stoga mora poznavati
arhitekturu raˇcunala za koje piˇse program te kontrolirati sve operacije koje
raˇcunalo izvrˇsava. Programiranje i najjednostavnijih zada´ca rezultira velikim
i sloˇzenim programima pa se programiranje u asembleru koristi se samo za
specifiˇcne zada´ce vezane uz manipulacije s hardwareom. Izvorni program na-
pisan u asembleru nije prenosiv izmedu raˇcunala razliˇcite arhitekture. Zbog
svih tih razloga za ve´cinu programerskih zada´ca koriste se viˇsi programski
jezici.
Viˇsi programski jezici (C, Pascal, FORTRAN, C++, Java, Perl, Python,
. . . ) razvijeni su kako bi se prevladali nedostaci asemblerskog jezika. Oni
oslobadaju programera potrebe poznavanja arhitekture raˇcunala, omogu´ca-
vaju prenosivost programa izmedu raˇcunala razliˇcitih arhitektura te brˇze i
jednostavnije programiranje. Programi napisani u viˇsem programskom jeziku
moraju prije izvodenja pro´ci postupak prevodenja u izvrˇsni kˆ od ˇsto je zada-
tak prevodioca (eng. compiler) za dani jezik. Naredbe viˇseg programskog
jezika prilagodene su tipu podataka s kojima programski jezik manipulira i
operacijama koje nad podacima treba vrˇsiti. To je osnovna razlika naspram
asemblera koji je prilagoden naˇcinu funkcioniranja mikroprocesora i drugih
logiˇckih sklopova. Zadatak je prevodioca da program napisan u viˇsem pro-
gramskom jeziku prevede u kˆ od koji se moˇze izvrˇsavati na zadanom raˇcunalu.
Na taj se naˇcin program napisan u nekom viˇsem programskom jeziku moˇze
izvrˇsavati na svakom raˇcunalu koje ima prevodilac za taj jezik.
Programski jezik C viˇsi je programski jezik op´ce namjene. Razvio ga je
Dennis Ritchie sedamdestih godina proˇslog stolje´ca u Bell Telephone Labora-
tories, Inc. Opis jezika dan je u knjizi Brian W. Kernighan, Dennis M. Ritchie:
The C Programming Language, Prentice-Hall, 1978. Tijekom sedamdesetih
i osamdesetih godina jezik se brzo ˇsirio te je American National Standard
Institute (ANSI) pristupio njegovoj standardizaciji koja je dovrˇsena 1989.
godine. Novi standard uveo je znaˇcajne izmjene u jezik koji se stoga, za
10 POGLAVLJE 1. UVOD
razliku od prvotne verzije, ˇcesto naziva ANSI-C. Novi je standard opisan u
knjizi Brian W. Kernighan, Dennis M. Ritchie: The C Programming Language,
2nd ed., Prentice-Hall, 1988. Danas gotovo svi moderni C-prevodioci imple-
mentiraju ANSI-C verziju jezika. ANSI standard usvojila je i Medunarodna
organizacija za standarde (International Organisation for Standardization)
1990. godine (ISO C). Konaˇcan ANSI/ISO standard ´cemo jednostavno nazi-
vati C90 standard. Godine 1999. ISO je prihvatio novi C standard koji uvodi
manje dopune u C90 standard, a koji ´cemo zvati C99 standard.
1.2 Osnove pisanja programa u Unix okruˇze-
nju
Postupak programiranja u programskom jeziku C, pod operacijskim sus-
tavima unix i linux, sastoji se od tri koraka: prvo se programski kˆ od smjesti
u tekstualnu datoteku koja ima ekstenziju .c (npr. prvi.c). Zatim se napi-
sani program transformira u izvrˇsni kˆ od pozivom programa prevodioca. Kao
rezultat dobiva se izvrˇsna datoteka ˇcijim se pozivom izvrˇsava napisani pro-
gram. Budu´ci da se u pisanju programa redovito javljaju greˇske, tre´ci korak
u postupku programiranja je nalaˇzenje i ispravljanje greˇsaka.
1.2.1 Editor teksta
Da bismo program upisali u tekstualnu datoteku potreban nam je editor
teksta. Svaki unix sustav ima editor koji se naziva vi (visual editor) i koji
se moˇze koristiti i bez grafiˇckog suˇcelja. Pored vi editora mogu´ce je koristiti
svaki drugi editor koji ne ume´ce znakove formatiranja u datoteku.
1.2.2 Compiler
Nakon ˇsto je program napisan i pohranjen u datoteku, recimo da se zove
prvi.c, potrebno je pozvati C-prevodilac. Pod unix-om njegovo je ime
najˇceˇs´ce cc (ili gcc ako se radi o GNU prevodiocu). Tako bismo na komandnoj
liniji otipkali
cc prvi.c
i ukoliko je program napisan bez greˇske prevodilac ´ce kreirati izvrˇsni program
i dati mu ime a.out; u sluˇcaju neispravnosti programa prevodenje ´ce biti
zaustavljeno i prevodilac ´ce dati poruke o pronadenim greˇskama. Nakon ˇsto
je izvrˇsni program kreiran pod unix-om je dovoljno otipkati
1.2. OSNOVE PISANJA PROGRAMA U UNIX OKRU
ˇ
ZENJU 11
./a.out
i program ´ce biti izvrˇsen.
1.2.3 man
Prevodilac cc ima brojne opcije koje su opisane u njegovoj man stranici
(man je skra´ceno od manual). Dovoljno je na komandnoj liniji utipkati
man cc
i opis komande cc bit ´ce izlistan. Na isti naˇcin moˇzemo dobiti opis bilo koje
naredbe operacijskog sustava te niza programa i alata. Dovoljno je otipkati
man naslov
gdje je naslov naredba ili program koji nas zanima. Jednako tako man ´ce nam
dati informacije o funkcijama iz standardne biblioteka jezika C. Na primjer,
otipkajte
man scanf
i dobit ´cete opis funkcije za uˇcitavanje podataka scanf.
1.2.4 Debugger
Ukoliko program upisan u datoteku prvi.c nije ispravno napisan to se
moˇze manifestirati na tri naˇcina. Prvi, najpovoljniji, je onaj u kome prevodi-
lac javi poruke o greˇskama i zaustavi proces prevodenja u izvrˇsni kˆ od. Druga
mogu´cnost je da proces prevodenja bude uspjeˇsan i da cc kreira izvrˇsnu
datoteku a.out, no izvrˇsavanje programa zavrˇsava s porukom operacijskog
sustava o neispravnom izvrˇsnom programu. Tre´ca mogu´cnost, ˇcesto najne-
povoljnija, je ona u kojoj je izvrˇsni program ispravan ali ne radi ono ˇsto je
programer zamislio. U svakom sluˇcaju, kod pojave greˇsaka potrebno je greˇske
prona´ci, korigirati i ponoviti postupak prevodenja u izvrˇsni kˆ od. Budu´ci da
se greˇske redovito javljaju, programiranje je iterativan proces u kojem je
ispravljanje greˇsaka tre´ci (i najvaˇzniji) korak.
Prilikom pronalaˇzenja greˇsaka mogu nam pomo´ci dva alata. Jedan je lint
(ukucajte man lint na komandnoj liniji) koji provjerava sintaksu napisanog
programa i daje detaljniju dijagnostiku od poruka o greˇskama koje daje sam
prevodilac. Drugi alat je debugger (dbx ili gdb), program pomo´cu kojega
naˇs kˆ od moˇzemo izvrˇsavati liniju po liniju, pratiti vrijednosti varijabli, zaus-
tavljati ga na pojedinim mjestima itd. Debugger je najefikasnije sredstvo za
otkrivanje razloga neispravnog funkcioniranja kˆ oda.
12 POGLAVLJE 1. UVOD
1.2.5 Linker
Sloˇzeniji C programi sastoje se od viˇse datoteka s ekstenzijama .c i .h.
Datoteke s ekstenzijom .c sadrˇze dijelove C programa odnosno izvorni kˆ od
(eng. source code). Datoteke s ekstenzijom .h nazivaju se datoteke zaglavlja
(eng. header files) i sadrˇze informacije potrebne prevodiocu za prevodenje
izvornog kˆ oda u strojni jezik. To su tekstualne datoteke koje programer
kreira zajedno s .c datotekama.
Svaku pojedinu .c datoteku prevodilac prevodi u strojni jezik no time
se joˇs ne dobiva izvrˇsni program jer njega ˇcine tek sve .c datoteke zajedno.
Prevodilac za svaku .c datoteku generira strojni kˆ od i smjeˇsta ga u dato-
teku istog imena ali s ekstenzijom .o. Dobiveni se kˆ od naziva objektni kˆ od ili
objektni program. Zadatak je linkera
1
kreirati izvrˇsni program povezivanjem
svih objektnih programa u jednu cjelinu. Prevodilac ´ce nakon generiranja
objektnog kˆ oda automatski pozvati linker tako da njegovo neposredno pozi-
vanje nije potrebno. Ukoliko povezivanje objektnog kˆ oda nije bilo uspjeˇsno
linker ´ce dati poruke o greˇskama.
1.2.6 make
Pri prevodenju u izvrˇsni kˆ od programa koji je razbijen u viˇse datoteka
potrebno je svaku pojedinu .c datoteku prevesti u objektni kˆ od, a zatim sve
objektne datoteke povezati, eventualno i s vanjskim bibliotekama. Pri tome
nam pomaˇze programski alat koji se naziva make. On provjerava datume
zadnje izmjene pojedinih datoteka i osigurava da se prevedu u objektni kˆ od
samo one datoteke koje su mijenjane nakon posljednjeg generiranja izvrˇsnog
programa.
1.2.7 Programske biblioteke
Svaki sloˇzeniji C-program koristi niz funkcija kao ˇsto su printf, exit,
fgets itd. koje su pohranjene u standardnim bibliotekama funkcija. Potpun
popis funkcija dostupnih programeru razlikuje se od raˇcunala do raˇcunala no
jedan broj tih funkcija prisutan je na svakom sustavu koji ima C prevodilac.
U ovoj skripti bit ´ce opisan dio najˇceˇs´ce koriˇstenih funkcija iz standardne bi-
blioteke. Potpuniji popis moˇze se na´ci u [4] ili u priruˇcnicima za C-prevodilac
na danom raˇcunalu.
1
Koristi se joˇs i naziv punjaˇc.
Poglavlje 2
Osnove programskog jezika C
U ovom poglavlju dajemo pregled programskog jezika C uvode´ci osnovne
elemente jezika kroz jednostavne primjere. Svi ovdje uvedeni pojmovi bit ´ce
detaljnije analizirani u narednim poglavljima.
2.1 Prvi program
Jedan jednostavan ali potpun C program izgleda ovako:
#include <stdio.h>
int main(void)
{
printf("Dobar dan.\n");
return 0;
}
Program treba spremiti u datoteku s ekstenzijom .c kako bi se znalo da
se radi o C-programu. Nazovimo stoga datoteku prvi.c. Program zatim
kompiliramo naredbom
cc prvi.c
nakon ˇcega prevodilac kreira izvrˇsni program i daje mu ime a.out. Da bismo
izvrˇsili program dovoljno je otipkati
./a.out
Rezultat izvrˇsavanja programa je ispis poruke
13
14 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C
Dobar dan.
Svi C-programi sastoje se od funkcija i varijabli. Funkcije sadrˇze instruk-
cije koje odreduju koje ´ce operacije biti izvrˇsene, a varijable sluˇze memorira-
nju podataka.
Izvrˇsavanje programa poˇcine izvrˇsavanjem funkcije main koja mora biti
prisutna u svakom programu. Funkcija main svoju zada´cu obavlja op´cenito
pozivanjem drugih funkcija. Tako se u naˇsem programu poziva funkcija
printf iz standardne biblioteke, koja je zaduˇzena za ispis podataka na
ekranu. Da bismo mogli koristiti funkcije iz standardne biblioteke zaduˇzene
za ulaz i izlaz podataka program zapoˇcinjemo naredbom
#include <stdio.h>
Njom se od prevodioca traˇzi da ukljuˇci (include) u program datoteku stdio.h
koja sadrˇzi informacije nuˇzne za koriˇstenje funkcije printf i mnogih drugih.
Datoteke s ekstenzijom .h nazivaju se datoteke zaglavlja (eng. header files)
i njihovo stavljanje u oˇstre zagrade < > informira prevodilac da se radi o
standardnim datotekama zaglavlja, koje se nalaze na unaprijed odredenim
mjestima u datoteˇcnom sustavu.
Sljede´ca linija predstavlja deklaraciju funkcije main:
int main(void)
Funkcija moˇze uzimati jedan ili viˇse argumenata i obiˇcno vra´ca neku vrijed-
nost. Deklaracijom se uvodi ime funkcije, broj i tip argumenata koje uzima
i tip povratne vrijednosti. U naˇsem primjeru ime funkcije je main i ona ne
uzima niti jedan argument. To je deklarirano tako ˇsto je u oble zagrade
stavljena kljuˇcna rijeˇc void (eng. void=prazan). Povratna vrijednost je tipa
int, ˇsto je oznaka za cijeli broj.
1
Iza oblih zagrada dolaze vitiˇcaste zagrade koje omeduju tijelo funkcije.
Tijelo funkcije je sastavljeno od deklaracija varijabli i izvrˇsnih naredbi. Sve
deklaracije varijabli dolaze prije prve izvrˇsne naredbe. U naˇsem primjeru ne-
mamo deklaracija varijabli. Tijelo sadrˇzi samo dvije naredbe: poziv funkcije
printf i return naredbu:
{
printf("Dobar dan.\n");
return 0;
}
Svaka naredba zavrˇsava znakom toˇcka-zarez (;). To omogu´cava da se viˇse
naredbi stavi na istu liniju. Mogli smo, na primjer, pisati
1
int=integer.
2.1. PRVI PROGRAM 15
#include <stdio.h>
int main(void){
printf("Dobar dan.\n"); return 0;
}
Funkcija se poziva tako da se navede njezino ime iza koga idu oble zagrade
s listom argumenata koji se funkciji predaju. Funkcija printf dobiva samo
jedan argument: to je niz znakova "Dobar dan.\n". Navodnici sluˇze za
ograniˇcavanje konstantnih znakovnih nizova. Sve ˇsto se nalazi izmedu ” i
” predstavlja niz znakova koji ˇcini jednu cjelinu. Pored obiˇcnih znakova
izmedu navodnika mogu se na´ci i specijalni znakovi koji poˇcinju znakom \
(eng. backslash). Tako \n oznaˇcava prijelaz u novi red. Funkcija printf
nakon zavrˇsenog ispisa ne prelazi automatski u novi red nego je potrebno
ubaciti znak \n tamo gdje takav prijelaz ˇzelimo. Program smo stoga mogli
napisati u obliku
#include <stdio.h>
int main(void)
{
printf("Dobar ");
printf("dan.");
printf("\n");
return 0;
}
i dobili bismo posve isti ispis.
Izvrˇsavanje funkcije zavrˇsava naredbom return. Kad funkcija vra´ca neku
vrijednost u pozivni program ta se vrijednost navodi u return naredbi. Funk-
cija main vra´ca nulu pa je stoga zadnja naredba return 0;
Funkcija main mora biti prisutna u svakom programu. Kada na koman-
dnoj liniji otipkamo
./a.out
operacijski sustav poziva funkciju main. Zatim se redom izvrˇsavaju naredbe
unutar funkcije main sve dok se ne dode do return naredbe. Ona ope-
racijskom sustavu vra´ca cjelobrojnu vrijednost koja ima znaˇcenje izlaznog
statusa. Nula se interpretira kao uspjeˇsni zavrˇsetak programa, a svaka druga
vrijednost signalizira zavrˇsetak uslijed greˇske.
16 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C
2.2 Varijable. while petlja
Napiˇsimo program koji ispisuje prvih deset faktorijela. Brojeve 1!, 2!,
3!,. . . ,10! ˇzelimo ispisati u obliku tablice
2
1 1
2 2
3 6
4 24
. . . . . .
#include <stdio.h>
/* Program koji ispisuje
prvih 10 faktorijela. */
int main(void)
{
int n,fakt;
n=1;
fakt=1;
while(n<=10)
{
fakt=fakt*n;
printf(" %d %d\n",n,fakt);
n=n+1;
}
return 0;
}
Prva linija programa ukljuˇcuje u program standardnu datoteku zaglavlja
<stdio.h> koja nam je nuˇzna jer koristimo funkciju printf.
Dvije linije
/* Program koji ispisuje
prvih 10 faktorijela. */
predstavljaju komentar unutar programa. Prevodilac ignorira dio programa
izmedu znakova /* i */. Tekst koji se nalazi izmedu tih znakova ima ulogu
komentara koji treba olakˇsati razumijevanje programa.
Prva linija unutar funkcije main
2
n! je broj definiran formulom n! = 1 · 2 · 3 · · · · n
2.2. VARIJABLE. WHILE PETLJA 17
int n,fakt;
predstavlja deklaraciju varijabli n i fakt.
U C-u je potrebno deklarirati sve varijable koje se u programu koriste.
Preciznije, unutar svake funkcije (ovdje unutar funkcije main) sve varijable
koje funkcija koristi treba deklarirati prije prve izvrˇsne naredbe.
Deklaracija se sastoji od tipa varijable koji slijede imena varijabli odvo-
jena zarezima. Deklaracija kao i svaka druga naredba u programu zavrˇsava
znakom toˇcka-zarez. U deklaraciji
int n,fakt;
imena varijabli koje se deklariraju su n i fakt, a njihov tip je int. Tip
int predstavlja cjelobrojnu varijablu koja moˇze biti pozitivna i negativna.
Granice u kojima se moˇze kretati varijabla tipa int ovise o raˇcunalu i danas
je to najˇceˇs´ce od -214748347 do 214748348. Pored varijabli tipa int postoje
joˇs dvije vrste cjelobrojnih varijabli. To su varijable tipa short i varijable
tipa long. Razlika je u tome da short pokriva manji raspon cijelih brojeva i
zauzima manje memorijskog prostora, dok long pokriva ve´ci raspon i treba
viˇse memorijskog prostora. Varijable realnog tipa (tzv. brojevi s pokretnim
zarezom) pojavljuju se u dvije forme: brojevi jednostruke preciznosti float i
brojevi dvostruke preciznosti double. Konaˇcno, postoje i varijable tipa char
u kojima se pamte znakovi.
Tipovi varijabli:
char jedan znak,
short “kratki” cijeli broj,
int cijeli broj,
long “dugi” cijeli broj,
float realan broj jednostruke preciznosti,
double realan broj dvostruke preciznosti.
Pored ovih tipova podataka koje nazivamo osnovnim, postoje joˇs i sloˇzeni
tipovi podataka kao ˇsto su strukture, polja, unije te pokazivaˇci kao posebni
osnovni tip podatka.
Prva izvrˇsna naredba u programu je naredba pridruˇzivanja
n=1;
Znak jednakosti je operator pridruˇzivanja. On vrijednost konstante ili varija-
ble na desnoj strani pridruˇzuje varijabli na lijevoj strani. Nakon te naredbe
vrijednost varijable n postaje jednaka 1.
Naredba
18 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C
while(n<=10)
{
....
}
je while petlja. Ona se sastoji od kljuˇcne rijeˇci while, testa n<=10 i tijela
petlje; tijelo petlje je skup naredbi koje se nalaze unutar vitiˇsastih zagrada.
while-petlja funkcionira na sljede´ci naˇcin: prvo se izraˇcunava izraz u za-
gradama: n<=10. Ukoliko je je on istinit (n je manji ili jednak 10) onda se
izvrˇsava tijelo petlje (sve naredbe unutar vitiˇcastih zagrada). Zatim se po-
novo testira izraz n<=10 i izvrˇsava tijelo petlje ako je izraz istinit. Izvrˇsavanje
petlje prekida se kada izraz n<=10 postane neistinit. Program se tada nas-
tavlja prvom naredbom koja slijedi iza tijela while petlje.
Tijelo petlje moˇze se sastojati od jedne ili viˇse naredbi. Ako se sastoji
od samo jedne naredbe nije potrebno koristiti vitiˇcaste zarade, iako nihova
upotreba nije pogreˇsna. Kada se tijelo sastoji od viˇse naredbi one moraju
biti omedene vitiˇcastim zagradama.
Manje ili jednako (<=) je jedan od relacijskih operatora. Ostali su:
operator primjer znaˇcenje
<= a <= b manje ili jednako,
>= a >= b ve´ce ili jednako,
< a < b strogo manje,
> a > b strogo ve´ce,
== a == b jednako,
!= a != b nejednako.
Vaˇzno je razlikovati operator pridruˇzivanja (=) i relacijski operator jed-
nakosti (==).
U tijeli while petlje imamo tri naredbe:
fakt=fakt*n;
printf(" %d %d\n",n,fakt);
n=n+1;
U prvoj i tre´coj naredbi pojavljuju se operacije mnoˇzenja i zbrajanja. Os-
novne aritmetiˇcke operacije dane su sljede´coj tabeli:
operator primjer
zbrajanje + a+b
oduzimanje - a-b
mnoˇzenje * a*b
dijeljenje / a/b
2.2. VARIJABLE. WHILE PETLJA 19
U naredbi fakt=fakt*n varijable fakt i n su pomnoˇzene i rezultat je
pridruˇzen varijabli fakt. Varijabla fakt pri tome mjenja vrijednost, dok
varijabla n ostaje nepromijenjena. U naredbi n=n+1 varijabli n pove´cavamo
vrijednost za 1. Operator pridruˇzivanje (=) djeluje tako da se prvo izraˇcuna
izraz na desnoj strani (n+1) i zatim se ta vrijednost pridruˇzi varijabli na
lijevoj strani (varijabla n). Ukupan efekt je pove´canje vrijednosti varijable n
za 1.
Operacija dijeljenja s cjelobrojnim operandima daje cjelobrojni rezultat.
To se radi tako da se rezultatu dijeljenja odsijeku decimale kako bi se dobio
cijeli broj (npr., 2/3 = 0). Ako je bar jedan operand realan broj dijeljenje
predstavlja uobiˇcajeno dijeljenje realnih brojeva. Na primjer,
6/4 == 1
6.0/4.0 == 1.5
6/4.0 == 1.5
6.0/4 == 1.5
Ako konstanta nema decimalnu toˇcku, onda je cjelobrojna (tipa int), inaˇce
je tipa double.
Naredba
printf(" %d %d\n",n,fakt);
je poziv funkcije printf. Ona ispisuje brojeve n i fakt na ekranu. Funkcija
je pozvana s tri argumenta. Prvi je konstantan niz znakova " %d %d\n",
a drugi i tre´ci su varijable n i fakt. Prvi se argument naziva kontrolni
znakovni niz i on sadrˇzi znakove konverzije koji se sastoje od jednog slova
kome prethodi znak %. Njihova uloga je sljede´ca: Prvi znak konverzije %d
oznaˇcava da na mjestu na kome se on nalazi treba ispisati prvi argument
koji slijedi nakon kontrolnog znakovnog niza. Slovo d osnaˇcava da ga treba
ispisati kao cijeli decimalni broj. Drugi znak %d oznaˇcava da na tom mjestu
treba ispisati drugi argument koji slijedi nakon kontrolnog znakovnog niza,
kao cijeli decimalni broj.
Broj argumenata koji se daju printf funkciji moˇze biti proizvoljan.
ˇ
Zelimo
li ispisati realan broj (float ili double) trebamo koristiti %f umjesto %d. Svi
ostali znakovi u kontrolnom znakovnom nizu bit ´ce ispisani onako kako su
uneseni. U ovom primjeru imamo dvije bjeline i znak za prijelaz u novi red.
U prvom programu je funkcija printf bila pozvana samo s jednim argumen-
tom, kontrolnim znakovnim nizom. Pri takvom pozivu on ne sadrˇzi znakove
konverzije ve´c samo tekst koji treba ispisati.
20 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C
2.3 for petlja
Treba napisati program koji za proizvoljni prirodni broj n raˇcuna sumu
n

k=1
1
k(k + 1)
i ispisuje rezultat.
#include <stdio.h>
int main(void)
{
int n,i;
double suma;
printf("Unesite broj n: n= ");
scanf(" %d",&n);
suma=0.0;
for(i=1;i<=n;i=i+1)
{
suma=suma+1.0/(i*(i+1));
}
printf("Suma prvih %d clanova = %f\n",n,suma);
return 0;
}
Program zapoˇcinje deklaracijama varijabli. Varijable n i i su cjelobrojne,
dok je suma realna varijabla tipa double (dvostruke preciznosi).
Naredba
printf("Unesite broj n: n= ");
ispisuje znakovni niz "Unesite broj n: n= " (bez prijelaza u novi red).
Pozivom funkciji scanf ˇcita se cijeli broj sa standardnog ulaza:
scanf(" %d",&n);
Program u ovom trenutku ˇceka da korisnik upiˇse jedan cijeli broj.
Funkcija scanf pripada standardnoj biblioteci i deklarirana je u stan-
dardnoj datoteci zaglavlja <stdio.h>. Po strukturi je sliˇcna funkciji printf.
2.3. FOR PETLJA 21
Njen prvi argument je niz znakova koji sadrˇzi znakove konverzije. U naˇsem
sluˇcaju to je %d koji informira scanf da mora proˇcitat jedan broj tipa int i
smjestiti ga u varijablu koja je sljede´ci argument funkcije (varijabla n).
Stvarni argument funkcije scanf je memorijska adresa varijable n, a ne
sama varijabla n. Adresa varijable dobiva se pomo´cu adresnog operatora &
(&n je adresa varijable n). Stoga svi argumenti funkcije scanf, osim prvog,
moraju imati znak & ispred sebe.
Naredba
for(i=1;i<=n;i=i+1)
{
....
}
je for petlja. Ona djeluje na sljede´ci naˇcin. Prvo se varijabla i inicijali-
zira tako ˇsto joj se pridruˇzi vrijednost 1 (i=1). Inicijalizacija se izvrˇsi samo
jednom. Zatim se testira izraz
i<=n;
Ako je rezultat testa istinit izvrˇsavaju se naredbe iz tijela petlje (naredbe u
vitiˇcastim zagradama). Nakon toga se izvrˇsava naredba
i=i+1;
i kontrola programa se vra´ca na testiranje istinitosti izraza i<=n. Petlja
zavrˇsava kad izraz koji se testira postane laˇzan. Program se tada nastavlja
prvom naredbom iza tijela petlje.
for-petlja iz naˇseg primjera moˇze se zapisati pomo´cu while-petlje na
ovaj naˇcin:
i=1;
while(i<=n)
{
suma=suma+1.0/(i*(i+1));
i=i+1;
}
Naredbe oblika i=i+1 i i=i-1 kojima se brojaˇc pove´cava, odnosno sma-
njuje za jedan sre´cu se vrlo ˇcesto pa za njih postoji kra´ca notacija:
n=n+1 je ekvivalentno s n++,
n=n-1 je ekvivalentno s n--.
22 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C
Operator ++ naziva se operator inkrementiranja i on pove´cava vrijednost
varijable za 1; Operator -- koji smanjuje vrijednost varijable za 1 naziva se
operator dekrementiranja. Ti se operatori redovito koriste u for petljama pa
bismo gornju petlju pisali u obliku:
for(i=1;i<=n;i++)
suma=suma+1.0/(i*(i+1));
Operatore inkrementiranja i dekrementiranja moˇzemo primijeniti na cjelo-
brojne i realne varijable. Uoˇcimo joˇs da smo vitiˇcaste zagrade oko tijela
for-petlje mogli ispustiti jer se tijelo sastoji samo od jedne naredbe. To
vrijedi za sve vrste petlji.
Svaka petlja sadrˇzi u sebi jedan ili viˇse brojaˇca kojim se kontrolira odvi-
janje petlje. Brojaˇc je potrebno na poˇcetku inicijalizirati i zatim ga u sva-
kom prolazu petlje pove´cavati (ili smanjivati). Pelja se zaustavlja testira-
njem brojaˇca. U while petlji jedino testiranje brojaˇca ulazi u zagrade nakon
kljuˇcne rijeˇci while. Inicijalizaciju moramo staviti izvan petlje, a promjenu
brojaˇca negdje unutar petlje. U for petlji sva tri elementa dolaze u zagra-
dama iza kljuˇcne rijeˇci for, u obliku
for(inicijalizacija brojaca; test; promjena brojaca)
{
.........
}
Inicijalizacija, test i promjena brojaˇca odvajaju se znakom toˇcka-zarez (;).
Napomena. U C-u postoji joˇs do-while petlja o koju ´cemo objasniti u
sekciji 6.6.
2.4 if naredba
Treba napisati program koji rijeˇsava kvadratnu jednadˇzbu: ax
2
+bx+c =
0. Rjeˇsenja su op´cenito kompleksna i zadana su formulom
z
1,2
=
−b ±

b
2
−4ac
2a
.
Koristit ´cemo notaciju z
1
= x
1
+ iy
1
, z
2
= x
2
+ iy
2
.
#include <stdio.h>
#include <stdlib.h>
2.4. IF NAREDBA 23
#include <math.h>
/* Resavanje kvadratne jednadzbe.
a x^2 + b x + c = 0 */
int main(void) {
double a, b, c, /* Koeficijenti jednadzbe */
d, /* Diskriminanta */
x1, x2, /* Realni dijelovi korijena. */
y1, y2; /* Imaginarni dijelovi korijena. */
printf("Upisite koeficijente kvadratne jednadzbe: a, b, c: ");
scanf ("%lf%lf%lf", &a, &b, &c);
y1=0.0;
y2=0.0;
if(a != 0.0) {
d=b*b-4*a*c;
if (d > 0) {
x1=(- b + sqrt (d))/(2 * a);
x2=(- b - sqrt (d))/(2 * a);
} else if (d == 0) {
x1=- b/(2 * a);
x2=x1;
} else{
x1=-b/(2 * a); x2 = x1;
y1=sqrt(-d)/(2 * a); y2 = - y1;
}
}
else {
printf("Jednadzba nije kvadratna.\n");
exit(-1);
}
printf("z1=%f + i*(%f), z2=%f + i*(%f)\n",x1, y1, x2, y2);
return 0;
}
U programu imamo nekoliko novih elemenata. Prvo, imamo tri include
naredbe. Datoteku stdio.h ukljuˇcujemo radi funcija printf i scanf; da-
toteka stdlib.h ukljuˇcujemo radi funkcije exit, a datoteku math.h radi
funkcije sqrt (sqrt(x)=

x). U datoteci zaglavlja stdlib.h deklarirano
24 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C
je nekoliko funkcija koje se ˇcesto koriste tako da se ta datoteka ukljuˇcije
vrlo ˇcesto u programe. Datoteku math.h potrebno je ukljuˇciti kada program
koristi bilo koju matematiˇcku funkciju.
U prvoj naredbi unutar funkcije main deklariramo osam varijabli tipa
double: a, b, c, d, x1, x2, y1, y2. Varijable smo razbili u ˇcetiri grupe
i svaku smo grupu napisali u zasebnom redu da bismo lakˇse komentirali
njihovo znaˇcenje. Takav naˇcin pisanja je mogu´c stoga ˇsto prevodilac tretira
znak za prijelaz u novi red isto kao i bjelinu. Prijelaz u novi red moˇzemo
stoga ubaciti svugdje gdje moˇze do´ci bjelina.
Naredbama
printf("Upisite koeficijente kvadratne jednadzbe: a, b, c: ");
scanf ("%lf%lf%lf", &a, &b, &c);
uˇcitavaju se koeficijenti kvadratne jednadˇzbe. Uoˇcimo da je znak konverzije
kojim se uˇcitava varijabla tipa double jednak %lf. Ako ˇzelimo uˇcitati va-
rijablu tipa float (realni broj u jednostrukoj preciznosti) trebamo koristiti
znak konverzije %f.
3
Nasuprot tome, kod ispisivanja varijabli tipa float i
double funkcija printf koristi isti znak konverzije %f.
Nakon inicijalizacije varijabli y1 i y2 nulom izvrˇsava se if naredba:
if(a != 0.0) {
......
}
else{
......
}
Naredba if je naredba uvjetnog grananja. Op´cenito ima oblik
if(uvjet)
naredba1;
else
naredba2;
Ona funkcionira na ovaj naˇcin: prvo se testira uvjet i ako je zadovoljen
izvrˇsava se naredba1, a ako nije izvrˇsava se naredba2. U naˇsem sluˇcaju
uvjet je a != 0.0 (da li je a razliˇcito od nule?). Naredbe koje se izvrˇsavaju,
naredba1 i naredba2, mogu biti pojedinaˇcne naredbe ili grupe naredbi omedene
vitiˇcastim zagradama, kao u naˇsem primjeru. Dio naredbe else ne mora biti
prisutan, tako da if naredba moˇze imati i oblik
3
%f moˇzemo ˇcitati kao float, a %lf kao long float = double.
2.4. IF NAREDBA 25
if(uvjet)
naredba1;
pri ˇcemu se naredba1 izvrˇsava ako je uvjet ispunjen, dok se u protivnom
prijelazi na sljede´cu instrukciju.
Testiranjem uvjeta a != 0.0 provjeravamo je li jednaˇzba kvadratna ili
nije. Ako nije izvrˇsava se else dio naredbe uvjetnog grananja u kojem se
ispisuje poruka da jednadˇzba nije kvadratna i poziva se funkcija exit s ar-
gumentom -1. Zada´ca funkcija exit je zaustaviti izvrˇsavanje programa i
predati operacijskom sustavu svoj argument (-1 u naˇsem primjeru) koji se
interpretira kao kod greske. Pod operacijskim sustavima Unix i Linux ko-
nvencija je da kod 0 oznaˇcava ispravan zavrˇsetak programa dok svaka druga
cjelobrojna vrijednost signalizira zaustavljanje usljed greˇske.
Ako je uvjet a != 0.0 ispunjen, izvrsava se dio koda
d=b*b-4*a*c;
if (d > 0) {
x1=(- b + sqrt (d))/(2 * a);
x2=(- b - sqrt (d))/(2 * a);
} else if (d == 0) {
x1=- b/(2 * a);
x2=x1;
} else{
x1=-b/(2 * a); x2 = x1;
y1=sqrt(-d)/(2 * a); y2 = - y1;
}
Prvo se raˇcuna diskriminanta jednadˇzbe d, a onda se ulazi u jednu viˇsestruku
naredbu uvjetnog granjnja u kojoj se ispituje je li diskriminanta, pozitivna,
jednaka nuli ili negativna. Proizvoljan broj if naredbi moˇzemo ugnijezditi
tako da dobijemo viˇsestruku if naredbu. Na primjer, pomo´cu dvije if na-
redbe dobivamo naredbu oblika
if(uvjet1){
naredba1;
} else if (uvjet2){
naredba2;
} else{
naredba3;
}
naredba4;
26 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C
U ovoj se konstrukciji prvo testira uvjet1. Ako je on ispunjen izvrsava se
blok naredbi naredba1 i izvrˇsavanje programa se nastavlja s prvom naredbom
koja slijedi viˇsestruku naredbu uvjetnog granjnja (naredba4). Ako uvjet1
nije ispunjen testira se uvjet2. Ako je on ispunjen izvrˇsava se blok naredbi
naredba2 i nakon toga naredba4. Ako niti uvjet2 nije ispunjen izvrˇsava se
else blok.
U sluˇcaju d > 0.0 imamo dva realna korijena, dok u sluˇcaju d == 0.0
imamo jedan dvostruki realni korijen. Konaˇcno, ako je diskriminanta nega-
tivna imamo konjugirano kompleksne korijene. Uoˇcimo da smo u tom dijelu
koda dvije naredbe pisali u jednoj liniji. C nam dozvoljava pisati bilo koji
broj naredbi u istoj liniji, premda zbog preglednosti najˇceˇs´ce piˇsemo svaku
naredbu u svojoj liniji.
Konaˇcno, u zadnjoj naredbi
printf("z1=%f + i*(%f), z2=%f + i*(%f)\n",x1, y1, x2, y2);
ispisujemo rezultat.
Centralni dio programa ˇcini jedna if-else naredba u koju je smjeˇstena
druga, sloˇzena if naredba. Pogledajmo joˇs jednom kako se program izvrˇsava,
na primjer u situaciji kada je a != 0 i d > 0. Program ´ce u´ci u if dio
vanjske if-else naredbe, izraˇcunat ´ce d i zatim ´ce u´ci u if dio unutarnje
sloˇzene if naredbe. Kada se izraˇcunaju x1 i x2 program izlazi iz sloˇzene if
narebe i nastavlja s prvom sljede´com naredbom. Kako takve naredbe nema,
izvrˇsavanje if bloka prve if-else naredbe je zavrˇseno i program se nastavlja
s prvom naredbom iza nje, a to je poziv funkcije printf.
U dobro pisanom programu njegova logiˇcka struktura mora biti vidljiva
iz njegove forme. Posebnu paˇznju treba posvetiti ugnjeˇzdenim naredbama
uvjetnog granjanja te petljama. Kod if-else naredbe potrebno je uvijek
uvu´ci tijelo if dijela i tijelo else dijela da se naglasi logiˇcka struktura. Posve
isto se postupa i sa petljama.
Napomena. Kod kompilacije programa koji koristi matematiˇcke funkcije
moramo prevodiocu dati -lm opciju. Na primjer, ako je program spremljen
u datoteku kvadratna jed.c, onda ga komiliramo linijom
cc kvadratna_jed.c -lm

Napomena. C ima i switch naredbu koja takoder sluˇzi uvjetnom granja-
nju (vidi sekciju 6.3).
2.5 Funkcije
2.5. FUNKCIJE 27
Treba napisati funkciju koja raˇcuna binomni koeficijent
_
n
k
_
=
n · (n −1) · · · (n −k + 1)
1 · 2 · · · k
. (2.1)
Funkcija treba uzeti dva cjelobrojna parametra n i k te vratiti broj
_
n
k
_
. U
glavnom programu treba zatim ispisati tzv. Pascalov trokut prikazan niˇze.
U liniji s indeksom n ispisani su brojevi
_
n
k
_
, za sve vrijednosti k od 0 do n.
n = 0 1
n = 1 1 1
n = 2 1 2 1
n = 3 1 3 3 1
n = 4 1 4 6 4 1
n = 5 1 5 10 10 5 1
n = 6 1 6 15 20 15 6 1
n = 7 1 7 21 35 35 21 7 1
n = 8 1 8 28 56 70 56 28 8 1
n = 9 1 9 36 84 126 126 84 36 9 1
Radi jednostavnosti ispisat ´cemo Pacalov trokut poravnan na lijevoj strani.
#include <stdio.h>
/* Funkcija binom() racuna binomni koeficijent. */
long binom(int n, int k)
{
long rezult=1;
int i;
if(n == k) return 1;
if(k == 0) return 1;
for(i=n;i>n-k;i--)
rezult=rezult*i;
for(i=1;i<=k;++i)
rezult=rezult/i;
return rezult;
}
28 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C
int main(void)
{
long bnm;
int n, k;
for(n=0;n<10;n++){
for(k=0;k<=n;k++){
bnm=binom(n,k);
printf("%ld ",bnm);
}
printf("\n");
}
return 0;
}
Funkcija binom definirana je ispred funkcije main. Iz definicije se vidi da
uzima dva argumenta tipa int i vra´ca rezultat tipa long.
Definicija funkcije op´cenito ima oblik
tip ime_funkcije(tip_1 arg_1, tip_2 arg_2, ...)
{
deklaracije varijabli
narebe
}
gdje je tip tip podatka koji funkcija vra´ca kao rezultat svog djelovanja. Unu-
tar zagrada nalaze se deklaracije argumenata funkcije: tip 1 arg 1, tip 2
arg 2, .... Za svaki argument navodi se njegov tip i ime. Tijelo funk-
cije je omedeno vitiˇcastim zagradama i unutar njega prvo dolaze deklaracije
varijabli, a zatim izvrˇsne naredbe.
U dijelu koda
long binom(int n, int k)
{
long rezult=1;
int i;
if(n == k) return 1;
if(k == 0) return 1;
for(i=n;i>n-k;i--)
2.5. FUNKCIJE 29
rezult=rezult*i;
for(i=2;i<=k;++i)
rezult=rezult/i;
return rezult;
}
definirana je funkcija binom. Njezini se argumenti zovu n i k i tipa su int.
Funkcija vra´ca vrijednost tipa long. Unutar funkcije deklarirane su dvije
varijable: rezult te i. To su tzv. lokalne varijable koje postoje samo za
vrijeme izvrˇsavanja funkcije. U deklaraciji
long rezult=1;
varijabla rezult je inicijalizirana s 1. Na taj naˇcin, pri deklaraciji mogu se
inicijalizirati i ostali tipovi varijabli.
Svaka funkcija koja vra´ca neku vrijednost mora sadrˇzavati barem jednu
return naredbu. Nailaskom na return zaustavlja se izvodenje funkcije i
u pozivni program se vra´ca ona vrijednost koja stoji uz return. U naˇsem
primjeru ako je n == k funkcija se zaustavlja i vra´ca 1, jer je
_
n
n
_
= 1 za
svako n. Isto tako, ako je k == 0 ponovo se vra´ca 1, jer vrijedi
_
n
0
_
= 1
za svako n. U tim smo naredbama koristili if naredbe bez pripadnog else
dijela.
Ukoliko prethodna dva uvjeta nisu zadovoljena, izvrˇsavanje funkcije ´ce
do´ci do for petlje
for(i=n;i>n-k;i--)
rezult=rezult*i;
Ona raˇcuna brojnik rezult=n(n−1) · · · (n−k+1) u formuli (2.1). Uoˇcimo da
je brojaˇc i inicijaliziran s n i da se u svakom prolasku kroz petlju smanjuje za
jedan dok ne postane jednak n-k. Tad se izlazi iz petlje. Kako unutar petlje
imamo samo jednu naredbu, nismo koristili vitiˇcaste zagrade. U sljede´coj
for petlji brojnik dijelimo brojevima 2,3,. . . , k. Time dolazimo do izraza
koji smo trebali izraˇcunati.
U glavnom programu (tj. u funkciji main)
int main(void)
{
long bnm;
int n, k;
30 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C
for(n=0;n<10;n++){
for(k=0;k<=n;k++){
bnm=binom(n,k);
printf("%ld ",bnm);
}
printf("\n");
}
return 0;
}
deklarirane su varijable bnm (tipa long), n i k (tipa int). Funkcija binom se
poziva unutar dvostruke for petlje u naredbi
bnm=binom(n,k);
Varijable n i k deklarirane u main su stvarni argumenti funkcije binom. Ar-
gumenti koji su deklarirani u definiciji funkcije binom nazivaju se formalni
argumenti. Njima smo dali ista imena n, k kao i stvarnim argumentima,
ali to nije nuˇzno budu´ci da se radi o posve razliˇcitim varijablama. Varija-
ble n i k deklarirane u funkciji main postoje za vrijeme izvrˇsavanja funkcije
main dok varijable n i k deklarirane u funkciji binom postoje samo za vrijeme
izvrˇsavanja funkcije binom i nestaju nakon izlaska iz funkcije binom. Prili-
kom poziva funkcije vrijednosti stvarnih argumenata se kopiraju u formalne
argumente. Nakon ˇsto funkcija binom izvrˇsi return naredbu, vrijednost koja
stoji uz return se kopira u varijablu bnm koja stoji na lijevoj strani jedna-
kosti. Program se zatim nastavlja ispisivanjem izraˇcunate vrijednosti bnm.
Kako se radi o varijabli tipa long, za ispis u printf naredbi koristimo znak
konverzije %ld.
2.5.1 Funkcija main
Svaki program mora sadrˇzavati funkciju main budu´ci da izvrˇsavanje pro-
grama zapoˇcinje prvom izvrˇsnom naredbom u main-u. Nju poziva opera-
cijski sustav kad korisnik pokrene program. Nakon zavrˇsetka main vra´ca
cjelobrojnu vrijednost operacijskom sustavu koja ima znaˇcenje izlaznog sta-
tusa. Nula se interpretira kao uspjeˇsni zavrˇsetak programa, a svaka vrijednost
razliˇcita od nule signalizira zavrˇsetak uslijed greˇske.
2.5.2 Prototip funkcije
Funkcija mora biti deklarirana prije mjesta na kome se poziva kako bi pre-
vodilac znao broj i tip argumenata funkcije i tip rezultata koji vra´ca. Stoga
2.6. POLJA 31
je prirodno definiciju funkcije staviti prije funkcije main, kao ˇsto smo uˇcinili
u prethodnom primjeru. Ako u programu imamo puno funkcija takav naˇcin
pisanja moˇze biti nepregledan jer funkcija main dolazi na kraju datoteke. C
nam stoga dozvoljava da definiciju funkcije stavimo iza funkcije main (odn.
iza mjesta na kome se poziva) ako prije funkcije main postavimo prototip
funkcije.
Prototip funkcije dobiva se tako da se u definiciji funkcije ispusti ˇcitavo
tijelo funkcije. Prototip kao i sve druge naredbe zavrˇsava s toˇcka-zarezom.
Op´cenito, dakle, prototip ima oblik
tip ime_funkcije(tip_1 arg_1, tip_2 arg_2, ...);
Prototip za funkciju binom bi bio
long binom(int n, int k);
On predstavlja samo deklaraciju funkcije koja kaˇze da je binom ime funkcije
koja uzima dva argumenta tipa int i vra´ca argument tipa long. Ako je takva
deklaracija postavljena prije funkcije main, onda se definicija funkcije moˇze
premjestiti iza funkcije main. Na osnovu prototipa prevodilac moˇze provjeriti
da li je funkcija ispravno pozvana.
Istaknimo da definicija funkcije mora biti u skladu s prototipom funkcije
– tip funkcije te broj i tip argumenata moraju se podudarati.
2.6 Polja
Treba napisati program koja uzima dva trodimenzionalna vektora i ispi-
tuje njihovu okomitost. Matematiˇcki pojmovi vektora i matrica implemen-
tiraju se u C-u pomo´cu polja. Polje je niz indeksiranih varijabli istog tipa,
smjeˇstenih u memoriji na uzastopnim lokacijama. Indeks polja uvijek kre´ce
od nule.
Da bismo ispitali okomitost vektora iskoristit ´cemo formulu za kosinus
kuta izmedu dva vektora:
cos φ =
a ·

b
a

b
,
gdje je
a ·

b = a
1
b
1
+ a
2
b
2
+ a
3
b
3
skalarni produkt vektora, a
a =
_
a
2
1
+ a
2
2
+ a
2
3
32 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C
norma vektora. Vektori su okomiti ukoliko je kosinus kuta medu njima jednak
nuli. Budu´ci da se u raˇcunu kosinusa pojavljuju greˇske zaokruˇzivanja, uzimat
´cemo da su vektori okomiti ˇcim je kosinus njihovog kuta manji od nekog malog
broja ε, u naˇsem primjeru 10
−10
.
#include <stdio.h>
#include <math.h>
double epsilon=1.0E-10;
double kosinus_kuta(double x[], double y[]);
int main(void)
{
double a[3], b[3];
double cos_phi;
int i;
printf("Unesite vektor a.\n");
for(i=0;i<3;++i){
printf("a[%d]= ",i+1);
scanf(" %lf",&a[i]);
}
printf("Unesite vektor b.\n");
for(i=0;i<3;++i){
printf("b[%d]= ",i+1);
scanf(" %lf",&b[i]);
}
cos_phi= kosinus_kuta(a, b);
if(fabs(cos_phi) < epsilon){
printf("Vektori su okomiti.\n");
printf("Kosinus kuta = %f\n", cos_phi);
}
else{
printf("Vektori nisu okomiti.\n");
printf("Kosinus kuta = %f\n", cos_phi);
}
return 0;
}
2.6. POLJA 33
double norma(double x[]) {
int i;
double suma;
suma=0.0;
for(i=0;i<3;++i) suma = suma + x[i]*x[i];
return sqrt(suma);
}
double produkt(double x[], double y[]) {
int i;
double suma;
suma=0.0;
for(i=0;i<3;++i) suma = suma + x[i]*y[i];
return suma;
}
double kosinus_kuta(double x[], double y[]) {
double cos_phi;
cos_phi=produkt(x,y);
cos_phi=cos_phi/(norma(x)*norma(y));
return cos_phi;
}
Program se sastoji od funkcije main, funkcije norma koja raˇcuna normu
vektora, funkcije produkt koja raˇcuna skalarni produkt dva vektora i funkcije
kosinus kuta koja raˇcuna kosinus kuta pozivaju´ci funkcije produkt i norma.
Ispred funkcije main naveden je prototip funkcije kosinus kuta
double kosinus_kuta(double x[], double y[]);
To je nuˇzno jer je funkcija definirana iza main funkcije, u kojoj se poziva.
S druge strane, funkcija kosinus kuta poziva funkcije produkt i norma, no
kako su one definirane ispred nje, prototip nije potreban.
Funkcija main zapoˇcine deklaracijom polja a i b:
double a[3], b[3];
34 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C
Uglate zagrade iza imena varijabli govore da je varijabla polje s tri elementa.
Op´cenita, definicija polja je oblika
tip ime[max];
gdje je tip tip varijable, max broj elemenata polja, a ime ime polja. Indeks
polja stavlja se unutar uglatih zagrada. Elementi polja ime su
ime[0], ime[1], ime[2], ... , ime[max-1]
U naˇsem primjeru deklarirali smo polja a i b, oba sa po tri element tipa
double. Elementi tih polja su a[0], a[1], a[2] i b[0], b[1], b[2].
U dijelu koda
printf("Unesite vektor a.\n");
for(i=0;i<3;++i){
printf("a[%d]= ",i+1);
scanf(" %lf",&a[i]);
}
printf("Unesite vektor b.\n");
for(i=0;i<3;++i){
printf("b[%d]= ",i+1);
scanf(" %lf",&b[i]);
}
uˇcitavaju se dva vektora, element po element. Pri ispisu
printf("a[%d]= ",i+1);
pomaknuli smo indeks i za jedan kako bi korisnik imao dojam da komponente
vektora imaju indekse 1, 2 i 3. Stvarni indeksi u C-u uvijek poˇcinju s nulom.
Elemente polja indeksiramo cjelobrojnom varijablom. Izraz a[i] je i+1-
vi element poja (jer prvi ima indeks i=0). On predstavlja jednu varijablu tipa
double i moˇze se javiti u svim izrazima u kojima se moˇze pojaviti varijabla
tipa double. Tako, na primjer, izraz &a[i] daje adresu i+1-vog elementa
poja a.
U liniji
cos_phi= kosinus_kuta(a, b);
poziva se funkcija kosinus kuta. Njena definicija je dana iza main-a:
2.6. POLJA 35
double kosinus_kuta(double x[], double y[]) {
double cos_phi;
cos_phi=produkt(x,y);
cos_phi=cos_phi/(norma(x)*norma(y));
return cos_phi;
}
Funkcija uzima dva argumenta tipa polja. Ti su argumanti deklarirani s
uglatim zagradam koje oznaˇcavaju da se radi o poljima. Dimenzije polja
nisu navedene, jer to u deklaraciji argumenata funkcije nije potrebno.
Kod poziva funkcije formalni argumenti x i y zamijenjuju se imenima
polja a i b, definiranim u funkciji main. To su stvarni argumenti funkcije
i sve operacije koja funkcija obavlja nad poljima obavljaju se nad stvarnim
argumentima.
Treba dobro uoˇciti razliku izmedu definicije varijable tipa polja i deklara-
cije argumenta tipa polja u nekoj funkciji. Kad se definira varijabla obavezno
navodimo njezinu dimenziju, kako bi prevodilac mogao rezervirati dovoljno
memorijskog prostora za nju. Definicije poput
double x[]; /* POGRESNO */
predstavljaju greˇsku. U deklaraciji argumenta tipa polja dimenziju ne treba
navoditi jer za taj argument ne´ce biti rezerviran memorijski prostor. Funk-
cija ´ce vrˇsiti operacije nad stvarnim argumentima. Pri tome je odgovornost
programera da osigura da stvarni argument funkcije bude odgovaraju´ce di-
menzije. Ipak, nije pogreˇsno deklarirati argumente tipa polja s njihovim
dimenzijama. Tako smo mogli pisati
double kosinus_kuta(double x[3], double y[3])
{
......
}
U implementaciji funkcija norma i produkt koristili smo varijablu istog
imena: suma. Svaka varijabla definirana unutar tijela funkcije je lokalna
varijabla. Ona postoji samo za vrijeme izvrˇsavanja tijela funkcije. U nekoj
drugoj funkciji lokalnoj varijabli moˇzemo dati isto ime jer samim time ˇsto su
definirane u razliˇcitim funkcijama, te su varijable posve razliˇcite. Isto vrijedi
i za argumente funkcije, tako da razliˇcite funkcije mogu imati argumente
istog imena.
U zavrˇsnom dijelu funkcije main
36 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C
if(fabs(cos_phi) < epsilon){
printf("Vektori su okomiti.\n");
printf("Kosinus kuta = %f\n", cos_phi);
}
else{
printf("Vektori nisu okomiti.\n");
printf("Kosinus kuta = %f\n", cos_phi);
}
ispitujemo da li je apsolutna vrijednost kosinusa manja od epsilona i ispi-
sujemo odgovaraju´cu poruku. Uoˇcimo da je varijabla epsilon definirana
ispred funkcije main i tamo je inicijalizirana s 10
−10
.
Varijabla definirana ispred funkcije main je globalna varijabla. Njeno po-
druˇcje djelovanja je od mjesta definicije do kraja datoteke u kojoj je defini-
rana. Takvim se varijablama moˇze pristupiti u svim funkcijama definiranim
u datoteci. Tako varijabli epsilon moˇzemo pristupiti i u main i u svim
ostalim funkcijama.
Napomena. Realne konstante s eksponentom piˇsu se pomo´cu slova E koje
razdvaja mantisu i eksponent. Na primjer, 5, 23 ×10
4
je 5.23E4 itd.
2.6.1 <math.h>
Funkcije poput printf i scanf nisu dio programskog jezika C ve´c pri-
padaju standardiziranim bibliotekama funkcija kojima je opskrbljen svaki C
prevodilac. Prototipovi funkcija iz standardnih biblioteka nalaze se u sistem-
skim datotekama zaglavlja koje je potrebno ukljuˇciti u svaki program koji ih
koristi. Ako koristimo matematiˇcke funkcije iz standardne biblioteke, onda
moramo ukljuˇciti zaglavlje <math.h> i pri kompilaciji na kraj komandne linije
staviti -lm opciju.
Biblioteka matematiˇckih funkcija sadrˇzi brojne funkcije. Izmedu ostalih
sin, cos, tan (tg), asin (arcsin), acos (arc cos), atan (arc tg), exp (e
x
), log
(ln), log10 (log) itd. Sve one uzimaju jedan argument tipa double i vra´caju
rezultat tipa double.
U C-u nema operatora potenciranja pa zato postoji funkcija pow:
double pow(double,double)
koja raˇcuna pow(x, y) = x
y
. Ona daje greˇsku ako je x = 0 i y ≤ 0 te x < 0 i
y nije cijeli broj.
Treba razlikovati funkciju abs koja uzima argument tipa int i vra´ca ap-
solutnu vrijednost broja (tipa int) od funkcije fabs koja uzima double i
vra´ca apsolutnu vrijednost (tipa double).
2.7. POKAZIVA
ˇ
CI 37
2.7 Pokazivaˇci
Svaka varijabla deklarirana u C programu ima svoju adresu koju je mogu´ce
dohvatiti putem adresnog operatora &. Na primjer,
#include <stdio.h>
int main(void) {
double x=5;
printf("x = %f, adresa od x= %p\n",x,&x);
return 0;
}
Program ´ce ovisno o raˇcunalu na kojem se izvodi ispisati neˇsto kao
x=5.000000, adresa od x= 0065FDF0
Adresa varijable x ispisana je heksadecimalno; znak konverzije %p sluˇzi za
ispis adresa.
Adresu varijable nekog tipa moˇzemo zapamtiti u varijabli koji je tipa
pokazivaˇc na dani tip. Na primjer, program
#include <stdio.h>
int main(void) {
double x=5;
double *px;
px=&x;
printf("x = %f, adresa od x= %p\n",x,px);
return 0;
}
daje posve isti rezultat kao i prethodni program. Deklaracija
double *px;
definira varijablu px kao pokazivaˇc na tip double. Zvjezdica (*) prije imena
varijable govori da se ne radi o varijabli tipa double nego o pokazivaˇcu na
tip double. Pokazivaˇc na tip double moˇze sadrˇzavati samo adrese varijabli
tipa double. U naredbi
38 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C
px=&x;
u varijablu px smjeˇsta se adresa varijable x.
Vrijednost na koju pokazivaˇc pokazuje moˇzemo dohvatiti putem operatora
dereferenciranja *. Na primjer,
double x=5,y; <-- varijable tipa double
double *px; <-- pokazivac na tip double
px=&x; <-- px sada ima adresu varijable x
y=*px; <-- y prima vrijednost varijable x (=5)
Na primjer, prethodni program bismo mogli zapisati u obliku
#include <stdio.h>
int main(void) {
double x=5;
double *px;
px=&x;
printf("x = %f, adresa od x= %p\n",*px,px);
return 0;
}
Uoˇcimo da * ima razliˇcito znaˇcenje u deklaraciji i u izvrˇsnoj naredbi. U
deklaraciji varijable ona ukazuje da je varijabla tipa pokazivaˇc na dani tip.
U izvrˇsnoj naredbi ona predstavlja operator dereferenciranja.
Skalarni argumenti prenose se funkciji “po vrijednosti”, ˇsto znaˇci kopira-
njem. U primjeru,
double x,y;
.....
x=2.0;
y=sin(x);
......
prevodilac ´ce kopirati vrijednost varijable x u formalni argument funkcije
sin, koja ´ce na osnovu te vrijednosti izraˇcunati sinus. Na taj naˇcin funkcija
ne moˇze (namjerno ili nenamjerno) promijeniti vrijednost varijable x koja
joj je dana kao argument. To je vrlo korisno svojstvo koje u ovom sluˇcaju
osigurava da raˇcunanje vrijednosti sin(x) ne´ce promijeniti vrijednost argu-
menta x. Ipak, u nekim sluˇcajevima ˇzelimo da funkcija promjeni vrijednost
2.7. POKAZIVA
ˇ
CI 39
svog argumenta, kao kod funkcije scanf. U takvim sluˇcajevima moramo se
posluˇziti pokazivaˇcima. Funkciji dajemo kao argument pokazivaˇc koji sadrˇzi
adresu varijable koju treba promijeniti. Ona ne´ce mo´ci promijeniti vrijed-
nost pokazivaˇca, ali ´ce putem operatora dereferenciranja mo´ci promijeniti
vrijednost na koju pokazivaˇc pokazuje.
Pogledajmo sljede´ci primjer. Treba napisati funkciju koja uzima niz zna-
kova i u njemu nalazi prvi samoglasnik. Funkcija treba vratiti nadeni sa-
moglasnik i mjesto u nizu na kojem je naden. Ukoliko u danom nizu nema
samoglasnika, umjesto mjesta u nizu vra´ca se -1.
U ovom primjeru funkcija mora vratiti dvije vrijednosti. Kroz return
naredbu moˇze se vratiti samo jedna vrijednost, tako da se druga mora vra-
titi kroz argument funkcije. (Funkcija ne moˇze vratiti polje.) To moˇzemo
realizirati na sljede´ci naˇcin:
#include <stdio.h>
int trazi(char linija[], int n, char *psamoglasnik)
{
int i;
char c;
for(i=0; i<n; i++){
c=linija[i];
if(c == ’a’ || c == ’e’ || c == ’i’
|| c == ’o’ || c == ’u’)
{
*psamoglasnik=c;
return i;
}
}
return -1;
}
Funkcija trazi uzima niz znakova linija, cijeli broj n koji daje broj zna-
kova u nizu linija i pokazivaˇc na varijablu tipa char, koji smo nazvali
psamoglasnik (pointer na samoglasnik). Nadeni samoglasnik ´ce biti vra´cen
kroz argument psamoglasnik, a mjesto na koje je naden vra´ca se kroz return
naredbu.
Varijabla tipa char sluˇzi za pam´cenje pojedinaˇcnih znakova. Kada treba
memorirati viˇse znakova koristimo se poljima tipa char. Znakovne konstante
formiraju se tako da se znak stavi u jednostruke navodnike. Na primjer, ’a’,
’/’, ’?’ itd.
40 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C
U for petlji prolazimo kroz ˇcitav niz znakova. Za svaki znak u if na-
redbi ispitujemo da li je samoglasnik. Uoˇcimo da smo pet testova spojili
operatorom logiˇcko ILI.
Svi logiˇcki operatori dani su u ovoj tabeli:
Operator Znaˇcenje
&& logiˇcko I
|| logiˇcko ILI
! logiˇcka negacija (unarno)
Ako je samoglasnik naden, u naredbi
*psamoglasnik=c;
se znak c=linija[i] kopira na lokaciju na koju pokazuje psamoglasnik.
Nakon toga se u pozivni program vra´ca poloˇzaj znaka koji je dan varijablom
i. Ukolikoje petlja for izvrˇsena a da samoglasnik nije naden, vra´ca se -1, a
lokacija na koju pokazuje psamoglasnik se ne mijenja. Poziv funkcije trazi
mogao bi izgledati ovako:
int main(void)
{
char ime[]={’P’,’r’,’o’,’g’,’r’,’a’,’m’,’s’,’k’,’i’,’ ’,
’j’,’e’,’z’,’i’,’k’,’ ’,’C’,’.’};
char znak;
int no;
no=trazi(ime,19,&znak);
if(no != -1){
printf("Prvi samoglasnik = %c\n",znak);
printf("Nalazi se na mjestu %d\n",no);
}
else printf("Nema samoglasnika.\n");
return 0;
}
Varijabla u koju smjeˇstamo nadeni samoglasnik je znak. Njena se adresa
predaje funkciji trazi.
U deklaraciji polja ime susre´cemo se s inicijalizacijom polja. Svako se
polje prilikom deklaracije moˇze inicijalizirati tako da se nakon znaka jedna-
kosti u vitiˇcastim zagradama navedu svi elementi polja, odvojeni zarezom.
Dimenziju polja ne moramo nuˇzno navesti jer ´ce ju prevodilac sam izraˇcunati
na osnovu inicijalizacijskog niza.
2.8. POLJA ZNAKOVA 41
2.8 Polja znakova
Napiˇsimo program koji uˇcitava niz znakova limitiran bjelinama, raˇcuna
broj numeriˇckih znakova u nizu i ispisuje uˇcitani niz u obrnutom poretku.
#include <stdio.h>
#include <string.h>
#define MAX 128
void inv(char []);
int main(void) {
char niz_znakova[MAX],c;
int i,brojac=0;
scanf("%s", niz_znakova);
i=0;
while((c=niz_znakova[i]) !=’\0’) {
if(c>=’0’ && c<=’9’) ++brojac;
i++;
}
printf("Broj ucitanih numerickih znakova = %d\n",brojac);
inv(niz_znakova);
printf("%s\n",niz_znakova);
return 0;
}
void inv(char s[]) {
int c,i,j;
for(i=0,j=strlen(s)-1; i<j; i++,j--) {
c=s[i]; s[i]=s[j]; s[j]=c;
}
}
Program zapoˇcinje ukljuˇcivanjem standardnih datoteka zaglavlja <stdio.h>
(zbog printf i scanf) te <string.h> (zbog funcije srtlen). Zatim
#define MAX 128
uvodi simboliˇcku konstantu MAX koja ima vrijednost 128.
42 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C
Naredba #define je preprocesorska naredba. Preprocesor je program koji
prolazi kroz izvorni kod prije prevodioca i izvrˇsava naredbe koje su mu na-
mijenjene. Na primjer, #include je preprocesorska naredba kojom se u
datoteku, na onom mjestu na kojem se nalazi, ukljuˇcuje sadrˇzaj datoteke
navedene u #include liniji. #define je prepprocesorska naredba kojom se
definira simboliˇcko ime. U naˇsem primjeru ime je MAX, a vrijednost koja mu
se pridruˇzuje je 128. Preprocesor nalazi u programu svako pojavljivanje sim-
bola MAX i zamijenjuje ga sa 128. Na taj naˇcin umjesto konstanti moˇzemo
koristiti simboliˇcka imena.
Linija
void inv(char []);
je prototip funkcije inv koja je definirana iza main funkcije. Funkcija je tipa
void ˇsto znaˇci da ne vra´ca nikakvu vrijednost u pozivni program. Ona ´ce
vrˇsiti inverziju niza znakova.
Argument funkcije inv je niz znakova. Vidjeli smo da pri deklaraciji polja
kao argumenta funkcije dimenziju polja ne moramo navesti. U ovom primjeru
vidimo da u prototipu niti ime varijable nije vaˇzno, pa je stoga ispuˇsteno.
Vaˇzna je samo prisutnost uglatih zagrada koje signaliziraju da je argument
polje tipa char.
Varijabla niz znakova iz funkcije main je polje od MAX (=128) znakova
tj. varijabli tipa char. Ti su znakovi smjeˇsteni jedan do drugog u memoriji
i indeksirani su od 0 do 127.
Deklaracije obiˇcnih varijabli i polja moˇzemo slobodno mijeˇsati (ako su
istog tipa) te smo stoga mogli pisati
char niz_znakova[MAX],c;
umjesto
char niz_znakova[MAX];
char c;
U deklaraciji varijabli i i brojac varijabla brojac je inicijalizirana nulom.
int i,brojac=0;
Takva inicijalizacija kod same deklaracije varijable mogu´ca je za sve vrste
varijabli. Na primjer, varijablu tipa char moˇzemo iniijalizirati znakom a na
ovaj naˇcin:
char c=’a’;
Prva izvrˇsna naredba je
2.8. POLJA ZNAKOVA 43
scanf("%s", niz_znakova);
koja uˇcitava niz znakova u varijablu niz znakova. Znak konverzije "%s"
oznaˇcava da se uˇcitava niz znakova.
Znakovni niz (ili string) predstavlja poseban tip podatka u C-u. Kons-
tantan znakovni niz (konstantan string) je svaki niz znakova smjeˇsten u dvos-
truke navodnike, kao npr. ”Dobar dan.\n”. To je znakovni niz koji se sastoji
od sljede´cih znakova:
D o b a r d a n . \n \0
Uoˇcimo da je na kraj niza dodan nul-znak ’\0’ koji ima ulogu signaliziranja
kraja niza znakova. Znakovni niz se time razlikuje od niza znakova. Niz
znakova je niz bilo kakvih znakova, a znakovni niz (string) je niz znakova ˇciji
je zadnji znak nul-znak.
U datoteci zaglavlja <string.h> deklariran je niz funkcija namjenjenih
radu sa stringovima. Jedna od njih je i strlen koja vra´ca duljinu niza
znakova, ne raˇcunaju´ci nul-znak. tako je strlen("Dobar dan.\n")=11.
Deskriptor "%s" oznaˇcava da funkcija scanf mora uˇcitati niz znakova u
varijablu niz znakova i dodati \0 iza posljednjeg znaka. Uˇcitavanje niza
znakova sa ulaza prestaje s prvom bjelinom. Pod bjelinama se osim bjelina
unesenih razmaknicom smatraju i znakovi uneseni tipkama Tab (tabulator)
i Enter (prijelaz u novi red).
Uoˇcimo da ispred varijable niz znakova u pozivu funkciji scanf nema
adresnog operatora &. To je op´cenito pravilo za nizove. Kada je niz stvarni
argument funkcije ona ne dobiva kopiju ˇcitavog niza ve´c adresu prvog ele-
menta u nizu. U funkciji se ˇclanovi niza mogu dohvatiti (i mijenjati) isto kao
i u pozivnom programu pomo´cu indeksa.
U C-u izraz pridruˇzivanja ima vrijednost koja je jednaka vrijednosti lijeve
strane nakon pridruˇzivanja. Stoga izrazi poput
c=niz_znakova[i]
mogu sudjelovati u sloˇzenijim izrazima. U while petlji
i=0;
while((c=niz_znakova[i]) !=’\0’) {
if(c>=’0’ && c<=’9’) ++brojac;
i++;
}
prvo se varijabli c pridruˇzuje niz znakova[i], a zatim se vrijednost izraza
pridruˇzivanja (to je vrijednost varijable c) usporeduje s znakom ’\0’. Ope-
rator != je logiˇcki operator usporedivanja. Rezultat usporedivanja
44 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C
c!=’\0’
je istina ako varijabla c ne sadrˇzi nul-znak ’\0’.
Gornja while petlja mogla je biti napisana i na sljede´ci naˇcin:
i=0;
c=niz_znakova[0];
while(c !=’\0’) {
if(c>=’0’ && c<=’9’) ++brojac;
i++;
c=niz_znakova[i];
}
Vidimo da koriˇstenje izraza pridruˇzivanja unutar sloˇzenijih izraza ˇcini kˆ od
kompaktnijim. Pri tome su bitne zagrade je operatori pridruˇzivanja imaju
najniˇzi prioritet. Izraz
c=niz_znakova[i] !=’\0’
ekvivalentan je izrazu
c=(niz_znakova[i] !=’\0’)
koji bi pridruˇzio varijabli c vrijednost 0 (laˇz) ako niz znakova[i] sadrˇzi
znak ’\0’ ili 1 (istina) ako niz znakova[i] ne sadrˇzi znak ’\0’.
Relacijski izrazi kao ˇsto je to c != ’\0’ ili i<=10 imaju logiˇcku vrijed-
nost istine ili laˇzi koja se u C-u reprezentira cijelim brojem: 1 (i bilo koja
vrijednost razliˇcita od nule) predstavlja istinu, a 0 laˇz. To je razlog zbog
kojeg bi u prethodnom primjeru varijabla c dobila vrijednost 0 ili 1.
Naredbom
if(c>=’0’ && c<=’9’) ++brojac;
provjerava se da li je znakovna vrijednost smjeˇstena u varijablu c broj. Pri
tome koristimo ˇcinjenicu da su brojevi u skupu znakova poredani u prirodnom
poretku (isto vrijedi i za slova engleske abecede).
Po zavrˇsetku while petlje iz funkcije main varijabla brojac sadrˇzi broj
numeriˇckih znakova u znakovnom nizu. Uoˇcimo da smo pisali ++brojac
umjesto brojac++. Operatore inkrementiranja i dekrementiranja moˇzemo
pisati prije i poslije varijable. Iako medu tim zapisima postoji razlika koju
´cemo nauˇciti kasnije, oba ova oblika pove´cavaju varijablu za jedan.
Funkcija inv sastoji se od jedne for petlje
for(i=0,j=strlen(s)-1; i<j; i++,j--)
2.8. POLJA ZNAKOVA 45
u kojoj se inicijaliziraju dvije varijable: i koja se pozicionira na poˇcetak
stringa i j koja se pozicionira na kraj. Viˇsetruke inicijalizacije odvajaju
se zarezom. Sliˇcno, u dijelu u kome se mijenja brojaˇc petlje mijenjaju je
oba brojaˇca: i se pove´cava, a j se smanjuje. Ove se dvije naredbe ponovo
odvajaju zarezom. Petlja se izvrˇsava sve dok je izraz i<j istinit. Unutar
tijela petlje vrˇsi se zamjena znakova
c=s[i]; s[i]=s[j]; s[j]=c;
koji su na pozicijama i i j. Pri toj zamjeni morali smo koristiti pomo´cnu
varijablu c. Evidentno je da ´ce na izlazu iz for petlje poredak znakova u
znakovnom nizu biti invertiran. Pri tome posljednji nul-znak nismo pomicali
s njegovog mjesta.
Poglavlje 3
Konstante i varijable
3.1 Znakovi
C koristi sljede´ci skup znakova: velika i mala slova engleske abecede A-Z
i a-z, decimalne znamenke 0-9 te specijalne znakove
+ - * / = % & #
! ? ^ " ’ ~ \ |
< > ( ) [ ] { }
: ; . , _ (bjelina)
Pod bjelinom se podrazumijeva, osim sam bjeline, horizontalni i vertikalni
tabulator te znak za prijelaz u novi red. Mnoge verzije jezika C dozvoljavaju
da i drugi znakovi budu ukljuˇceni unutar stringova i komentara.
3.2 Komentari
U program je potrebno umetati komentare radi lakˇseg razumijevanja nje-
govog funkcioniranja. Minimalno je potrebno komentirati svaku funkciju u
programu, objaˇsnjavaju´ci zada´cu funkcije, njene argumente i vrijednost koju
vra´ca.
Komentar zapoˇcinje znakom /* i zavrˇsava znakom */. Tekst izmedu tih
graniˇcnika ignorira se prilikom prevodenja programa u izvrˇsni kˆ od. Komentar
se moˇze umetnuti bilo gdje u programu i moˇze sadrˇzavati viˇse linija teksta.
/* Ovo je
komentar. */
Prevodilac zamijenjuje svaki komentar jednom bjelinom. Zamjena se vrˇsi prije prola-
ska preprocesora, tako da se mogu komentirati i preprocesorske naredbe.
46
3.3. IDENTIFIKATORI I KLJU
ˇ
CNE RIJE
ˇ
CI 47
Komentari ovog tipa mogu dovesti do gubitka kˆ oda u situacijama u kojim
je ispuˇsten jedan graniˇcnik. U primjeru
/* Ovo je prvi komentar.
x=72.0;
/* Ovo je drugi komentar. */
prvi komentar smo zaboravili zatvoriti. Prevodilac ´ce ignorirati text sve
do prvog znaka */ na koji naide, ˇsto rezultira gubitkom naredbe x=72.0;.
Jednako tako ne moˇzemo komentirati dio programa koji ve´c sadrˇzi komentar.
Na primjer, kˆ od
/*
x=72.0; /* Inicijalizacija */
y=31.0;
*/
je neispravan jer komentar zapoˇcet prvim /* graniˇcnikom zavrˇsava s prvim
*/ graniˇcnikom, ˇsto znaˇci u liniji
x=72.0; /* Inicijalizacija */
Prevodilac ´ce javiti sintaktiˇcku greˇsku jer zadnji */ graniˇcnik nema odgova-
raju´ceg /* graniˇcnika.
Standard C99 omogu´cava koriˇstenje drugog tipa komentara koji dolazi iz
Jave i C++a. Komentar zapoˇcinje znakom // i prostire se do kraja linije.
Na primjer,
int n; // brojac znakova
Prevodilac ignorira tekst od znaka // do kraja linije. Stariji prevodioci ne
prihva´caju ovaj naˇcin komentiranja.
Ako treba komentirati veliki dio programa koji u sebi sadrˇzi brojne komentare, najbolje
je koristiti preprocesor (vidi Poglavlje 8).
3.3 Identifikatori i kljuˇcne rijeˇci
Identifikatori su imena koja se pridruˇzuju razliˇcitim elementima programa
kao ˇsto su varijable polja i funkcije. Sastoje se od slova i brojeva u bilo kojem
poretku s ograniˇcenjem da prvi znak mora biti slovo. Velika i mala slova se
razlikuju i znak (underscore) smatra se slovom.
Duljina imena je proizvoljna premda je prema standardu C90 prevodilac duˇzan pre-
poznati samo prvih 31 znakova. U standardu C99 ta je granica dignuta na 63 znaka. Na
imena vanjskih varijabli postoje ve´ca ograniˇcenja i ona ne bi trebala biti duˇza od 6 znakova
(C90), odnosno 31 (C99).
48 POGLAVLJE 3. KONSTANTE I VARIJABLE
Primjer 3.1 Primjeri pravilno napisanih identifikatora:
x y13 sum_1 _temp
names Pov1 table TABLE
Primjer 3.2 Primjeri nepravilno napisanih identifikatora:
3dan (prvi znak je broj)
"x" (nedozvoljeni znak ")
ac-dc (nedozvoljeni znak -)
Jedan broj rijeˇci, tzv. kljuˇcne rijeˇci, imaju posebno znaˇcenje u jeziku
i stoga se ne mogu koristiti kao identifikatori. Kljuˇcne rijeˇci prema C90
standardu su:
auto extern sizeof
break float static
case for struct
char goto switch
const if typedef
continue int union
default long unsigned
do register void
double return volatile
else short while
enum signed
Standard C99 uveo je i sljede´ce kljuˇcne rijeˇci:
inline Bool Imaginary
restrict Complex
U nekim verzijama jezika postoje i sljede´ce (nestandardne) kljuˇcne rijeˇci:
ada far near
asm fortran pascal
entry huge
Imena koja poˇcinju sa znakom treba izbjegavati jer su rezervirana za varijable i
funkcije iz standardne biblioteke.
3.4. OSNOVNI TIPOVI PODATAKA 49
3.4 Osnovni tipovi podataka
Osnovni tipovi podataka su
char, int, float, double.
Koliˇcina memorije koju pojedini tip zauzima ovisi o raˇcunalu. Mi navodimo
tipiˇcne vrijednosti.
int: cjelobrojni podatak. Tipiˇcno zauzima 4 okteta.
char: znakovni podatak. Sadrˇzava jedan znak iz sustava znakova koji raˇcunalo
koristi. U memoriji tipiˇcno zauzima jedan oktet.
float: broj s pokretnim zarezom u jednostrukoj preciznosti. U memoriji
tipiˇcno zauzima ˇcetiri okteta.
double: broj s pokretnim zarezom u dvostrukoj preciznosti. U memoriji tipiˇcno
zauzima osam okteta.
3.4.1 Cjelobrojni podaci
Cjelobrojni tipovi podataka sluˇze pam´cenju cijelih brojeva. Svaki tip
pokriva odreden raspon cijelih brojeva ovisno o koliˇcini memorije koju za-
uzima i naˇcinu kodiranja. Na primjer, ako varijabla odredenog integralnog
tipa zauzima n bitova memorije i naˇcin kodiranja je 2-komplement, onda je
pokriveni raspon od −2
n−1
do 2
n−1
−1.
Cjelobrojni tip int moˇze se modificirati pomo´cu kvalifikatora short i
long. Tako dobivamo nove cjelobrojne tipove:
short int ili kra´ce short
long int ili kra´ce long
short: “kratki” cjelobrojni podatak. U memoriji zauzuma manje memorijskog
prostora od podatka tipa int pa moˇze prikazati manji raspon cijelih
brojeva.
long: “dugi” cjelobrojni podatak. U memoriji zauzuma viˇse memorijskog
prostora od podatka tipa int pa moˇze prikazati ve´ci raspon cijelih
brojeva.
C propisuje samo minimalnu preciznost pojedinih cjelobrojnih tipova. Tako tip int
mora imati ˇsirinu najmanje 16 bitova, dok tip long mora imati minimalno 32 bita.
Cijeli brojevi tipa int, short i long pokrivaju podruˇcje pozitivnih i nega-
tivnih brojeva. Primjenom kvalifikatora unsigned dobivamo tipove podataka
50 POGLAVLJE 3. KONSTANTE I VARIJABLE
unsigned int ili kra´ce unsigned
unsigned short int ili kra´ce unsigned short
unsigned long int ili kra´ce unsigned long
koji zauzimaju isti memorijski prostor kao osnovni tipovi podataka (int,
short, long), a mogu reprezentirati samo pozitivne cijele brojeve (ukljuˇcivˇsi
nulu). Oni pokrivaju stoga pribliˇzno dvostruko ve´ci raspon pozitivnih cijelih
brojeva od osnovnih tipova.
Tip int je prirodan cjelobrojni podatak za dano raˇcunalo. Za njegovo
pam´cenje koristi se jedna raˇcunalna rijeˇc, ˇsto je u danaˇsnje vrijeme uglavnom
32 bita. Koriˇstenje ˇsirih cjelobrojnih tipova moˇze stoga imati za posljedicu
sporije izvrˇsavanje programa.
Koje su minimalne i maksimalne vrijednosti cjelobrojnih varijabli pojedi-
nih tipova? Odgovor je zavisan o raˇcunalu. Stoga postoji sistemska datoteka
zaglavlja <limits.h>
1
u kojoj su graniˇcne vrijednosti definirane. Na nekom
raˇcunalu dio sadrˇzaja datoteke <limits.h> mogao bi biti sljede´ci:
2
#define SHRT_MIN (-32768)
#define SHRT_MAX 32767
#define INT_MIN (-2147483648)
#define INT_MAX 2147483647
#define LONG_MIN (-9223372036854775808L)
#define LONG_MAX 9223372036854775807L
#define USHRT_MAX 65535
#define UINT_MAX 4294967295U
#define ULONG_MAX 18446744073709551615UL
(Oznake L,U i UL su objaˇsnjene u sekciji 3.6.) Ovdje su
• SHRT MIN, SHRT MAX: Minimalna i maksimalna vrijednost za tip short.
• INT MIN, INT MAX: Minimalna i maksimalna vrijednost za tip int.
• LONG MIN, LONG MAX: Minimalna i maksimalna vrijednost za tip long.
• USHRT MAX: Maksimalna vrijednost za tip unsigned short.
• UINT MAX: Maksimalna vrijednost za tip unsigned.
• ULONG MAX: Maksimalna vrijednost za tip unsigned long.
1
Na Unix sustavima ta se datoteka najˇceˇs´ce nalazi u /usr/include/ direktoriju.
2
Zaˇsto su negativne vrijednosti u zagradama objaˇsnjeno je u sekciji 8.
3.4. OSNOVNI TIPOVI PODATAKA 51
Ovdje imamo primjer raˇcunala na kojem tip short zauzima 2 okteta, tip int
4 okteat, a tip long 8 okteta. Stoga imamo sljede´ce raspone:
• unsigned short: 0,1,. . . , 2
16
−1 = 65535,
• short: −2
15
= −32768,. . . , -1,0,1,. . . , 2
15
−1 = 32767,
• unsigned: 0,1,. . . , 2
32
−1 = 4294967295,
• int: −2
31
= −2147483648,. . . , -1,0,1,. . . , 2
31
−1 = 2147483647,
• unsigned long: 0,1,. . . , 2
64
−1 = 18446744073709551615,
• long: −2
63
,. . . , -1,0,1,. . . , 2
63
−1.
Na vaˇsem raˇcunalu ovi rasponi mogu biti drugaˇciji. Sljede´ci program ispisuje
gornje vrijednosti:
#include <stdio.h>
#include <limits.h>
int main()
{
short s_max = SHRT_MAX;
short s_min = SHRT_MIN;
unsigned short us_max = USHRT_MAX;
int i_max = INT_MAX;
int i_min = INT_MIN;
unsigned ui_max = UINT_MAX;
long l_max = LONG_MAX;
long l_min = LONG_MIN;
unsigned long x_max = ULONG_MAX;
printf("SHRT_MAX = %hd\n",s_max);
printf("SHRT_MIN = %hd\n",s_min);
printf("USHRT_MAX = %hu\n",us_max);
printf("INT_MAX = %d\n",i_max);
printf("INT_MIN = %d\n",i_min);
printf("UINT_MAX = %u\n",ui_max);
printf("LONG_MAX = %ld\n",l_max);
printf("LONG_MIN = %ld\n",l_min);
printf("ULONG_MAX = %lu\n",x_max);
return 0;
}
52 POGLAVLJE 3. KONSTANTE I VARIJABLE
Uoˇcite kontrolne znakove s kojima se ispisuju pojedini integralni tipovi.
ˇ
Sto se deˇsava ako pokuˇsamo generirati cijeli broj ve´ci od najve´ceg ili
manji od najmanjeg dopuˇstenog? Da bismo to vidjeli dodajmo prethodnom
programu sljede´ce naredbe:
printf("SHRT_MAX+1 = %hd\n",s_max+1);
printf("SHRT_MIN-1 = %hd\n",s_min-1);
printf("USHRT_MAX+1 = %hu\n",us_max+1);
printf("INT_MAX+1 = %d\n",i_max+1);
printf("INT_MIN-1 = %d\n",i_min-1);
printf("UINT_MAX+1 = %u\n",ui_max+1);
printf("LONG_MAX+1 = %ld\n",l_max+1);
printf("LONG_MIN-1 = %ld\n",l_min-1);
printf("ULONG_MAX+1 = %lu\n",x_max+1);
Rezultat koji se dobiva je sljede´ci: kada se najve´cem broju bez predznaka
doda jedan dobiva se nula; kad se doda dva dobiva se jedan itd. Brojevi bez
predznaka su dakle “poredani u krug”, odnosno implementirana je aritmetika
modulo 2
n
, gdje je n ˇsirina tipa (u bitovima).
Kod brojeva s predznakom imamo sliˇcno ponaˇsanje. Dodavanje jedinice
najve´cem pozitivnom broju daje najve´ci negativni broj, a oduzimanje jedi-
nice od najve´ceg negativnog broja daje najve´ci pozitivni broj. I ovdje su
brojevi “poredani u krug”, ali ponaˇsanje ovisi o naˇcinu kodiranja negativnih
brojeva (2-komplement u naˇsem sluˇcaju).
Standard C99 uvodi dodatne tipove cijelih brojeva long long i unsigned long long.
Tip long long mora biti implementiran s minimalno 64 bita.
3.4.2 Znakovni podaci
Podatak tipa char namijenjen je pam´cenju individualnih znakova. Zna-
kovi se u raˇcunalu kodiraju i sve manipulacije sa znakovima obavljaju se
putem njihovih numeriˇckih kodova. Zbog toga je tip char ustvari cjelobrojni
tip podataka vrlo malog raspona.
Postoje razliˇciti naˇcini kodiranja znakova, Jedan od najstarijih je tzv.
ASCII kˆ od.
3
ASCII kˆ od ima numeriˇcki raspon od 0 do 127 i dovoljno je 7
bitova za njegovo pamˇcenje. Npr. slovo A (veliko) ima ASCII kˆ od 65, B ima
kˆ od 66 itd. Budu´ci da ASCII kˆ od ne sadrˇzi slova iz europskih jezika razvijeni
su razni drugi osam-bitni kodovi od kojih treba spomenuti ISO Latin 1 (za
zapadno europske jezike), ISO Latin 2 (za srednjoeuropske jezike) itd.
3
ASCII je kratica od “American Standard Code for Information Interchange”.
3.4. OSNOVNI TIPOVI PODATAKA 53
U C-u je char cjelobrojni tip podatka s vrlo malim rasponom. Najˇceˇs´ce
je to jedan oktet ˇsto je dovoljno za sve osam-bitne kodove. Na tip char
moˇzemo primijeniti kvalifikatore signed i unsigned kao i na ostale cjelo-
brojne tipove. Ako se za char koristi jedan oktet, onda tip unsigned char
poprima pozitivne cjelobrojne vrijednosti izmedu 0 i 255, dok tip signed
char poprima pozitivne i negativne cjelobrojne vrijednosti izmedu -127 (ili -
128) i 127. Pravilo je da se kvalifikatori signed i unsigned ne koriste s tipom
char ako on sluˇzi za manipulaciju sa znakovima, ve´c samo ako ga koristimo
kao cjelobrojni tip malog raspona.
ˇ
Sirina tipa char te njegova minimalna i
maksimalna vrijednost dani su u datoteci <limits.h>.
U datoteci zaglavlja <stddef.h> definiran je cjelobrojni tip wchar t, tzv. ˇsiroki zna-
kovni tip, koji je namijenjen kodiranju znakova iz razliˇcitih jezika.
3.4.3 Logiˇcki podaci
U ANSI C-u (standard C90) ne postoji poseban logiˇcki tip. Svaki cjelobrojni tip moˇze
preuzeti ulogu logiˇckog tipa tako ˇsto se vrijednost razliˇcita od nule interpretira kao istina,
a nula kao laˇz. Standard C99 uvodi poseban logiˇcki tip Bool koji prima samo vrijednosti
0 i 1 (“laˇz” i “istina”). I nadalje se cjelobrojne varijable mogu koristiti za prezentaciju
logiˇckih vrijednosti, ali upotreba novog tipa moˇze uˇciniti program jasnijim. Kljuˇcna rijeˇc
Bool je izabrana da se izbjegnu konflikti s aplikacijama koje su implementirale svoj tip
bool. Korisnik moˇze ukljuˇciti datoteku zaglavlja <stdbool.h> u kojoj se definira bool
kao sinonim za Bool te se uvode simboliˇcka imena true i false.
3.4.4 Realni podaci
Podaci tipa float, double i long double su realni brojevi koji zbog
specifiˇcnog naˇcina pam´cenja u raˇcunalu imaju naziv brojevi s pokretnim za-
rezom (eng. floating point numbers). Oni su analogni cjelobrojnim tipovima
short, int i long. Svaki sljede´ci tip pokriva ve´ci raspon realnih brojeva i
ima viˇsu preciznost (ve´ci broj znaˇcajnih znamenaka). Uobiˇcajeno je brojeve
tipa float, double i long double nazivati brojevima jednostruke, dvostruke
i ˇcetverostruke preciznosti. (Na pojedinim raˇcunalima double moˇze biti isto
ˇsto i long double, ˇsto se lako provjerava pomo´cu sizeof operatora [vidi
sekciju 4.2.2].)
Napomena. Mada su brojevi s pokretnim zarezom konaˇcan podskup
skupa realnih brojeva, radi kratko´ce ´cemo ih jednostavno zvati realnim bro-
jevima (ili realnim varijablama, konstantama, podacima).
Jezik ne propisuje ˇsirinu pojedinih tipova brojeva s pokretnim zarezom
ve´c to ovisi o raˇcunalu na kojem se program izvrˇsava. S druge strane, svojstva
sustava brojeva s pokretnim zarezom propisana su IEEE standardom kog
54 POGLAVLJE 3. KONSTANTE I VARIJABLE
se danas pridrˇzava ve´cina proizvodaˇca hardwarea. Stoga ´ce svojstva tipa
float i tipa double na ve´cini strojeva biti ona propisana IEEE standardom
(standard IEEE ne propisuje strogo brojeve ˇcetverostruke preciznosti).
Ovdje ne´cemo ulaziti u diskusiji brojeva s pokretnim zarezom. Nave-
dimo samo granice u kojima se ti brojevi kre´cu (prema IEEE standardu): U
jednostrukoj preciznosti (float) imamo
1.17549 · 10
−38
≈ 2
−126
≤ |x| ≤ (1 −2
−24
) · 2
128
≈ 3.40282 · 10
38
,
dok u dvostrukoj (double) vrijedi
2.225 · 10
−308
≈ 2
−1022
≤ |x| ≤ (1 −2
−53
) · 2
1024
≈ 1.798 · 10
308
.
Kao i u sluˇcaju cijelih brojeva vrijednosti ovisne o hardwareu stavljene
su u standardnu datoteku zaglavlja. U sluˇcaju brojeva s pokretnim zarezom
to je datoteka <float.h>. Ona sadrˇzi niz simboliˇckih konstanti koje daju
razliˇcite informacije o realnim tipovima podataka. Na nekom raˇcunalu dio
sadrˇzaja datoteke <float.h> mogao bi biti sljede´ci:
#define FLT_EPSILON 1.192092896E-07F
#define FLT_MIN 1.175494351E-38F
#define FLT_MIN_10_EXP (-37)
#define FLT_MAX 3.402823466E+38F
#define FLT_MAX_10_EXP (+38)
#define DBL_EPSILON 2.2204460492503131E-16
#define DBL_MIN 2.2250738585072014E-308
#define DBL_MIN_10_EXP (-307)
#define DBL_MAX 1.7976931348623157E+308
#define DBL_MAX_10_EXP (+308)
Konstante koje poˇcinju s FLT odnose se na tip float, dok se one s DBL odnose
na tip double. Ovdje redom imamo: strojni epsilon, najmanji (normalizi-
rani) broj s pokretnim zarezom, najmanji dekadski eksponent, najve´ci (nor-
malizirani) broj s pokretnim zarezom i najve´ci dekadski eksponent. Strojni
epsilon je udaljenost izmedu broja 1.0 i prvog ve´ceg broja s pokretnim zare-
zom. On je bitan utoliko ˇsto pri aproksimaciji realnog broja najbliˇzim brojem
s pokretnim zarezom
4
ˇcinimo greˇsku koja ne prelazi jednu polovicu strojnog
epsilona. On je stoga dobra mjera preciznosti brojevnog sustava.
Sustav brojeva s pokretnim zarezom sadrˇzi simboliˇcke veliˇcine +Inf i -Inf
koje predstavljaju plus i minus beskonaˇcno. Njih dobivamo kada raˇcunskim
4
Ukoliko je realan broj u podruˇcju koje pokrivaju brojevi s pokretnim zarezom.
3.5. VARIJABLE I DEKLARACIJE 55
operacijama izademo iz podruˇcja koje pokrivaju brojevi s pokretnim zare-
zom (eng. overflow). Operacija koja matematiˇcki nije dobro definirana (u
proˇsirenom skupu realnih brojeva) daje kao rezultat vrijednost NaN (eng.
Not a Number). Malu ilustraciju tih svojstava predstavlja sljede´ci program:
#include <stdio.h>
#include <float.h>
#include <math.h>
int main(void)
{
double x_max=DBL_MAX;
double x_min=DBL_MIN;
printf("DBL_MAX = %e\n",x_max);
printf("DBL_MAX = %e\n",x_max);
printf("\n");
printf("DBL_MAX*1.1 = %e\n",x_max*1.1);
printf("DBL_MIN/0 = %e\n",x_min/0);
printf("sqrt(-1) = %e\n",sqrt(-1.0));
return 0;
}
Uoˇcimo da smo morali ukljuˇciti datoteku zaglavlja <math.h> radi funkcije
sqrt(x)=

x. Kompilirati treba s opcijom -lm.
3.4.5 Kompleksni podaci
Kompleksni podaci su dodani jeziku tek u standardu C99. Standard uvodi tri kom-
pleksna tipa float Complex, double Complex i long double Complex. Na primjer,
jedna float Complex varijabla sastoji se od dvije float varijable koje reprezentiraju re-
alni i kompleksni dio broja. K tome se uvode joˇs i tri imaginarna tipa: float Imaginary,
double Imaginary i long double Imaginary. Ukljuˇcivanje zaglavlja <complex.h> sup-
stituira ime complex za Complex, imaginary za Imaginary te I za

−1.
3.5 Varijable i deklaracije
Varijabla je objekt koji zauzima dio memorijskog prostora i koji se moˇze
dohvatiti putem svog imena. Varijabla ima tri atributa o kojim ovisi kako ´ce
se podatak pohranjen u memoriji interpretirati: to su tip , memorijska klasa
varijable i doseg. Memorijska klasa varijable je najˇceˇs´ce odredena implicitno
poloˇzajem varijable u programu i stoga ´cemo ju za sada zanemariti (vidi
sekciju 9.2 gdje ´ce biti viˇse rijeˇci od memorijskoj klasi i dosegu).
56 POGLAVLJE 3. KONSTANTE I VARIJABLE
Prije upotrebe varijablu je potrebno deklarirati. Deklaracija odreduje ime
i tip varijable i ima sljede´ci oblik:
tip ime;
gdje je tip varijable, a ime njeno ime. Na primjer, u deklaracijama
int a,b;
unsigned c;
char d;
a i b su cjelobrojne varijable, c je cjelobrojna varijabla bez predznaka, a d je
znakovna varijabla. Varijable istog tipa mogu´ce je deklarirati u istom retku
kao u primjeru
short a,b,c;
ili svaku zasebno
short a;
short b;
short c;
Varijable se deklariraju na poˇcetku funkcije, prije prve izvrˇsne naredbe.
Mogu´ce je varijablu deklarirati i izvan svih funkcija, obiˇcno na poˇcetku dato-
teke i tada takve varijable nazivamo vanjskim varijablama (vidi sekciju 9.1).
Kada je varijabla deklarirana unutar neke funkcije (npr. main ili neke druge)
onda se na mjestu deklaracije rezervira memorijski prostor za varijablu. Dek-
laracija varijable je tada ujedno i njena definicija. Kod vanjskih varijabli po-
jam deklaracije i definicije varijable mogu se razlikovati kao ˇsto ´cemo vidjeti
u sekciji 9.1. Varijable definirane unutar neke funkcije i izvan svih funkcija
razlikuju se po svom dosegu. Doseg varijable je dio programa u kojem je va-
rijabla vidljiva, odnosno, dio programa u kojem joj se moˇze pristupiti putem
njenog imena. Doseg varijable definirane u nekoj funkciji je tijelo te funkcije.
Iz drugih funkcija toj se varijabli ne moˇze pristupiti. Nasuprot tome, varija-
bli deklariranoj izvan svih funkcija (vanjskoj varijabli) moˇze se pristupiti iz
svake funkcije (vidi sekciju 9.1).
3.5.1 Polja
Polje je niz varijabli istog tipa indeksiranih cjelobrojnim indeksom koji
se kre´ce u rasponu od 0 do n-1, gdje je n broj elemenata polja. Ako
je npr. vektor polje od 10 elementa nekog tipa, onda su elementi polja
vektor[0], vektor[1], vektor[2],. . . , vektor[9]. Za indeksiranje polja
koristimo uglate zagrade i poˇcetna vrijednost indeksa je uvijek nula.
Deklaracija polja ima oblik:
3.5. VARIJABLE I DEKLARACIJE 57
tip ime[dimenzija];
gdje je tip tip podatka, ime ime polja, a dimenzija broj elemenata polja. Na
primjer, polje vektor od 10 realnih brojeva jednostruke preciznosti deklarira
se naredbom
float vektor[10];
Deklaracija polja moˇze biti dana zajedno s deklaracijama drugih varijabli kao
u primjeru
float x,vektor[10],y;
u kome su x i y varijable tipa float, a vektor polje od 10 elemenata tipa
float.
3.5.2 Pokazivaˇci
Svakoj varijabli u programu pridruˇzena je odredena memorijska lokacija
koju moˇzemo dohvatiti pomo´cu adresnog operatora &. Ako je v neka vari-
jabla, onda je &v njena adresa u memoriji. Adrese se pohranjuju i s njima
se manipulira pomo´cu posebnih varijabli koje se nazivaju pokazivaˇci (poin-
teri). Ukoliko ˇzelimo memorirati adresu npr. cjelobrojne varijable, moramo
deklarirati pokazivaˇc na cjelobrojnu varijablu. Sintaksa deklaracije je
tip_p *ime;
gdje je ime, ime pokazivaˇca, a * u deklaraciji oznaˇcava da identifikator ime
nije varijabla tipa tip p ve´c pokazivaˇc na varijablu tipa tip p. Na primjer,
u kˆ odu
int v;
int *pv;
.....
pv=&v;
deklarirana je cjelobrojna varijabla v i pokazivaˇc na cjelobrojnu varijablu,
pv. U liniji kˆ oda
pv=&v;
u pokazivaˇc pv spremljena je adresa varijable v.
Deklaracije varijabli nekog tipa i pokazivaˇca na isti tip mogu se pisati u
jednom retku kao u primjeru
58 POGLAVLJE 3. KONSTANTE I VARIJABLE
float u,*pu;
gdje je deklarirana varijabla u tipa float i pokazivaˇc pu na float.
Sadrˇzaj memorijske lokacije moˇze se dohvatiti pomo´cu pokazivaˇca. Ako
je pv varijabla tipa pokazivaˇc na neki tip podatka, recimo char, onda je *pv
znak spremljen na tu lokaciju. Tako moˇzemo imati
char *pv;
char v;
.....
v=’a’;
*pv=’b’;
printf("Na adresi %p smjesten je znak %c\n",&v,v);
printf("Na adresi %p smjesten je znak %c\n",pv,*pv);
.....
Vidimo da * ima dvije razliˇcite uloge: u deklaraciji varijable ona oznaˇcava
da je varijabla pokazivaˇckog tipa, a u izvrˇsnim naredbama ona predstavlja
operator dereferenciranja koji primijenjen na pokazivaˇc daje vrijednost na koju
pokazivaˇc pokazuje. Sintaksa deklaracije pokazivaˇca je u skladu s primjenom
* kao operatora dereferenciranja tako ˇsto deklaracija tipa
char *pv;
sugerira da je pv nakon dereferenciranja (*pv) tipa char.
Napomena. Prethodni primjer ve´c ukazuje na opasnost koja se jav-
lja upotrebom pokazivaˇca. Pomo´cu pokazivaˇca mogu´ce je unutar programa
pristupiti svakoj memorijskoj lokaciji koju je operacijski sustav dodijelio pro-
gramu. Tu se krije mogu´cnost (namjerne ili nenamjerne) korupcije memorije.
U gornjem primjeru nije pokazana linija kˆ oda u kojoj je pokazivaˇc inicijaliziran.
Pretpostavimo stoga da smo ga zaboravili inicijalizirati prije dereferenciranja
*pv=’b’;
Na koju memorijsku lokaciju ´ce biti upisan znak ’b’? Precizan odgovor ovisi
o memorijskoj klasi pokazivaˇca (vidi sekciju 9.2), no u sluˇcaju da pv sadrˇzi
neku sluˇcajnu vrijednost naˇs ´ce program pokuˇsati pisati na sluˇcajno odabranu
adresu. Posljedice toga mogu biti najrazliˇcitije, od prekida programa do
pogreˇsnog funkcioniranja. Radi se, u svakom sluˇcaju, o tipu greˇske koju je
teˇsko otkriti.
3.6. KONSTANTE 59
3.6 Konstante
C poznaje cjelobrojne, realne i znakovne konstante te konstantne zna-
kovne nizove.
Cjelobrojne konstante mogu biti zapisane u tri brojevna sustava: decimal-
nom (baza 10), oktalnom (baza 8) i heksadecimalnom (baza 16).
Decimalne cjelobrojne konstante sastavljene su od decimalnih znamenki
0 do 9. Ako konstanta sadrˇzi viˇse od jedne znamenke prva ne smije biti
jednaka nuli. Primjeri su
0 1 234 -456 99999
Decimalna toˇcka nije dozvoljena, tako da 10.0 nije cjelobrojna konstanta.
Oktalne cjelobrojne konstante dobivaju se kombinacijom decimalnih zna-
menki 0 do 7. Prva znamenka mora uvijek biti 0 ˇsto signalizira da se radi o
broju zapisanom u oktalnom sustavu. Primjeri su
0 01 -0651 077777
Decimalna toˇcka nije dozvoljena.
Heksadecimalne cjelobrojne konstante zapoˇcinju s 0x ili 0X i sastoji se od
kombinacije znamenki 0 do 9 i slova a do f (mala ili velika). Interpretacija
slova je sljede´ca:
a = 10
b = 11
c = 12
d = 13
e = 14
f = 15
Primjeri su
0x0 0x1 -0x7FFF 0xabcd
Decimalna toˇcka nije dozvoljena.
Sve numeriˇcke konstante moraju biti unutar granica odredenih tipom
podatka koji predstavljaju. Cjelobrojne konstante moraju stoga biti unutar
granica odredenih tipom int. Konstante tipa unsigned, long i unsigned
long mogu poprimiti vrijednosti ve´ce od obiˇcnih cjelobrojnih konstanti i
stoga moraju biti posebno oznaˇcene.
Konstanta tipa long formira se tako da se na kraj cjelobrojne konstante
doda slovo L (veliko ili malo); konstanta tipa unsigned formira se dodava-
njem slova U (veliko ili malo). Konstanta tipa unsigned long formira se
dodavanjem slova UL (veliko ili malo). Primjeri su
60 POGLAVLJE 3. KONSTANTE I VARIJABLE
Konstanta Tip
500000U unsigned (decimalna)
123456789L long (decimalna)
123456789ul unsigned long (decimalna)
0123456l long (oktalna)
0X50000U unsigned (heksadecimalna)
-5000ULL long long (decimalno)
50000ULL unsigned long long (decimalno)
U i L mogu se kombinirati u bilo kojem poretku. Znakovi konverzije za printf
funkciju vide se u sljede´cem primjeru:
unsigned a=20U;
long b=2000L;
unsigned long c=200000LU;
long long d=20000000LL;
unsigned long long e=2000000000ULL;
printf("unsigned = %u\n",a);
printf("long = %Ld\n",b);
printf("unsigned long = %lu\n",c);
printf("unsigned long = %lo\n",c);
printf("long long = %lld\n",d);
printf("unsigned long long = %llu\n",e);
Znakovne konstante. Znakovna konstanta je jedan znak u jednostrukim
navodnicima. Npr.
’A’ ’x’ ’5’ ’?’ ’ ’
(uoˇcimo da je i bjelina znak). Znakovne konstante imaju svoje cjelobrojne
vrijednosti koje su odredene naˇcinom kodiranja znakova. Koriˇstenjem kons-
tanti poput ’a’, ’3’ postiˇze se neovisnost o naˇcinu kodiranja znakova.
Jedan broj posebnih znakova moˇze se reprezentirati u C-u pomo´cu dva
znaka: prvi je obrnuta kosa crta \(eng. backslash), a drugi je neko od slova
ili znakova.
ˇ
Cesto koriˇsteni posebni znakovi su
3.6. KONSTANTE 61
Kod Znaˇcenje
\b povratak za jedno mjesto unazad (backspace)
\f nova stranica (form feed)
\n novi red (new line)
\r povratak na poˇcetak linije (carriage return)
\t horizontalni tabulator
\v vertikalni tabulator
\0 nul znak (null character)
\? upitnik
\” navodnik
\’ jednostruki navodnik
\\ obrnuta kosa crta (backslash)
Na primjer,
’\n’ ’\b’ ’\\’
Znakovne konstante mogu biti zadane svojim kodom koji se piˇse u obliku
\ooo, gdje je ooo troznamenkasti oktalni broj. Na primjer ’\170’ je znak
koji ima kod 170 oktalno, odnosno 120 decimalno (x u ASCII znakovima).
Sliˇcno, kodovi mogu biti zadani i heksadecimalno u obliku \xoo, gdje oo
dvoznamenkasti heksadecimalni broj.
Realna konstanta je broj zapisan u dekadskom sustavu s decimalnom
toˇckom. Npr.
0. 1. -0.2 5000.
Realne se konstante mogu pisati i s eksponentom i tada decimalna toˇcka nije
potrebna. Eksponent mora biti cijeli broj kome prethodi slove e (malo ili
veliko). Na primjer, broj 3 ×10
5
moˇze biti napisan na sljede´ce naˇcine:
300000. 3e5 3E+5 3.0e+5 .3e6 30E4
Uoˇcimo da ne smije biti razmaka izmedu mantise i eksponenta (npr. 3.0 e+5
nije ispravno napisana konstanta).
Tip svih realnih konstanti je double. Ukoliko se ˇzeli konstanta tipa float
na kraj joj se dodaje f ili F. Sliˇcno, realna konstanta koja nakraju ima l ili
L je tipa long double:
0.00fe-3f 1.345E+8F -0.2e3l 5000.0L
Kontrolni znakovi u printf funkciji mogu se vidjeti u sljede´cem primjeru:
62 POGLAVLJE 3. KONSTANTE I VARIJABLE
float x=-1.0F;
double y=2.0;
long double z=-0.2e8l;
long double w=-0.2e-8L;
printf("float = %f\n",x);
printf("double = %f\n",y);
printf("long double = %Lf\n",z);
printf("long double = %Le\n",w);
Kompleksne konstante se formiraju kao kombinacija realnih konstanti i imaginarne
jedinice Complex I (ili I), definirane u <complex.h>.
Konstantni znakovni nizovi (konstantni stringovi) su nizovi znakova nave-
deni unutar (dvostrukih) navodnika. Na primjer,
"Zagreb" "01/07/2001" "Linija 1\nLinija 2\nLinija3"
Specijalni znakovi kao \n i \t mogu biti ukljuˇceni u konstantne znakovne
nizove. Svakom znakovnom nizu automatski se na kraj dodaje nul-znak
(\0) kao oznaka kraja znakovnog niza. Na taj naˇcin algoritmi koji rade sa
znakovnim nizovima ne trebaju poznavati broj znakova ve´c prepoznaju kraj
niza pomo´cu nul znaka \0. To ima za posljedicu da su ’a’ i "a" dva razliˇcita
objekta. Prvo je jedan znak koji sadrˇzi slovo a, a drugo je znakovni niz koji
sadrˇzi slovo a, dakle niz od dva znaka: a i \0. Prazan niz znakova ”” sadrˇzi
samo nul-znak.
Ako je konstantan znakovni niz suviˇse dugaˇcak da bi stao u jednu liniju, onda imamo
dvije mogu´cnosti. Moˇzemo ga produˇziti u sljede´cu liniju ako ispred prijelaza u novi red
stavimo znak \. Prevodilac ´ce tada ignorirati kombinaciju znaka \ i znaka za prijelaz u
novi red. Druga mogu´cnost je iskoristiti svojstvi automatskog nadovezivanja konstantnih
znakovnih nizova: ako se dva takva niza nalaze jedan pored drugog, onda ´ce ih prevodilac
automatski nadovezati. U sljede´cem primjeru oba stringa su jednako inicijalizirana:
char s1[]="Vrlo dugacak \
niz znakova";
char s2[]="Vrlo dugacak "
"niz znakova";
Simboliˇcke konstante su imena koja preprocesor (vidi Poglavlje 8) sub-
stituira nizom znakova. Definiraju se na poˇcetku programa a sintaksa im
je
#define ime tekst
gdje je ime ime simboliˇcke konstante, a tekst niz znakova koji ´ce biti subs-
tituiran na svakom mjestu u programu na kojem se pojavljuje ime. Obiˇcno
se na taj naˇcin definiraju konstante kao npr.
3.7. INICIJALIZACIJA VARIJABLI 63
#define PI 3.141593
#define TRUE 1
#define FALSE 0
Ipak, u tu svrhu bolje je koristiti const varijable (sekcija 3.7).
3.7 Inicijalizacija varijabli
Varijable se mogu inicijalizirati u trenutku deklaracije. Sintaksa je
tip varijabla = konstantni izraz;
Na primjer,
int a=7,b;
unsigned c=2345;
char d=’\t’;
gdje su inicijalizirane varijable a, c i d, dok b nije inicijalizirana.
Sve deklaracije varijabli mogu nositi kvalifikator const koji indicira da
varijabla ne´ce nikad biti mijenjana, tj. da se radi o konstanti. Npr.
const double e=2.71828182845905;
Prevodilac ne´ce dozvoliti izmjenu varijable e u preostalom dijelu programa.
Polja znakova mogu se inicijalizirati konstantnim nizovima znakova. Tako
je deklaracijom
char tekst[]="Inicijalizacija";
definiran niz znakova tekst koji ima 16 znakova, 15 za rijeˇc Inicijalizacija
i jedan za nul-znak koji dolazi na kraju niza znakova. Uoˇcimo da nismo mo-
rali eksplicitno navesti dimenziju polja tekst. Ona ´ce biti izraˇcunata iz
stringa "Inicijalizacija". Alternativno smo mogli napisati
char tekst[16]="Inicijalizacija";
Op´cenito, polja se mogu inicijalizirati navodenjem vrijednosti elemenata
polja unutar vitiˇcastih zagrada:
double x[]={1.2,3.4,-6.1};
Dimenzija polja bit ´ce izraˇcunata na osnovu broja konstanti unutar zagrada.
Nakon ove deklaracije imat ´cemo
x[0]=1.2, x[1]=3.4, x[2]=-6.1
64 POGLAVLJE 3. KONSTANTE I VARIJABLE
3.8 Enumeracije
Enumeracije predstavljaju alternativu uvodenju simboliˇckih konstanti pu-
tem preprocesorske direktive #define. Pomo´cu enumeracije deklariramo
simboliˇcka imena koja primaju cjelobrojne vrijednosti i ˇcija je osnovna svrha
pove´canje ˇcitljivosti programa. Na primjer, nakon deklaracije
enum {FALSE, TRUE};
imena FALSE i TRUE moˇzemo koristiti u programu kao cjelobrojne konstante.
FALSE ima vrijednost 0, a TRUE 1. To ekvivalentno preprocesorskim nared-
bama
#define FALSE 0
#define TRUE 1
Princip je da se svakom imenu deklariranom u enumeraciji pridruˇzi jedan
cjeli broj: prvom se pridruˇzi nula, drugom 1 itd. Tako smo doobili FALSE=0,
TRUE=1.
Enumeraciji moˇzemo dati ime i tada moˇzemo deklarirati varijable tipa
enumeracije. To su varijable koje primaju samo konaˇcno mnogo vrijednosti
navedenih u enumeraciji. Prethodnu enumeraciju smo mogli deklarirati kao
enum boolean {FALSE, TRUE};
Varijable x i y tipa boolean definiraju se naredbom
enum boolean x,y;
Moˇzemo ih koristiti na sljede´ci naˇcin:
x=FALSE;
........
if(x==TRUE) y=FALSE;
itd. Varijable tipa enum pridonose razumljivosti programa. Koristimo ih za
one varijable koje primaju konaˇcno mnogo vrijednosti i za koje je poˇzeljno
uvesti simboliˇcka imena.
Op´cenito se enumeracija deklarira naredbom
enum ime {clan_1, clan_2,....,clan_n};
gdje je ime ime koje dajemo enumeraciji, a clan 1, clan 2,....,clan n
predstavljaju identifikatore koji se mogu pridruˇziti varijabli tipa enum ime.
Identifikatori moraju biti medusobno razliˇciti i razliˇciti od drugih identi-
fikatora u dosegu enumeracije. Identifikatorima se automatski pridruˇzuju
cjelobrojne vrijednosti
3.8. ENUMERACIJE 65
clan_1=0
clan_2=1
clan_3=2
.......
clan_n=n-1
Varijabla tipa enumeracije deklarira se naredbom
enum ime var_1, var_2,....,var_m;
Deklaracije enumeracije i varijabli tog tipa mogu se spojiti:
enum ime {clan_1, clan_2,....,clan_n}
var_1, var_2,....,var_m;
Vrijednosti koje se dodijeljuju identifikatorima mogu se modificirati kao
u sljede´cem primjeru
enum ESC {novi_red=’\n’, tabulator=’\t’};
ili
enum boje {plavo=-1,zuto,crveno,zeleno=0,ljubicasto,bijelo};
Time dobivamo
plavo=-1
zuto=0
crveno=1
zeleno=0
ljubicasto=1
bijelo=2
Vidimo da ´ce se u ovom sluˇcaju neke vrijednosti ponoviti.
Poglavlje 4
Operatori i izrazi
U ovom se poglavlju bavimo operatorima i izrazima koji se formiraju pomo´cu
operatora i operanada. Izostavit ´cemo operatore vezane uz strukture (Po-
glavlje 12), pokazivaˇce (Poglavlje 11) te operatore koji djeluju na bitovima
(Poglavlje 14). Paˇznju ´cemo posvetiti naˇcinu izraˇcunavanja izraza.
4.1 Aritmetiˇcki operatori
Programski jezik C poznaje pet aritmetiˇckih operatora:
Operator Znaˇcenje
+ zbrajanje
- oduzimanje
* mnoˇzenje
/ dijeljenje
% modulo
• Aritmetiˇcki operatori djeluju na numeriˇckim operandima tj. cjelobroj-
nim, realnim i znakovnim operandima (znakovna konstanta ima cjelo-
brojni tip).
• Operacije dijeljenja u sluˇcaju cjelobrojnih operanada ima znaˇcenje cje-
lobrojnog dijeljenje. Rezultatu dijeljenja odsjecaju se decimalne zna-
menke kako bi se dobio cjelobrojni rezultat. Ukoliko je rezultat dijelje-
nja negativan, onda prema standardu C90 naˇcin zaokruˇzivanja rezul-
tata ovisi o implementaciji. Najˇceˇs´ce je to odsjecanje, tj. zaokruˇzivanje
prema nuli. Standard C99 je propisao da se i u sluˇcaju negativnih
brojeva vrˇsi odsjecanje, tj. zaokruˇzivanje prema nuli. Ako bar jedan
od operanada nije cjelobrojan, onda je rezultat dijeljenja realan broj.
Nazivnik pri dijeljenju mora biti razliˇcit od nule.
66
4.1. ARITMETI
ˇ
CKI OPERATORI 67
• Operacija modulo (%) djeluje na cjelobrojnim operandima i kao rezul-
tat daje ostatak pri cjelobrojnom dijeljenju operanada. Na primjer,
ako je x = 10 i y = 3, onda je x/y = 3 (cjelobrojno dijeljenje) te
x%y = 1. Uvijek mora vrijediti da je (x/y) ∗y +(x%y) jednako x, tako
da ponaˇsanje operatora % na negativnim operandima ovisi o ponaˇsanju
operatora /.
• Operator potenciranja u C-u ne postoji. Umjesto njega koristi se funk-
cija pow iz matematiˇcke biblioteke.
Primjer. Uzmimo da je na primjer x = 13 i y = −3. Tada je prema stan-
dardu C99 x/y = −4 (odsjecanje) te x%y = 1 (predznak prvog operanda).
Stoga je (x/y) ∗ y + (x%y) = 13 = x.
4.1.1 Konverzije
Kada u aritmetiˇckom izrazu sudjeluju operandi razliˇcitih tipova onda
prije izraˇcunavanja izraza dolazi konverzije operanada u zajedniˇcki tip prema
odredenim pravilima. Op´cenito operandi ´ce biti konvertirani u najprecizniji
tip prisutan medu operandima i rezultat aritmetiˇckog izraza bit ´ce tog tipa.
Na primjer, ako je x varijabla tipa double, a n varijabla tipa int u izrazu
x+n, prije zbrajanja do´ci ´ce do konverzije varijable n u tip double i vrijednost
izraza bit ´ce tipa double.
Vrijede sljede´ca pravila:
• short i char (s predznakom ili bez) automatski se konvertiraju u int
prije svake aritmetiˇcke operacije. U sluˇcaju da je short isto ˇsto i int,
onda ´ce unsigned short biti ˇsiri od int, pa ´ce operandi biti konverti-
rani u unsigned int.
• U svakoj operaciji koja ukljuˇcuje operanda razliˇcitih tipova prije izvr-
ˇsenja operacije vrˇsi se konverzija operanada u najˇsiri tip.
• Tipovi su prema ˇsirini poredani na sljede´ci naˇcin (od najˇsireg prema
najuˇzem): long double, double, float, unsigned long long, long
long, unsigned long, long, unsigned int i int. Jedina iznimka je
kad su long i int isti. Tada je unsigned int ˇsiri od long. Uˇzi tipovi
se ne pojavljuju jer se oni automatski konvertiraju u int ili unsigned
int prema prvom pravili.
Prilikom ovih konverzija ne dolazi do gubitka informacije osim eventualno
u sluˇcaju kada se cjelobrojni tip konvertira u realni.
68 POGLAVLJE 4. OPERATORI I IZRAZI
Konverzija se deˇsava i kod operacije pridruˇzivanja (vidi sekciju 4.4) ako
su operandi s lijeve i desne strane razliˇcitog tipa. Operand na desnoj strani
konvertira se u tip operanda na lijevoj strani. Pri tome moˇze do´ci do gubitka
informacije ako se konvertira ˇsiri tip u uˇzi. Na primjer, ako je x varijabla tipa
float i n varijabla tipa int prilikom pridruˇzivanja i=x do´ci ´ce do odsjecanja
decimala u broju x.
Konverzija se deˇsava prilikom prenoˇsenja argumenata funkciji (vidi sek-
ciju 7.3). Naime, argumenti su izrazi i prije njihovog izraˇcunavanja dolazi do
konverzije ukoliko je ona potrebna. Pri tome vrijede razliˇcita pravila ovisno
o tome da li funkcija ima prototip ili ne.
• Ako funkcija nema prototipa, onda se svaki argument tipa char i short
transformira u int, a float u double.
• Ukoliko funkcija ima prototip, onda se svi argumenti pri pozivu ko-
nvertiraju (ako je to potrebno) u tipove deklarirane u prototipu (vidi
sekciju 7.2).
Vrijednost nekog izraza moˇze se eksplicitno konvertirati u zadani tip
pomo´cu operatora eksplicitne konverzije (tip) (eng. cast operator). Sin-
taksa konverzije je
(tip) izraz;
izraz ´ce biti izraˇcunat i njegova vrijednost konvertirana u tip unutar za-
grada. U primjeru
double x;
float y;
........
x= (double) y;
y je eksplicitno konvertiran u tip double prije pridruˇzivanja. Eksplicitne
konverzije mogu biti nuˇzne u sluˇcajevima kao ˇsto je sljede´ci:
int i;
double x;
((int) (i+x))%2;
4.1.2 Prioriteti i asocijativnost
Svi su operatori hijerhijski grupirani prema svom prioritetu. Operacija
iz grupe s viˇsim prioritetom izvrˇsit ´ce se prije operacija s niˇzim prioritetom.
Upotrebom zagrada mogu´ce je promijeniti redoslijed vrˇsenja operacija.
4.2. UNARNI OPERATORI 69
Operacije *, / i % spadaju u jednu prioritetnu grupu, dok operacije + i -
spadaju u niˇzu prioritetnu grupu (vidi sekciju 4.6).
Tako na primjer u izraz
2+4/2
dijeljenje ´ce biti izvrˇseno prije zbrajanja, pa je vrijednost izraza 4, a ne 3
ˇsto bi bilo da se prvo izvrˇsi zbrajanje. Ukoliko ˇzelimo zbrajane izvrˇsiti prije
dijeljenja treba pisati
(2+4)/2
ˇsto daje 3.
Zagrade koristimo kako bismo grupirali operande oko operatora. Ukoliko
ih nema, onda se grupiranje vrˇsi oko operatora najviˇseg prioriteta. Kada viˇse
operatora ima isti, najviˇsi, prioritet, onda naˇcin izraˇcunavanja izraza ovisi o
asocijativnosti. Asocijativnost moˇze biti slijeva na desno ili zdesna na lijevo.
Ako imamo, na primjer, dva operatora istog prioriteta, ˇcija je asocijativnost
slijeva na desno, onda ´ce se operandi prvo grupirati oko lijevog operanda. Na
primjer,
a - b + c je ekvivalntno s (a - b) + c
4.2 Unarni operatori
C ima jedan broj operatora koji djeluju na jednom operandu koje stoga
nazivamo unarni operatori.
Konstante i varijable mogu imati ispred sebe minus kao u primjerima
-345, -(x+y), gdje minus daje negativan predznak konstanti i mijenja pred-
znak varijable. Takav minus nazivamo unarni minus za razlike od binarnog
minusa koji predstavlja operaciju oduzimanja.
Operator konverzije tipa
(tip) izraz
kojeg smo sreli u sekciji 4.1 takoder je unarni operator.
Svi unarni operatori spadaju u istu prioritetnu grupu i imaju viˇsi prioritet
od aritmetiˇckih operatora. U izrazu
-x+y
´ce se stoga prvo izvrˇsiti unarni minus, a zatim zbrajanje.
Asocijativnost unarnih operatora je zdesna na lijevo ˇsto ´ce biti vaˇzno kad
uvedemo preostale unarne oparatore !,˜, * i &.
70 POGLAVLJE 4. OPERATORI I IZRAZI
4.2.1 Inkrementiranje, dekrementiranje
Operator inkrementiranja ++ pove´cava varijablu za jedan. Tako je izraz
x++;
ekvivalentan izrazu
x=x+1;
Operator dekrementiranja -- smanjuje vrijednost varijable za 1, odnosno
x--;
je ekvivalentno izrazu
x=x-1;
Izraz x++ i x-- mogu se pojaviti kao elementi sloˇzenijih izraza i zbog toga
imaju dvije forme. Moˇzemo pisati x++ ili ++x i isto tako x-- ili --x. U oba
sluˇcaja varijabla x se pove´cava (umanjuje) za 1. Razlika izmedu prefiks i
postfiks notacije pojavljuje se u sloˇzenim izrazima.
• U prefiks notaciji (++x, --x) varijabla ´ce biti promijenjena prije no ˇsto
´ce njena vrijednost biti iskoriˇsten u sloˇzenom izrazu;
• U postfiks notaciji (x++, x--) varijabla ´ce biti promijenjena nakon ˇsto
´ce njena vrijednost biti iskoriˇsten u sloˇzenom izrazu.
Na primjer, ako je x=3, onda izraz
y=++x;
daje y=4, x=4, a izraz
y=x++;
daje y=3, x=4.
Argumenti funkcije su izrazi koji se izraˇcunavaju prije no ˇsto se njihova
vrijednost prenese u funkciju. Prefiks i postfiks notacija operatora inkremen-
tiranja/dekrementiranja stoga i ovdje ima isto znaˇcenje. U primjeru
i=7;
printf("i= %d\n", --i);
printf("i= %d\n", i--);
printf("i= %d\n", i);
prva printf naredba ispisat ´ce 6, druga ´ce takoder ispisati 6, a tre´ca ´ce
ispisati 5. Razlog je to ˇsto se u drugoj printf naredbi vrijednost varijable i
prvo prenese funkciji, a zatim smanji za 1.
4.2. UNARNI OPERATORI 71
4.2.2 sizeof operator
sizeof operator vra´ca veliˇcinu svog operanda u bajtovima. U C-u se
jedan bajt definira kao broj bitova potreban za pam´cenje podatka tipa char.
To je na veˇcini raˇcunala jedan oktet (osam bitova).
Operand moˇze biti izraz ili tip podatka. Na primjer, kˆ od
int i;
float x;
printf("Velicina tipa int = %d\n", sizeof(i));
printf("Velicina tipa float = %d\n", sizeof(x));
bi na nekim sustavima ispisao
Velicina tipa int = 4
Velicina tipa float = 4
Isti efekt postiˇzemo ako sizeof primijenimo na tip podatka:
printf("Velicina tipa int = %d\n", sizeof(int));
printf("Velicina tipa float = %d\n", sizeof(float));
Kod sloˇzenijih podataka dobivamo ukupan broj okteta koji podatak za-
uzima. Ako imamo
char tekst[]="Dalmacija";
naredba
printf("Broj znakova u varijabli tekst =%d\n",
sizeof(tekst));
daje
Broj znakova u varijabli tekst =10
Operator sizeof vra´ca cjelobrojnu vrijednost bez predznaka koja ovisi o
implementaciji. Taj je tip definiran u datoteci zaglavlja <stddef.h> i zove
se size t.
72 POGLAVLJE 4. OPERATORI I IZRAZI
4.3 Relacijski i logiˇcki operatori
ˇ
Cetiri relacijska operatora su
Operator Znaˇcenje
< strogo manje
<= manje ili jednako
> strogo ve´ce
>= ve´ce ili jednako
Oni imaju isti prioritet, niˇzi od prioriteta aritmetiˇckih i unarnih operatora,
a asocijativnost im je slijeva na desno.
Operatori jednakosti su
Operator Znaˇcenje
== jednako
!= razliˇcito
Oni spadaju u zasebnu prioritetnu grupu s manjim prioritetom od relacijskih
operatora i asocijativnost im je slijeva na desno.
Pomo´cu ovih ˇsest operatora formiraju se logiˇcki izrazi. Njihova vrijednost
je istina ili laˇz. Kako u ANSI C-u (C90) ne postoji poseban logiˇcki tip po-
datka, logiˇcki izrazi su tipa int. (Standard C99 je uveo tip Bool). Logiˇcki
izraz koji je istinit dobiva vrijednost 1, a logiˇcki izraz koji je laˇzan prima
vrijednost 0. Na primjer:
Izraz Istinitost Vrijednost
i<j istinito 1
(i+j)>=k neistinito 0
i==2 neistinito 0
k!=i istinito 1
Budu´ci da je prioritet logiˇckih operatora niˇzi od prioriteta aritmetiˇckih ope-
ratora izraz poput
i >= ’A’-’a’+1
ekvivalentan je s
i >= (’A’-’a’+1)
Sloˇzeniji logiˇcki izrazi formiraju se pomo´cu logiˇckih operatora:
Operator Znaˇcenje
&& logiˇcko I
|| logiˇcko ILI
! logiˇcka negacija (unarno)
4.3. RELACIJSKI I LOGI
ˇ
CKI OPERATORI 73
Operandi logiˇckih operatora su logiˇcke vrijednosti (najˇceˇs´ce logiˇcki izrazi) s
tim da se svaka cjelobrojna vrijednost razliˇcita od nule interpretira kao istina, a
nula se interpretira kao laˇz. Vrijednost sloˇzenog logiˇckog izraza bit ´ce 0 (laˇz)
ili 1 (istina).
Svaki od ova dva operatora ima svoju prioritetnu grupu i prioritet im je
niˇzi od prioriteta relacijskih operatora i operatora jednakosti. Asocijativnost
im je slijeva na desno.
Primjer. Ako je izraz i>1 istinit, izraz c==’t’ istinit, a izraz j<6 laˇzan,
onda imamo:
Izraz Istinitost Vrijednost
i>1 || j<6 istinito 1
i>1 && j<6 neistinito 0
!(i>1) neistinito 0
i>1 || (j<6 && c!=’t’) istinito 1
Logiˇcki izrazi koji se sastoje od individualnih logiˇckih izraza povezanih
logiˇckim operatorima && i || izraˇcunavaju se slijeva na desno. Izraz se pres-
taje izraˇcunavati kad je njegova vrijednost poznata. To je vaˇznu u sljede´cim
situacijama: pretpostavimo da je x polje tipa char dimenzije n. Tada u
izrazu
for(i=0; i<n && x[i] !=’a’; ++i) {
....
}
ne´ce do´ci do ispitivanja x[i] !=’a’ ako je i=n (granica polja x ne´ce biti
prijedena). S druge strane, kˆ od
for(i=0; x[i] !=’a’ && i<n; ++i) {
....
}
bi ispitivao da li je x[n] razliˇcito od ’a’. To moˇze dovesti do nepredvidivog
ponaˇsanja programa.
Logiˇcka negacija koristi se ˇcesto u if naredbama. Konstrukcija
if(!istina)
ekvivalentan je s
if(istina==0)
i budu´ci da je kra´ca (za dva znaka) ˇcesto se koristi.
74 POGLAVLJE 4. OPERATORI I IZRAZI
4.4 Operatori pridruˇzivanja
Osnovni operator pridruˇzivanja je =. Naredba pridruˇzivanja je oblika
varijabla = izraz;
Izraz na desnoj strani ´ce biti izraˇcunat i njegova vrijednost se zatim pri-
druˇzuje varijabli na lijevoj strani. Na primjer,
i=2;
a=3.17;
c=’m’;
Pridruˇzivanje varijabla = izraz je ustvari izraz i stoga moˇze biti dio kom-
pleksnijeg izraza. Preciznije, izraz varijabla = izraz ima vrijednost va-
rijable na lijevoj strani nakon ˇsto se izvrˇsi pridruˇzivanje. Stoga je mogu´ce
koristiti pridruˇzivanje unutar drugih naredbi kao u primjeru
while((a=x[i])!=0){
....
++i;
}
gdje se u testu while narebe prvo x[i] pridruˇzi varijabli a, a zatim se testira
da li je dobiveni izraz, a to je vrijednost varijable a, razliˇcit od nule.
Ovakva mogu´cnost moˇze voditi na teˇsko uoˇcljive greˇske kao u sluˇcaju ako
napiˇsemo
if(varijabla = izraz) .....;
umjesto
if(varijabla == izraz) .....;
U prvoj if naredbi prvo ´ce se vrijednost izraza pridruˇziti varijabli, a zatim ´ce
se tijelo if naredbe izvrˇsiti ako je varijabla razliˇcita od nule. U drugoj if na-
redbi vrijednost varijable se ne mijenja ve´c se samo usporeduje s vrijednoˇs´cu
koju daje izraz. Tijelo if naredbe se izvrˇsava ako su te dvije vrijednosti
jednake.
Operatore pridruˇzivanja mogu´ce je ulanˇcati i pisati
varijabla1 = varijabla2 = varijabla3 = ... = izraz;
Niz pridruˇzivanja ide zdesna na lijevo. To znaˇci da se izraz jednom izraˇcuna
i zatim se redom pridruˇzuje varijablama .. varijabla3, varijabla2,
varijabla1. Na primjer,
4.5. UVJETNI OPERATOR : ? 75
x = y = cos(3.22);
je ekvivalentno s
x = (y = cos(3.22));
odnosno
y = cos(3.22);
x=y;
Sloˇzeni operatori pridruˇzivanja su
+= -= *= /= %=
Op´cenito izraz oblika
izraz1 op= izraz2;
gdje je op jedna od operacija +, -, *, /, %, ekvivalentan je s
izraz1 = izraz1 op izraz2;
Ovi operatori spadaju u istu prioritetnu grupu s operatorom = i izraˇcunavaju
se zdesna na lijevo. Prioritet im je manji od prioriteta aritmetiˇckih i logiˇckih
operatora.
Primjeri:
Izraz Ekvivalentan izraz
i +=5 i=i+5
i -=j i=i-j
i *=j+1 i=i*(j+1)
i /=4 i=i/4
i %=2 i=i%2
4.5 Uvjetni operator : ?
Uvjetni izraz je izraz oblika
izraz1 ? izraz2 : izraz3;
U njemu se prvo izraˇcunava izraz1. Ako je on istinit (razliˇcit od nule) onda
se izraˇcunava izraz2 i on postaje vrijednost ˇcitavog uvjetnog izraza; ako je
izraz1 laˇzan (jednak nuli) onda se izraˇcunava izraz3 i on postaje vrijednost
ˇcitavog uvjetnog izraza. Na primjer,
76 POGLAVLJE 4. OPERATORI I IZRAZI
double a,b;
........
(a<b) ? a : b;
je izraz koji daje manju vrijednost od brojeva a i b. Vrijednost uvjetnog
izraza moˇze se pridruˇziti nekoj varijabli, npr.
double a,b,min;
........
min=(a<b) ? a : b;
Uvjetni operator ima nizak prioritet tako da zagrade oko prvog, drugog i
tre´ceg izraza najˇceˇs´ce nisu potrebne.
4.6 Prioriteti i redoslijed izraˇcunavanja
U sljede´coj tabeli sumirani su prioriteti i asocijativnost svih operatora.
Neke od tih operatora joˇs nismo uveli.
Kategorija Operatori asocijativnost
() [] -> . L → D
unarni op. ! ∼ ++ -- - * & (type) sizeof D → L
aritmetiˇcki op. * / % L → D
aritmetiˇcki op. + - L → D
op. pomaka << >> L → D
relacijski op. < <= > >= L → D
op. jednakosti == != L → D
bit-po-bit I & L → D
bit-po-bit ex. ILI ^ L → D
bit-po-bit ILI | L → D
logiˇcko I && L → D
logiˇcko ILI || L → D
uvjetni ? : op. ? : D → L
op. pridruˇzivanja = += -= *= /= %= D → L
zarez op. , L → D
Operatori istog prioriteta i asocijativnosti dani su u istoj liniji. Operatori
u viˇsoj liniji imaju viˇsi prioritet od operatora u niˇzim linijama.
Primijetimo joˇs da C ne propisuje redoslijed kojim se izraˇcunavaju ope-
randi nekog operatora osim u sluˇcaju operatora &&, ||, ? : (i operatora
”,” kojeg joˇs nismo uveli). Stoga u izrazu
4.6. PRIORITETI I REDOSLIJED IZRA
ˇ
CUNAVANJA 77
x=f()+g();
C jezikom nije definirano koja ´ce se funkcija prva izvrˇsiti. Naredbe poput
printf("%d %d\n", ++n, n*n); /* greska */
mogu dati razliˇcite rezultate ovisno o stroju na kojem se izvrˇsavaju.
Poglavlje 5
Ulaz i izlaz podataka
U ovom poglavlju uvodimo ˇsest funkcija iz standardne ulazno-izlazne bi-
blioteke: getchar, putchar, gets, puts scanf i printf. Program koji ko-
risti neku od tih funkcija mora ukljuˇciti datoteku zaglavlja <stdio.h>.
5.1 Funkcije getchar i putchar
int getchar(void);
int putchar(int c);
Funkcija getchar ˇcita jedan znak sa standardnog ulaza (tipiˇcno tip-
kovnice). Funkcija nema argumenata pa je sintaksa poziva:
c_var=getchar();
Funkcija putchar ˇsalje jedan znak na standardni izlaz (tipiˇcno ekran).
Ona uzima jedan argument (znak koji treba ispisati) i vra´ca cjelobrojnu
vrijednost. Najˇceˇs´ce poziv funkcije ima oblik
putchar(c_var);
pri ˇcemu se vra´cena vrijednost ignorira.
Kada funkcija getchar naide na kraj ulaznih podataka vra´ca vrijednost
EOF (skra´ceno od eng. End of File). EOF je simboliˇcka konstanta definirana
u <stdio.h> koja signalizira kraj datoteke i kraj ulaznih podataka (ulaz je
tretiran kao datoteka).
Konstanta EOF mora se razlikovati od znakova iz sustava znakova koje
raˇcunalo koristi. Stoga funkcija getchar ne vra´ca vrijednost tipa char ve´c
vrijednost tipa int ˇsto daje dovoljno prostora za kodiranje konstante EOF.
78
5.1. FUNKCIJE GETCHAR I PUTCHAR 79
Isto tako putchar uzima vrijednost tipa int i vra´ca vrijednost tipa int.
Vra´cena vrijednost je znak koji je ispisan ili EOF ako ispis znaka nije uspio.
Kao ilustraciju upotrebe funkcija getchar i putchar napiˇsimo program
koji kopira znak po znak ulaz na izlaz i pri tome sva slova pretvara u velika:
#include <stdio.h>
#include <ctype.h>
/* kopiranje ulaza na izlaz */
int main(void) {
int c;
c=getchar();
while(c!=EOF) {
putchar(toupper(c));
c=getchar();
}
return 0;
}
Funkcija toupper je deklarirana u datoteci zaglavlja <ctype.h>. Ona uzima
varijablu tipa char i ako se radi o slovu pretvara malo slovo u veliko. Sve
druge znakove ostavlja na miru.
Uoˇcimo da je za pam´cenje znakovne varijable koriˇsten tip int umjesto
tipa char. U while petlji znakovi se ˇcitaju sve dok se ne dode do kraja
ulaznih podataka (EOF). Proˇcitani znak se odmah ispisuje putem funkcije
putchar, a vrijednost se koju putchar vra´ca ignorira. Funkciju putchar
smo mogli pozvati i naredbom i=putchar(c) (i je cjelobrojna varijabla)
koja bi vra´cenu vrijednost smjestila u varijablu i.
Isti je program mogao biti napisan jednostavnije ubacivanjem poziva
funkciji getchar u test while petlje:
#include <stdio.h>
#include <ctype.h>
/* kopiranje ulaza na izlaz */
int main(void) {
int c;
while((c=getchar())!=EOF)
80 POGLAVLJE 5. ULAZ I IZLAZ PODATAKA
putchar(toupper(c));
}
Zagrade u testu (c=getchar())!=EOF su nuˇzne jer bi c=getchar()!=EOF,
zbog niskog prioriteta operatora pridruˇzivanja, bilo interpretirano kao test
c=(getchar()!=EOF). Varijabla c bi tada dobila vrijednost 0 ili 1.
Ponaˇsanje funkcije getchar ovisi o operacijskom sustavu na kojem se pro-
gram izvrˇsava. Operacijski sustav prikuplja podatke s tastature i ˇsalje ih apli-
kacijskom programu najˇceˇs´ce liniju po liniju. Takav naˇcin rada omogu´cava
operacijskom sustavu standardno editiranje ulazne linije (koriˇstenje tipki bac-
kspace i delete) koje stoga ne mora biti ugradeno u svaki program. Poslje-
dica je toga da prvi poziv funkciji getchar blokira program sve dok se ne
ukuca ˇcitava ulazna linija i pritisne tipka RETURN (ENTER). Tada su svi
uˇcitani znakovi na raspolaganju funkciji getchar.
ˇ
Citanje znaka odmah na-
kon ˇsto je otipkan ili onemogu´cavanje ispisa utipkanog znaka moˇze se posti´ci
specifiˇcnim tehnikama programiranja vezanim uz operacijski sustav koji se
koristi (http://www.eskimo.com/ scs/C-faq/top.html).
Kada na ulazu nema podataka funkcija getchar ˇceka da se podaci utip-
kaju. Da bismo zaustavili ˇcitanje moramo utipkati znak za “kraj datoteke”.
Pod unix-om to je znak Ctr-D, a pod DOSom Ctr-Z.
Funkciju getchar ˇcesto koristimo za ˇcitanje jednog znaka (npr. odgovora
y ili n). U tim sluˇcajevima moramo paziti da proˇcitamo sve znakove u liniji,
ukljuˇcivˇsi znak za prijelaz u novi red, kako ne bi ostali za slijede´ci poziv
funkcije getchar. Na primjer, ovdje dajemo funkciju koja ˇcita samo prvo
slovo s ulaza:
int get_first(void) {
int ch;
ch=getchar();
while(getchar() != ’\n’)
; // prazna naredba
return ch;
}
5.2 Funkcije iz datoteke <ctype.h>
Datoteka zaglavlja <ctype.h> sadrˇzi deklaracija niza funkcija koje sluˇze
testiranju znakova. Svaka od tih funkcija uzima jedan argument tipa int koji
treba biti znak ili EOF i vra´ca vrijednost tipa int koja je razliˇcita od nule
(istina) ako je uvjet ispunjen ili nula ako nije. Neke od funkcija su sljede´ce:
5.3. FUNKCIJE GETS I PUTS 81
isalnum() Alfanumeriˇcki znak
isalpha() Alfabetski znak
isctrln() Kontrolni znak
isdigit() Znamenka
isgraph() Printabilni znak osim razmaka
islower() Malo slovo
isprint() Printabilni znak
ispunct() Printabilni znak osim razmaka, slova i brojeva
isspace() Razmak
isupper() Veliko slovo
Pod razmakom smatramo: bjelinu, znak za novi red, znak formfeed, znak
carriage return, tabulator i vertikalni tabulator (’ ’, ’\n’, ’\f’, ’\r’,
’\t’, ’\v’).
Dvije funkcije omogu´cavaju konverziju velikih slova u mala i obratno.
Ostale znakove ostavljaju na miru.
int tolower(int c) Veliko slovo u malo
int toupper(int c) Malo slovo u veliko
5.3 Funkcije gets i puts
char *gets(char *s);
int puts(const char *s);
Funkcije gets i puts sluˇze ˇcitanju i pisanju znakovnih nizova (strin-
gova). Funkcija gets ˇcita znakovni niz sa standardnog ulaza (tastature), a
funkcija puts ispisuje znakovni niz na standardni izlaz (ekran).
Funkcija gets uzima kao argument znakovni niz u koji ´ce biti uˇcitan niz
znakova s ulaza. Pri tome se znakovi s ulaza uˇcitavaju sve dok se ne naide na
kraj linije (’\n’) koji se zamijenjuje znakom ’\0’. Funkcija vra´ca pokazivaˇc
na char koji pokazuje na uˇcitani znakovni niz ili NULL ako se doˇslo do kraja
ulaznih podataka ili se javila greˇska. Simboliˇcka konstanta NULL definirana
je <stdio.h> i njen iznos je 0. To je jedina cjelobrojna vrijednost koja se
moˇze pridruˇziti pokazivaˇcu (vidi sekciju 11.3).
Funkcija puts uzima kao argument znakovni niz koji ´ce biti ispisan na
standardnom izlazu. Funkcija vra´ca broj ispisanih znakova ako je ispis uspio
te EOF ako nije. Prije ispisa puts dodaje znak ’\n’ na kraju znakovnog niza.
Program koji kopira ulaz na izlaz liniju po liniju mogao bi biti napisan
na ovaj naˇcin:
82 POGLAVLJE 5. ULAZ I IZLAZ PODATAKA
#include <stdio.h>
/* kopiranje ulaza na izlaz */
int main(void) {
char red[128];
while(gets(red)!=NULL)
puts(red);
}
Unutar testa while petlje gets ´ce proˇcitati ulaznu liniju i vratiti pokazivaˇc
razliˇcit od NULL ako ulaz nije prazan i ako nije doˇslo do greˇske. U tom sluˇcaju
izvrˇsava se komanda puts(red) koja ispisuje uˇcitanu liniju, a povratna vri-
jednost funkcije puts se zanemaruje.
Osnovni nedostatak funkcije gets je u tome ˇsto nije mogu´ce odrediti
maksimalni broj znakova koji ´ce biti uˇcitan. Ukoliko je broj znakova na
ulazu ve´ci od dimenzije polja koje je argument funkcije gets do´ci ´ce do
greˇske. Stoga je op´cenito bolje umjesto gets koristiti funkciju fgets (vidi
sekciju 13.3).
5.4 Funkcija scanf
int scanf(const char *format, ...);
Funkcija scanf sluˇzi uˇcitavanju podataka sa standardnog ulaza. Op´ca
forma funkcije je
scanf(kontrolni_string, arg_1, arg_2, ... ,arg_n)
gdje je kontrolni string konstantni znakovni niz koji sadrˇzi informacije o
vrijednostima koje se uˇcitavaju u argumente arg 1,. . . ,arg n.
Kontrolni znakovni niz (string) je konstantan znakovni niz koji se sastoji
od individualnih grupa znakova konverzije pri ˇcemu je svakom argumentu
pridruˇzena jedna grupa. Svaka grupa znakova konverzije zapoˇcinje znakom
postotka (%) kojeg slijedi znak konverzije koji upu´cuje na tip podatka koji se
uˇcitava. Npr. %c ili %d itd.
Najˇceˇs´ce koriˇsteni znakovi konverzije navedeni su u sljede´coj tablici:
5.4. FUNKCIJA SCANF 83
znak konverzije tip podatka koji se uˇcitava
%c jedan znak (char)
%d decimalni cijeli broj (int)
%e,%f,%g broj s pokretnim zarezom (float)
%h kratak cijeli broj (short)
%i decimalni, heksadecimalni ili oktalni cijeli broj (int)
%o oktalni cijeli broj (int)
%u cijeli broj bez predznaka (unsigned int)
%x heksadecimalni cijeli broj (int)
%s string (char *)
%p pokazivaˇc (void *)
Unutar kontrolnog niza znakova grupe kontrolnih znakova mogu se nastavljati
jedna na drugu bez razmaka ili mogu biti odvojene bjelinama Bjeline ´ce u
ulaznim podacima biti uˇcitane i ignorirane.
Argumenti funkcije scanf mogu biti samo pokazivaˇci na varijable. Uko-
liko podatak treba uˇcitati u neku varijablu, onda scanf uzima kao argument
adresu te varijable, a ne samu varijablu. To znaˇci da pri pozivu funkcije
scanf ispred imena varijable u koju scanf treba uˇcitati vrijednost moramo
staviti adresni operator &. Tako ´ce naredba
int x;
......
scanf("%d",&x);
uˇcitati cijeli broj s ulaza u varijablu x, dok naredba
int x;
......
scanf("%d",x); /* pogresno */
predstavlja greˇsku.
Podaci koje scanf ˇcita dolaze sa standardnog ulaza ˇsto je tipiˇcno tasta-
tura. Ako se unosi viˇse podataka oni moraju biti separirani bjelinama ˇsto u
sebi ukljuˇcuje i prijelaz u novi red (koji se raˇcuna kao bjelina). Numeriˇcki
podaci na ulazu moraju imati isti oblik kao i numeriˇcke konstante.
5.4.1 Uˇcitavanje cijelih brojeva
Cijeli brojevi mogu biti uneseni kao decimalni (%d) ili kao oktalni i heksa-
decimalni (%i). Znak konverzije (%i) interpretira ulazni podatak kao oktalan
broj ako mu prethodi nula ili kao heksadecimalan broj ako mu prethodi 0x
ili 0X. Na primjer, uzmimo da kˆ od
84 POGLAVLJE 5. ULAZ I IZLAZ PODATAKA
int x,y,z;
......
scanf("%i %i %i",&x,&y,&z);
uˇcitava sljede´ci ulazni podatak:
13 015 0Xd
onda ´ce scanf u sve tri varijable (x, y i z) uˇcitati vrijednost 13 (decimalno).
Cijeli brojevi u oktalnom i heksadecimalnom zapisu mogu se upisivati i
pomo´cu znakova konverzije %o i %x. Ti znakovi konverzije interpretiraju
ulazne podatke kao oktalne odnosno heksadecimalne i stoga ne zahtijevaju
da oktalna konstanta zapoˇcinje nulom, a heksadecimalna s 0x ili 0X. Kˆ od
int x,y,z;
......
scanf("%d %o %x",&x,&y,&z);
ispravno ´ce proˇcitati ulazne podatke
13 15 d
i svim varijablama pridruˇziti vrijednost 13 (decimalno).
Podatak uˇcitavamo u varijablu tipa unsigned sa znakom konverzije %u.
Znakovi konverzije d, i, o, u, x mogu dobiti prefiks h ako je argument
pokazivaˇc na short te prefiks l ako je argument pokazivaˇc na long. Na
primjer, kˆ od
int x;
short y;
long z;
......
scanf("%d %hd %ld",&x,&y,&z);
uˇcitava tri decimalna cijela broja i konvertira ih u varijable tipa int, short i
long. Uoˇcite da h i l ne mogu stajati sami i da u kombinaciji uvijek prethode
znaku konverzije (d, i, o, u, x).
Standard C99 uvodi prefiks ll za uˇcitavanje varijabli tipa long long i unsigned long
long.
5.4. FUNKCIJA SCANF 85
5.4.2 Uˇcitavanje realnih brojeva
Znakovi konverzije e, f, g sluˇze za uˇcitavnje varijable tipa float. Ukoliko
se uˇcitava vrijednost u varijablu tipa double treba koristiti prefiks l (le, lf
ili lg). Na primjer,
float x;
double y;
......
scanf("%f %lg",&x,&y);
Prefiks L koristi se ako je argument pointer na long double.
znak konverzije tip podatka koji se uˇcitava
%e,%f,%g broj tipa float
%le,%lf,%lg broj tipa double
%Le,%Lf,%Lg broj tipa long double
5.4.3 Drugi znakovi u kontrolnom nizu
Funkcija scanf dijeli niz znakova na ulazu u polja znakova odvojena
bjelinama. Svako polje znakova intrepretira se prema odgovaraju´cem znaku
konverzije i upisuje u varijablu na koju pokazuje odgovaraju´ci argument funk-
cije. Svaki znak konverzije uˇcitava jedno ulazno polje. U primjeru
scanf("%f%d",&x,&i);
znak konverzije %f uˇcitava (i konvertira) prvo polje znakova. Pri tome se
eventualne bjeline na poˇcetku preskaˇcu. Prvo polje znakova zavrˇsava bjeli-
nom koju %f ne uˇcitava. Drugi znak konverzije %d preskaˇce sve bjeline koje
odjeljuju prvo polje znakova od drugog polja znakova i uˇcitava (i konvertira)
drugo polje znakova.
Znakovi konverzije mogu biti odijeljeni bjelinama:
scanf("%f %d",&x,&i);
Svaka bjelina u kontrolnom znakovnom nizu ima za posljedicu preskakanje
svih bjelina na ulazu do poˇcetka novog ulaznog polja. Stoga je pisanje zna-
kova konverzije u kontrolnom znakovnom nizu razdvojeno bjelinam (kao u
primjeru "%f %d") ili nerazdvojeno (kao "%f%d") posve ekvivalentno (to ne
vrijedi za znakove konverzije %c i [).
U kontrolnom znakovnom nizu mogu se pojaviti i drugi znakovi osim
bjelina i znakova konverzije. Njima moraju odgovarati posve isti znakovi na
ulazu. Na primjer, ako realan i cijeli broj uˇcitavamo naredbom
86 POGLAVLJE 5. ULAZ I IZLAZ PODATAKA
scanf("%f,%d",&x,&i);
onda ulazni podaci moraju biti oblika npr.
1.456, 8
bez bjeline izmedu prvog broja i zareza. Ako se ˇzeli dozvoliti bjelina prije
zareza potrebno je koristiti naredbu
scanf("%f ,%d",&x,&i);
u kojoj bjelina nakon %f preskaˇce sve eventualne bjeline na ulazu ispred
zareza.
Znakovi u kontrolnom znakovnom nizu mogu npr. biti korisni kod uˇcitavanja
datuma u obliku dd/mm/gg. Tada moˇzemo iskoristiti naredbu
scanf("%d/%d/%d",&dan,&mjesec,&godina);
5.4.4 Uˇcitavanje znakovnih nizova
Znak konverzije %s uˇcitava niz znakova; niz zavrˇsava prvom bjelinom u
ulaznom nizu znakova. Iza posljednjeg uˇcitanog znaka automatski se dodaje
nul-znak (\0). U primjeru
char string[128];
int x;
......
scanf("%s%d",string,&x);
funkcija scanf uˇcitava jedan niz znakova i jedan cijeli broj. Budu´ci da se
svako polje kao argument funkcije interpretira kao pokazivaˇc na prvi elemet
polja, ispred varijable string ne stavlja se adresni operator. To je pravilo
koje vrijedi za polje bilo kojeg tipa.
Znakom konverzije %s nije mogu´ce uˇcitati niz znakova koji sadrˇzi u sebi
bjeline jer bjeline sluˇze za ograniˇcavanje ulaznog polja. Za uˇcitavanje nizova
znakova koji ukljuˇcuju i bjeline moˇzemo koristiti uglate zagrade kao znak
konverzije %[...]. Unutar uglatih zagrada upisuje se niz znakova. Funkcija
scanf ´ce uˇcitati u pripadni argument najve´ci niz znakova sa ulazu koji se
sastoji od znakova navedenih unutar uglatih zagrada. Uˇcitavanje zavrˇsava
prvi znak na ulazu koji nije naveden u uglatim zagradama i na kraj uˇcitanog
niza dodaje se nul-znak (\0). Vode´ce bjelina se ne preskaˇcu.
Na primjer, naredba
5.4. FUNKCIJA SCANF 87
char linija[128];
........
scanf(" %[ ABCDEFGHIJKLMNOPRSTUVWXYZ]", linija);
uˇcitava najve´ci niz znakova sastavljen od velikih slova i razmaka. Argu-
ment linija mora naravno imati dovoljnu dimenziju da primi sve znakove
i zavrˇsni nul-znak \0. Uoˇcimo da smo prije %[ ostavili jedan razmak koji
govori funkciji scanf da preskoˇci sve bjeline koje prethode znakovnom nizu.
To je nuˇzno ukoliko smo imali prethodni poziv scanf funkcije. Naime scanf
uvijek ostavlja zavrˇsni znak prijelaza u novi red u ulaznom nizu, tako da bi
naredba
scanf("%[ ABCDEFGHIJKLMNOPRSTUVWXYZ]", linija);
proˇcitala prethodni znak prijelaza u novi red i budu´ci da on nije u unu-
tar uglatih zagrada zavrˇsila bi ˇcitanje ulaznih podataka i linija ne bi bila
uˇcitana.
Druga mogu´cnost s uglatim zagradama je koristiti sintaksu
scanf(" %[^niz znakova]", linija);
Sada se u odgovaraju´ci argument uˇcitava najve´ci mogu´ci niz znakova sas-
tavljen od svih znakova osim onih koji se nalaze u uglatim zagradama. Na
primjer, uˇcitati cijelu liniju bez znaka za prijelaz u novi red moˇzemo pomo´cu
naredbe
scanf(" %[^\n]", linija);
Na kraj uˇcitanog niza znakova bit ´ce dodan \0, a ispred %[ mora biti ostav-
ljeno prazno mjesto kako bi bile preskoˇcene sve prethodne bjeline.
Znak konverzije c uˇcitava jedan znak u varijablu bez obzira je li on bjelina
ili ne. Stoga, ako je prvi znak konverzije c potrebno je ispred njega staviti
jednu bjelinu kako ne bi proˇcitao znak za prijelaz u novi red koji je ostao
nakon prethodnog poziva funkcije scanf.
Nadalje, kontrolni niz " %c%c%c" ˇcita tri znaka. Poˇcet ´ce s prvim zna-
kom koji nije bjelina (zbog bjeline ispred prvog %c znaka) i proˇcitat ´ce tri
uzastopna znaka bili oni bjeline ili ne. Ako se ˇzeli ˇcitati samo znakove bez
bjelina treba koristiti " %c %c %c" ili %c zamijeniti s %1s.
5.4.5 Prefiks *
Mogu´ce je preskoˇciti neki podatak u listi i ne pridruˇziti ga odgovaraju´coj
varijabli. To se radi tako da se znaku konverzije doda prefiks *. Na primjer
88 POGLAVLJE 5. ULAZ I IZLAZ PODATAKA
scanf(" %s %*d %f", linija, &n, &x);
ne´ce izvrˇsiti priduˇzivanje drugog podatka varijabli n. Umjesto toga on ´ce biti
preskoˇcen a tre´ci podatak ´ce normalno biti pridruˇzen varijabli x.
5.4.6 Maksimalna ˇsirina polja
Uz svaki kontrolni znak mogu´ce je zadati maksimalnu ˇsirinu ulaznog polja
koje ´ce se uˇcitati tako da se ispred kontrolnog znaka stavi broj koji odreduje
ˇsirinu polja. Tako na primjer %3d uˇcitava cijeli broj od najviˇse tri znamenke,
a %11c uˇcitava 11 znakova. Ukoliko podatak sadrˇzi manje znakova od zadane
maksimalne ˇsirine polja on se uˇcita samo do prve bjeline. Ako pak podatak
ima viˇse znamenaka od maksimalne ˇsirine polja, viˇsak znamenaka bit ´ce
uˇcitan sljede´cim konverzijskim znakom ili sljede´com scanf funkcijom. Na
primjer, uzmimo naredbu
scanf(" %3d %3d %3d", &i, &j, &k);
Ukoliko na ulazu imamo
1 2 3
bit ´ce uˇcitano i=1, j=2, k=3. Ako na ulazu imamo
123 456 789
bit ´ce uˇcitano i=123, j=456, k=789. Uzmimo sada ulaz oblika
123456789
ponovo ´cemo dobiti i=123, j=456, k=789 budu´ci da sljede´ce ulazno polje
zapoˇcinje tamo gdje prethodno zavrˇsava. Konaˇcno pogledajmo ulaz oblika
1234 56 789
dobit ´cemo i=123, j=4 i k=56.
ˇ
Sirina prvog ulaznog polja je tri znaka
(i=123); drugo ulazno polje poˇcine sa znakom 4 i zavrˇsava prvom bjelinom
(j=4). Tre´ce ulazno polje posve je separirano bjelinama pa dobivamo k=56.
Preostali znakovi ostat ´ce na ulazu i mogu biti proˇcitani novim pozivom
scanf funkcije (ili neke druge ulazne funkcije).
5.4. FUNKCIJA SCANF 89
5.4.7 Povratna vrijednost
Funkcija scanf vra´ca broj uspjeˇsno uˇcitanih podataka ili EOF. Tu ˇcinjenicu
moˇzemo iskoristiti za provjeru jesu li svi traˇzeni podaci uˇcitani. Uzmimo je-
dan primjer u kojem uˇcitavamo i procesiramo samo pozitivne cijele brojeve.
Kˆ od bi mogao izgledati ovako:
int n;
scanf("%d",&n);
while(n >= 0)
{
// radi nesto s brojem
scanf("%d",&n); // ucitaj novi broj
}
Ovakav kˆ od ne moˇze uspjeˇsno tretirati sluˇcajeve u kojima korisnik uˇcini
greˇsku priklikom upisa (npr. upiˇse slovo).
Ponaˇsanje programa moˇzemo popraviti ako ispitujemo da li je funkcija
scanf uspjeˇsno uˇcitala broj. To moˇzemo uˇciniti testom scanf("%d",&n) ==
1. Nova verzija programa bi glasila
int n;
while(scanf("%d",&n) == 1 && n >= 0)
{
// radi nesto s brojem
}
Uoˇcimo da prvo ispitujemo je li broj dobro uˇcitan. Ako nije, n >= 0 ne´ce
biti ispitivano.
No program bi trebao kod neuspjeˇsnog upisa zatraˇziti od korisnika da
ponovi upis. Da bismo to realizirali uoˇcimo da funkcija scanf nakon neus-
pjeˇsnog pokuˇsaja ˇcitanja nekog podatka prekida ˇcitanje i ostavlja taj podatak
i sve iza njega u ulaznom spremniku. Stoga je potrebno prije novog po-
ziva funkciji scanf isprazniti spremnik. To moˇzemo uˇciniti pomo´cu funkcije
getchar. Mi ´cemo, ˇstoviˇse, iskoristiti sadrˇzaj spremnika da obavijestimo ko-
risnika o greˇsci koju je napravio, Budu´ci da kˆ od postaje kompleksniji moˇzemo
postupak ˇcitanja broja zatvoriti u jednu funkciju koju ´cemo nazvati get int.
int get_int(void)
{
int input;
char ch;
while(scanf("%d",&input) != 1)
90 POGLAVLJE 5. ULAZ I IZLAZ PODATAKA
{
while((ch = getchar())!=’\n’)
putchar(ch);
printf(" nije cijeli broj.\nMolim unesite ");
printf("cijeli broj: ");
}
return input;
}
Kada korisnik ne unese cijeli broj, neproˇcitani ulazni podatak ´ce biti proˇcitan
pomo´cu druge while petlje. Sadrˇzaj ´ce biti ispisan funkcijom putchar i
korisnik ´ce biti zamoljen da ponovi upis.
5.5 Funkcija printf
int printf(const char *format, ...);
Funkcija printf sluˇzi za ispis podataka na standardnom izlazu. Op´ca
forma funkcije je
printf(kontrolni_string, arg_1, arg_2, ... ,arg_n)
gdje je kontrolni string konstantan znakovni niz koji sadrˇzi informaciju o
formatiranju ispisa argumenata arg 1,. . . ,arg n.
Kontrolni znakovni niz ima posve istu formu i funkciju kao kod funkcije
scanf. Pojedine grupe znakova unutar kontrolnog znakovnog niza mogu se
nastavljati jedna na drugu ili biti medusobno razmaknute bjelinama ili nekim
drugim znakovima. Svi znakovi osim kontrolnih bit ´ce ispisani onako kako
su uneseni.
Najˇceˇs´ce koriˇsteni znakovi konverzije dani su u sljede´coj tabeli:
znak konverzije tip podatka koji se ispisuje
%d,%i decimalni cijeli broj (int)
%u cijeli broj bez predznaka (unsigned int)
%o oktalni cijeli broj bez predznaka (unsigned int)
%x heksadecimalni cijeli broj bez predznaka (unsigned int)
%e,%f,%g broj s pokretnim zarezom (double)
%c jedan znak (char)
%s string (char *)
%p pokazivaˇc (void *)
5.5. FUNKCIJA PRINTF 91
Funkcija vra´ca broj ispisanih znakova ili EOF ako je doˇslo do greˇske.
Argumenti funkcije printf mogu biti konstante, varijable, izrazi ili polja.
Na primjer, naredba
double x=2.0;
......
printf("x=%d, y=%f\n",x,sqrt(x));
´ce ispisati
x=2.000000, y=1.414214
Svi znakovi koji nisu znakovi konverzije ispisani su onako kako su uneseni
u kontrolnom znakovnom nizu "x=%d, y=%f\n". Ako treba ispisati znak %,
onda unutar kontrolnog znakovnog niza na tom mjestu treba staviti %%.
Funkcija printf ima varijabilan broj argumenata i stoga se pri pozivu
funkcije vrˇsi promocija argumenata: argumenti tipa char i short konverti-
raju se u tip int, a argumenti tipa float u double. Ove konverzije deˇsavaju
se prije predaje argumenata funkciji pa stoga pomo´cu znaka konverzije %f
moˇzemo ispisati i varijablu tipa float i tipa double, jer ´ce u oba sluˇcaja
printf vidjeti samo varijablu tipa double. Sliˇcno pomo´cu %d moˇzemo ispi-
sati i varijablu tipa char i tipa short. Na primjer,
char c=’w’;
......
printf("c(int)=%d, c(char)=%c\n",c,c);
ispisat ´ce
c(int)=119, c(char)=w
ukoliko raˇcunalo koristi ASCII skup znakova (119 je ASCII kod znaka “w“).
5.5.1 Ispis cijelih brojeva
Pomo´cu znakova konverzije %o i %x cijeli brojevi se ispisuju u oktalnom i
heksadecimalnom obliku bez vode´ce nule odn. 0X. Na primjer,
short i=64;
......
printf("i(okt)=%o: i(hex)=%x: i(dec)=%d\n",i,i,i);
ispisuje
i(okt)=100: i(hex)=40: i(dec)=64
92 POGLAVLJE 5. ULAZ I IZLAZ PODATAKA
Izrazi tipa long ispisuju se pomo´cu prefiksa l. Na primjer, program
#include <stdio.h>
#include <limits.h>
long i=LONG_MAX;
int main(void){
printf("i(okt)=%lo: i(hex)=%lx: i(dec)=%ld\n",i,i,i);
}
(ovisno o raˇcunalu na kojem se izvrˇsava) moˇze ispisati
i(okt)=17777777777: i(hex)=7fffffff: i(dec)=2147483647
Simboliˇcka konstanta LONG MAX definirana je u datoteci zaglavlje <limits.h>
i predstavlja najve´ci broj tipa long.
5.5.2 Ispis realnih brojeva
Brojeve tipa float i double moˇzemo ispisivati pomo´cu znakova konver-
zije %f, %g i %e. U konverziji tipa f broj se ispisuje bez eksponenta, a u
konverziji tipa e s eksponentom. U konverziji tipa g naˇcin ispisa (s ekspo-
nentom ili bez) ovisi o vrijednosti koja se ispisuje. Naredba,
double x=12345.678;
.........
printf("x(f)=%f: x(e)=%e: x(g)=%g\n",x,x,x);
´ce ispisati
x(f)=12345.678000: x(e)=1.234568e+004: x(g)=12345.7
Znakovi konverzije e, f, g dobivaju prefiks L ako se ispisuje varijabla tipa
long double.
znak konverzije tip podatka koji se ispisuje
%e,%f,%g broj tipa float
%e,%f,%g broj tipa double
%Le,%Lf,%Lg broj tipa long double
5.5. FUNKCIJA PRINTF 93
5.5.3
ˇ
Sirina i preciznost
Uz svaki kontrolni znak mogu´ce je zadati minimalnu ˇsirinu ispisa tako da
se ispred kontrolnog znaka stavi broj koji odreduje ˇsirinu ispisa. Tako na
primjer %3d ispisuje cijeli broj sa najmanje tri znamenke. Ukoliko podatak
sadrˇzi manje znakova od zadane minimalne ˇsirine polja, do pune ˇsirine bit
´ce dopunjen vode´cim bjelinama. Podatak koji ima viˇse znamenaka od mini-
malne ˇsirine ispisa bit ´ce ispisan sa svim potrebnim znamenkama. Tako, na
primjer, sljede´ca naredba
double x=1.2;
.........
printf("%1g\n%3g\n%5g\n",x,x,x);
ispisuje
1.2
1.2
1.2
Prva dva ispisa koriste 3 znaka dok tre´ci koristi pet znakova tj. dvije vode´ce
bjeline.
Pored minimalne ˇsirine ispisa kod realnih brojeva mogu´ce je odrediti i
preciznost ispisa tj. broj decimala koje ´ce biti ispisane. Sintaksa je sljede´ca:
%a.bf ili %a.bg ili %a.be gdje je a minimalna ˇsirina ispisa, a b preciznost. Na
primjer %7.3e znaˇci ispis u e formatu s najmanje 7 znamenaka, pri ˇcemu ´ce
biti dano najviˇse 3 znamenke iza decimalne toˇcke. Sljede´ci program ispisuje
broj π:
#include <stdio.h>
#include <math.h>
int main(void){
double pi=4.0*atan(1.0);
printf("%5f\n %5.5f\n %5.10f\n",pi,pi,pi);
}
Rezultat ispisa ´ce biti
3.141593
3.14159
3.1415926536
Ispis bez specificirane preciznosti daje ˇset decimala, pa je prvi ispis zaokruˇzen
na ˇsest decimala. Drugi i tre´ci ispis su zaokruˇzeni na pet i deset decimala
budu´ci da je preciznost dana eksplicitno u printf funkciji.
94 POGLAVLJE 5. ULAZ I IZLAZ PODATAKA
ˇ
Sirinu i preciznost ispisa mogu´ce je odrediti dinamiˇcki tako da se na
mjesto ˇsirine ili preciznosti umjesto broja stavi *. Cjelobrojna varijabla (ili
izraz) na odgovaraju´cem mjestu u listi argumenata odreduje ˇsirinu odn. pre-
ciznost. U primjeru
#include <stdio.h>
#include <math.h>
int main(void){
double pi=4.0*atan(1.0);
int i=10;
printf("%*f\n %*.*f\n %5.*f\n",11,pi,16,14,pi,i,pi);
}
dobivamo ispis
3.141593
3.14159265358979
3.1415926536
Prva varijabla ispisana je s 11 znamenaka i 6 decimala (budu´ci da preciznost
nije specificirana); druga je ispisana s 16 znamenaka i 14 decimala, a tre´ca
10 decimala i 12 znamenaka.
5.5.4 Ispis znakovnih nizova
Konverzija tipa %s primjenjuje se na znakovne nizove (nizove znakova ˇciji
je kraj signaliziran nul-znakom \0). Stoga se mogu ispisati i nizovi znakova
koji sadrˇze bjeline. Na primjer,
char naslov[]="Programski jezik C";
....
printf("%s\n",naslov);
ispisat ´ce
Programski jezik C
i prije´ci u novi red.
Preciznost se moˇze koristiti i kod %s konverzije. Tada znaˇci maksimalni
broj znakova koji ´ce biti prikazan. Na primjer %5.12s specificira da ´ce biti
prikazano minimalno 5 znakova (dopunjenih bjelinama kao treba), a maksi-
malno 12 znakova. Ako je niz znakova duˇzi od 12, viˇsak znakova ne´ce biti
prikazan. Na primjer,
5.5. FUNKCIJA PRINTF 95
char naslov[]="Programski jezik C";
....
printf("%.16s\n",naslov);
ispisat ´ce
Programski jezik
5.5.5 Zastavice
Svaka grupa znakova za konverziju moˇze sadrˇzavati i zastavicu. Zastavica
je znak koji dolazi odmah nakon znaka %; mogu´ce zastavice su: -, +, 0, ’ ’
(bjelina) i #, a znaˇcenja su sljede´ca:
- podatak ´ce biti lijevo pozicioniran ako je manji od minimalne ˇsirine
polja;
+ Znak + ´ce biti napisan ispred pozitivnog broja;
0 Vode´ce bjeline (ako su nuˇzne) bit ´ce zamijenjene nulama. Odnosi se
samo na numeriˇcke podatke koji su desno pozicionirani i koji su uˇzi od
minimalne ˇsirine ispisa;
’ ’ (bjelina) jedna bjelina ´ce prethoditi svakom pozitivnom broju.
# (uz o ili x konverziju) osigurava da ´ce oktalni i heksadecimalni brojevi
biti ispisani s vode´com 0 ili 0x;
# (uz e, f ili g konverziju) osigurava da ´ce decimalna toˇcka biti ispisana
i da ´ce nule na krajnjoj desnoj poziciji broja biti ispisane.
Tako ´ce, na primjer, naredba
int i=66;
....
printf(":%6d\n:%-6d\n:%06d\n%#x\n",i,i,i,i);
ispisati
: 66
:66
:000066
:0x42
Poglavlje 6
Kontrola toka programa
6.1 Izrazi i naredbe
6.1.1 Izrazi
Izrazom nazivamo svaku kombinaciju operatora i operanada koju jezik
dozvoljava. Operatori mogu biti konstante i varijable ili njihove kombinacije.
Primjeri izraza su
7.2
2+13-4
x=3*2
a*(b+c/d)%2
n++
x= ++n % 2
q>2
itd. Sloˇzeni izrazi mogu biti kombinacija viˇse manjih izraza koje nazivamo
podizraima. Na primjer, c/d je podizraz u ˇcetvrtom primjeru.
Svakom izrazu u programskom jeziku C pridruˇzena je vrijednost koja se
dobiva izvrˇsavanjem svih operacija prisutnih u izrazu, u skladu s njihovim
prioritetima.
Ovdje su primjeri nekih izraza i njihovih vrijednosti:
Izraz vrijednost
-3+5 2
x=2+17 19
5>3 1
y=7+(x=2+17) 26
96
6.1. IZRAZI I NAREDBE 97
Ovdje treba uoˇciti da svaki logiˇcki izraz (npr. 5>3) dobiva vrijednost 0
ukoliko nije istinit ili 1 ako je istinit. Drugo, izrazi pridruˇzivanja primaju
vrijednost koja se pridruˇzuje lijevoj strani. Tako u primjeru x=2+17 vari-
jabla x prima vrijednost 17, ˇsto je ujedno i vrijednost ˇcitavog izraza. To
nam omogu´cava formiranje izraza poput y=7+(x=2+17): u njemu se prvo
izraˇcunava podizraz x=2+17 koji u varijablu x sprema vrijednost 17, a zatim
se izraˇcunava y=7+17, jer je vrijednost ˇcitavog podizraza na desnoj strani
17. To nam svojstvo izraza omogu´cava i pisanje viˇsestrukog pridruˇzivanja u
jednoj liniji kao npr. u
x=y=z=0.0
6.1.2 Naredbe
Program se sastoji od niza naredbi. Svaka naredba je potpuna instrukcija
raˇcunalu. U C-u kraj naredbe oznaˇcavamo znakom toˇcka-zarez. Tako je
x=3.0
jedan izraz, dok je
x=3.0;
naredba.
C poznaje viˇse vrsti naredbi. Svaki izraz kojeg slijedi toˇcka-zarez pred-
stavlja narednu. Tako su i
8;
4+4;
legalne naredbe premda ne ˇcine niˇsta. Sljede´ci program ilustrira razliˇcite
tipove naredbi:
#include <stdio.h>
int main(void) /* suma prvih 20 prirodnih brojeva */
{
int brojac,suma; /* deklaracijska naredba */
brojac=1; /* naredba pridruzivanja */
suma =0; /* isto */
while(brojac++ < 21) /* while */
suma += brojac; /* naredba */
printf("suma = %d\n",suma); /* funkcijska naredba */
return 0;
}
98 POGLAVLJE 6. KONTROLA TOKA PROGRAMA
Deklaracijskim naredbama deklariramo varijable. Naredbe pridruˇzivanja su
izrazi pridruˇzivanja zavrˇseni toˇcka-zarezom. Funkcijska naredba predstav-
lja poziv funkcije. Njenim izvrˇsavanjem izvrˇsavaju se sve naredbe iz tijela
funkcije. U naˇsem sluˇcaju poziva se funkcija printf za ispis sume.
Naredba while je primjer strukturirane naredbe. Ona se sastoji od tri di-
jela: kljuˇcne rijeˇci while, testa brojac++ < 21 i naredbe suma += brojac;.
Ostale strukturirane naredbe su petlje for, do-while, if-nareba itd.
6.1.3 Sloˇzene naredbe
Vitiˇcaste zagrade sluˇze grupiranju deklaracija i naredbi u jednu cjelinu.
Takva se cjelina naziva blok naredbi ili sloˇzena naredba. U strukturiranim
naredbama jedna naredba se moˇze uvijek zamijeniti blokom naredbi. Uspo-
redimo dvije while petlje.
/* fragment 1 */
while(brojac++ < 21)
suma += brojac;
printf("suma = %d\n",suma);
/* fragment 2 */
while(brojac++ < 21)
{
suma += brojac;
printf("suma = %d\n",suma);
}
U prvom dijelu primjera while naredba se proteˇze od kljuˇcne rijeˇci while
do toˇcke-zarez. Funkcija printf ostaje izvan petlje.
U drugom dijeli vitiˇcaste zagrade osiguravaju da su obje naredbe dio
while naredbe te se printf izvrˇsava u svako prolazu kroz petlju. Sa sta-
noviˇsta while petlje sloˇzena naredba tretira se isto kao i jednostavna naredba.
Svaka blok naredba moˇze sadrˇzavati deklaracije varijabli i izvrˇsne na-
redbe. Prema standardu C90 sve deklaracije varijabli moraju prethoditi pr-
voj izvrˇsnoj naredbi. Standard C99 dozvoljava slobodno preplitanje dekla-
racija i izvrˇsnih naredbi.
6.1.4 Popratne pojave i sekvencijske toˇcke
Ovdje ˇzelimo uvesti neˇsto C terminologije. Popratna pojava (eng. side ef-
fect) je svaka promjena nekog objekta koja se desi kao posljedica izraˇcunavanja
6.1. IZRAZI I NAREDBE 99
nekog izraza. Na primjer, popratna pojava naredbe
x=50.0;
je postavljnje varijable x na vrijednost 50.0. Takva se terminologija moˇze
ˇciniti ˇcudnom jer je oˇcita intencija ove naredbe postaviti varijablu x na 50.0,
no sa stanoviˇsta jezika osnovna intencija je izraˇcunati izraz. Kao posljedica
izraˇcunavanja izraza varijabla x je postavljena na 50.0.
Drugi primjer popratne pojave nalazimo u while naredbi iz prethodnog
programa
while(brojac++ < 21)
gdje kao popratnu pojavu izraˇcunavanja izraza brojac++ < 21 dobivamo
pove´canje varijable brojac za 1. Ovdje je termin popratna pojava posve
primjeren.
Sada se name´ce pitanje u kojem se trenutku izvrˇsavaju popratne po-
jave. Mjesto u programu na kojem se popratne pojave moraju izvrˇsiti prije
nastavka izvrˇsavanja programa naziva se sekvencijska toˇcka (eng. sequence
point).
Toˇcka-zarez oznaˇcava sekvencijsku toˇcku. Prije prijelaza na novu naredbu
sve popratne pojave prethodne naredbe moraju biti izvrˇsene.
Isto tako, na kraju svakog potpunog izraza nalazi se sekvencijska toˇcka.
Pri tome je potpuni izraz svaki izraz koji nije podizraz nekog ve´ceg izraza. To
je znaˇcajno u strukturnim naredbama koje testiraju neki izraz. Na primjer,
u petlji
while(brojac++ < 21)
printf("%d \n",brojac);
test petlje ˇcini potpuni izraz i stoga pove´canje varijable brojac mora biti
izvrˇseno odmah nakon izraˇcunavanja testa, a ne nakon izvrˇsavanja ˇcitave
while naredbe, kao ˇsto bi netko mogao pomisliti.
U primjeru
y=(4+x++)+(6-x++);
4+x++ nije potpun izraz i stoga C ne garantira da ´ce x biti pove´can odmah
nako izraˇcunavanja podizraza 4+x++.
ˇ
Citav izraz pridruˇzivanja je potpuni
izraz, a sekvencijska toˇcka je toˇcka-zarez te stoga C garantira da ´ce x biti
dva puta pove´can za 1 prije prijelaza na izvrˇsavanje nove naredbe. Jezik ne
specificira ho´ce li x biti pove´can nakon izraˇcunavanja svakog podizraza ili tek
nakon izraˇcunavanja ˇcitavog izraza te stoga ovakve izraze treba izbjegavati.
100 POGLAVLJE 6. KONTROLA TOKA PROGRAMA
6.2 Naredbe uvjetnog grananja
Naredbe uvjetnog grananja if, if-else i switch omogu´cavaju izvrˇsavanje
pojedinih grupa naredbi u ovisnosti o tome da li su ispunjeni neki uvjeti. Na-
redba goto pruˇza mogu´cnost prekida sekvencijalnog izvrˇsavanja programa i
skoka na neku drugu lokaciju unutar programa.
6.2.1 if naredba
Naredba if ima oblik
if(uvjet) naredba;
gdje je uvjet aritmetiˇcki (ili pokazivaˇcki) izraz. Naredba prvo izraˇcunava
izraz uvjet i ako je njegova vrijednost razliˇcita od nule (ˇsto se interpretira
kao istina, odn. ispunjenost uvjeta) izvrˇsava se naredba. Ako izraz uvjet
daje nulu (ˇsto se interpretira kao laˇz, odn. neispunjenost uvjeta), onda se
naredba ne izvrˇsava i program se nastavlja prvom naredbom iza if naredbe.
Na primjer, u dijelu programa
int x;
....
if(x>0) printf("\n x= %d \n",x);
x++;
izraz x>0 imat ´ce vrijednost 1 ako je vrijednost varijable x strogo pozitivna
i varijabla x ´ce biti ispisana naredbom printf("\n x= %d \n",x) te zatim,
u sljede´coj naredbi, pove´cana za 1; ako x nije strogo pozitivno, vrijednost
izraza x>0 bit ´ce 0 i program ´ce inkrementirati x bez poziva funkcije printf.
Umjesto jedne naredbe if moˇze sadrˇzavati blok naredbi omeden vitiˇcastim
zagradama (sloˇzena naredba).
if(uvjet) {
naredba;
naredba;
......
naredba;
}
U ovom se sluˇcaju ˇcitav blok naredbi izvrˇsava ili ne izvrˇsava ovisno o tome
da li uvjet ima vrijednost istine ili laˇzi.
Uvjet u if naredbi je najˇceˇs´ce izraz sastavljen od relacijskih i logiˇckih
operatora. Na primjer, pretvaranje malog u veliko slovo moglo bi biti izve-
deno na sljede´ci naˇcin:
6.2. NAREDBE UVJETNOG GRANANJA 101
char c;
......
if(c>=’a’ && c<=’z’)
c=’A’+c-’a’;
Ovdje koristimo logiˇcko I kako bismo povezali uvjete c>=’a’ i c<=’z’. Oba
uvjeta zadovoljavaju samo mala slova (engleske) abecede koja u naredbi
c=’A’+c-’a’; pretvaramo u odgovaraju´ca velika slova.
Relacijski operatori ne mogu se primijeniti na znakovne nizove (stringove)
pa stoga moramo koristiti strcmp funkciju kao u sljede´cem primjeru:
#include <string.h>
char *string1, *string2;
......
if(strcmp(string1,string2)==0)
printf("Stringovi su jednaki.\n");
Funkcija strcmp ´ce vratiti nulu ako su stringovi string1 i string2 jednaki.
Istu bismo if naredbu mogli napisati i u obliku
if(!strcmp(string1,string2))
printf("Stringovi su jednaki.\n");
Uoˇcimo op´cenito da uvjet if(x!=0) moˇzemo pisati kao if(x), te isto
tako if(x==0) moˇzemo zamijeniti s if(!x).
Kod logiˇckih izraza koji se pojavljuju u uvjetu if naredbe ponekad treba
koristiti pravilo da se izraz izraˇcunava sve dok njegova vrijednost ne postane
poznata. Na primjer, u dijelu kˆ oda
int x;
.....
if(x != 0 && (20/x) < 5)
......;
izraˇcunavanje izraza x != 0 && (20/x) < 5 u sluˇcaju x=0 prestat ´ce nakon
x != 0, jer je vrijednost cijelog izraza u tom trenutku poznata (=laˇz). Stoga
podizraz (20/x) < 5 ne´ce biti izraˇcunat i do dijeljenja s nulom ne´ce do´ci.
6.2.2 if-else naredba
if-else naredba ima oblik
102 POGLAVLJE 6. KONTROLA TOKA PROGRAMA
if(uvjet)
naredba1;
else
naredba2;
Ako izraz uvjet ima vrijednost istine, onda se izvrˇsava naredba1 a u suprot-
nom naredba2; obje naredbe mogu biti blokovi naredbi. Na primjer,
#include <stdlib.h>
......
if(!x){
printf("Djelitelj jednak nuli!\n");
exit(-1);
}else
y/=x;
Funkcija
void exit(int status)
deklarirana je u datoteci zaglavlja <stdlib.h> i ona zaustavlja izvrˇsavanje
programa. Vrijednost status predaje se operacijskom sustavu. Vrijednost
nula znaˇci da je program uspjeˇsno izvrˇsen, a vrijednost razliˇcita od nule
signalizira da je program zaustavljen zbog greˇske.
Uoˇcimo da je sljede´ca naredba s uvjetnim operatorom
max = a > b ? a : b;
ekvivalenta s
if(a>b)
max=a;
else
max=b;
6.2.3 Primjer: Djelitelji broja
Postavimo si sada sljede´ci zadatak: za zadani prirodan broja treba ispisati
sve njegove djelitelje.
Program ´cemo konstruirati kao petlju u kojoj se broj unosi, analizira i
ispisuje rezultat. Na taj naˇcin nije potrebno pokretati program za testiranje
svakog pojedinog broja.
Koristit ´cemo sljede´ci model:
6.2. NAREDBE UVJETNOG GRANANJA 103
printf("Unos:")
while(scanf vraca vrijednost 1){
analizirati broj i ispisati rezultat
printf("Unos:")
}
Funkcijom scanf uˇcitavamo brojeve. Kada je broj ispravno uˇcitan scanf
vra´ca broj uˇcitanih vrijednosti (1 u naˇsem sluˇcaju). Ako pri sljede´cem pozivu
funkciji scanf upiˇsemo npr. q ili neko drugo slovo, vrijednost ne´ce biti uˇcitana
i funkcija ´ce vratiti nulu ˇsto ´ce zaustaviti petlju.
Najjednostavniji naˇcin za pronalaˇzenje svih djelitelja zadanog broja num
je sljede´ci:
for(div=2; div < num; ++div)
if(num % div == 0)
printf("%d je djeljiv s %d\n", num, div);
Ovu petlju moˇzemo uˇciniti puno efikanijom ako primjetimo da svaki puta
dobivamo dva djelitelja. Na primjer, 128%2 = 0 ˇsto daje da je 128 djeljivo s
2, ali i sa 128/2 = 64. Dijele´ci redom dobivamo parove: 2, 64; 4, 32; 8,16.
Dalje od broja ˇciji je kvadrat ve´ci ili jednak od 128 nije potrebno i´ci. Ta nam
primjedba znaˇcajno reducira koliˇcinu raˇcunaja jer sada moˇzemo pisati:
for(div=2; (div*div) <= num; ++div)
if(num % div == 0)
printf("%d je djeljiv s %d i %d\n", num, div, num/div);
Uoˇcimo da je num/div cjelobrojno dijeljenje koje nam daje drugi djelitelj.
U ovom pristupu imamo joˇs dva problema. Prvi se javlja u sluˇcaju da je
broj kvadrat nekog broja, kao npr. 144 = 12 * 12. Tada bismo ispisivali
144 je djeljiv s 12 i 12
Da bismo to izbjegli uvodimo joˇs jednu if naredbu:
1
for(div=2; (div*div) <= num; ++div)
if(num % div == 0)
{
if(div * div != num)
printf("%d je djeljiv s %d i %d\n", num, div, num/div);
else
printf("%d je djeljiv s %d.\n", num, div);
}
1
Budu´ci da if-else ˇcini jednu naredbu, zagrade iza prve if-naredbe nisu nuˇzne.
104 POGLAVLJE 6. KONTROLA TOKA PROGRAMA
Drugi problem je kako prepoznati prim broj. Jasno je da ako num nije djeljiv
niti s jednim brojem onda nikad ne´cemo u´ci u tijelo if naredbe. Stoga
moˇzemo uvesti novu varijablu flag koju ´cemo inicijalizirati nulom i postaviti
na 1 u tijelu if naredbe. Ako na kraju programa imamo flag == 0 znaˇci
da se radi o prim broju.
Cijeli program sada izgleda ovako:
#include <stdio.h>
int main(void)
{
long num; // broj koji provjeravamo
long div; // potencijalni djelitelj
unsigned flag = 0; // prim broj?
printf("Unesite cijeli broj ili q za izlaz: ");
while(scanf("%ld",&num) == 1)
{
for(div=2; (div*div) <= num; ++div)
{
if(num % div == 0)
{
if(div * div != num)
printf("%d je djeljiv s %d i %d\n", num, div,
num/div);
else
printf("%d je djeljiv s %d.\n", num, div);
flag=1;
}
}
if(flag == 0) printf("%ld je prom broj.\n",num);
printf("Unesite cijeli broj ili q za izlaz: ");
}
return 0;
}
6.2.4 Viˇsestruka if-else naredba
Viˇse if-else naredbi mogu se nadovezati jedna na drugu. Tako pomo´cu
dvije if - else naredbe dobivamo sloˇzenu naredbu
if(uvjet1)
6.2. NAREDBE UVJETNOG GRANANJA 105
naredba1;
else if(uvjet2)
naredba2;
else
naredba3;
naredba4;
u kojoj se prvo ispituje uvjet1 te ako je istinit izvrˇsava se naredba1, a zatim
se prelazi na naredbu nareba4. Ako uvjet1 nije istinit izraˇcunava se uvjet2
te se izvrˇsava ili naredba2 ili naredba3 ovisno o istinitosti izraza uvjet2.
Program se zatim nastavlja naredbom naredba4. Na primjer, imamo funk-
ciju
#include <math.h>
double f(double x)
{
double y;
if(x<2.0)
y=3.0+(x-2.0);
else if(x<5.0)
y=3.0+(x-2.0)*exp(x);
else
y=3.0*(1.0+exp(5.0));
return y;
}
Uoˇcimo da je viˇsestruka if-else naredba u tijelu funkcije samo drukˇciji
naˇcin pisanja dviju ugnijeˇzdenih

if-else naredbi:
if(x<2.0)
y=3.0+(x-2.0);
else
if(x<5.0)
y=3.0+(x-2.0)*exp(x);
else
y=3.0*(1.0+exp(5.0));
Budu´ci da je if-else jedna naredba nisu nam bile potrebne zagrade poslije
else dijela prve if-else naredbe. Za drugu if-else naredbu kaˇzemo da je
ugnijeˇzdena u prvu.
106 POGLAVLJE 6. KONTROLA TOKA PROGRAMA
6.2.5 Primjer. Viˇsestruki izbor
Ugnijezditi se moˇze bilo koji broj if-else naredbi, a else dio zadnje
if-else naredbe moˇze se ispustiti. U sljede´cem primjeru uˇcitavaju se dva
broja i jedan znak koji predstavlja izbor raˇcunske operacije. U ovisnosti o
uˇcitanom znaku izvrˇsava se jedna od ˇcetiri raˇcunske operacije.
#include <stdio.h>
int main(void)
{
float a,b;
char operacija;
printf("Upisati prvi broj: ");
scanf(" %f",&a);
printf("Upisati drugi broj: ");
scanf(" %f",&b);
printf("Upisati operaciju: zbrajanje(z), oduzimanje(o),\n");
printf(" mnozenje(m),dijeljenje(d) :");
scanf(" %c",&operacija);
if(operacija==’z’)
printf("%f\n",a+b);
else if(operacija==’o’)
printf("%f\n",a-b);
else if(operacija==’m’)
printf("%f\n",a*b);
else if(operacija==’d’)
printf("%f\n",a/b);
else
printf("Nedopustena operacija!\n");
return 0;
}
6.2.6 Sparivanje if i else dijela
Budu´ci da se else dio if-else naredbe moˇze ispustiti mogu´ce su am-
bivalentne situacije ako je ugnjeˇzdeno viˇse if-else naredbi od kojih neke
nemaju else dio. Stoga vrijedi pravilo:
6.3. SWITCH NAREDBA 107
Svaka else naredba pripada sebi najbliˇzoj if naredbi.
Pogledajmo sljede´ci primjer:
if(n>0)
if(a>b)
z=a;
else
z=b;
Ovdje je else naredba pridruˇzena unutarnjoj if naredbi (if(a>b)) jer joj je
najbliˇza. Ako ˇzelimo da else naredba bude pridruˇzena vanjskoj if naredbi
moramo koristiti zagrade:
if(n>0){
if(a>b)
z=a;
}
else
z=b;
Sljede´ci naˇcin pisanja sugerira da je else naredba pridriˇzena vanjskoj if
naredbi (if(n>0))
if(n>0)
if(a>b) z=a;
else
z=b;
dok je ona ustvari pridruˇzena unutarnjoj if naredbi (if(a>b)). Takav stil
pisanja treba izbjegavati.
6.3 switch naredba
Naredba switch sliˇcna je nizu ugnjeˇzdenih if-else naredbi. Ona nam
omogu´cava da selektiramo kˆ od koji ´ce se izvrˇsiti na osnovu nekog uvjeta.
Naredba ima sljede´ci oblik:
switch(izraz) {
case konstanta_1:
naredba_1;
.......
break;
case konstanta_2:
108 POGLAVLJE 6. KONTROLA TOKA PROGRAMA
naredba_2;
.......
break;
.
.
.
case konstanta_n:
naredba_n;
.....
break;
default:
naredba;
.....
}
• Izraz u switch naredbi mora imati cjelobrojnu vrijednost (char, int ili
enum).
• Nakon kljuˇcne rijeˇci case pojavljuju se cjelobrojne konstante ili kons-
tantni izrazi.
• Pri izvrˇsavanju switch naredbe prvo se testira vrijednost izraza izraz.
Zatim se provjerava da li se dobivena vrijednost podudara s jednom
od konstanti: konstanta 1, konstanta 2, . . . ,konstanta n. Ukoliko
je izraz = konstanta i program se nastavlja naredbom naredba i i
svim naredbama koje dolaze nakon nje, sve do break naredbe. Nakon
toga program se nastavlja prvom naredbom iza switch naredbe.
• Ako izraz nije jednak niti jednoj konstanti, onda se izvrˇsava samo
naredba koja dolazi nakon kljuˇcne rijeˇci default i sve naredbe iza nje,
sve od vitiˇcaste zagrade koja omeduje switch naredbu.
• Sluˇcaj default ne mora nuˇzno biti prisutan u switch naredbi. Ako
nije i ako nema podudaranja izaza i konstanti, program se nastavlja
prvom naredbom iza switch naredbe.
Naredba switch je ˇcesto jasnija od niza if-else naredbi. Program iz
proˇsle sekcije mogli smo napisati pomo´cu switch naredbe na sljede´ci naˇcin:
#include <stdio.h>
int main(void)
{
6.3. SWITCH NAREDBA 109
float a,b;
char operacija;
printf("Upisati prvi broj: ");
scanf(" %f",&a);
printf("Upisati drugi broj: ");
scanf(" %f",&b);
printf("Upisati operaciju: zbrajanje(z), oduzimanje(o),\n");
printf(" mnozenje(m),dijeljenje(d) :");
scanf(" %c",&operacija);
switch(operacija){
case ’z’:
printf("%f\n",a+b);
break;
case ’o’:
printf("%f\n",a-b);
break;
case ’m’:
printf("%f\n",a*b);
break;
case ’d’:
printf("%f\n",a/b);
break;
default:
printf("Nedopustena operacija!\n");
}
return 0;
}
Namjera programera je sada jasnije izraˇzena nego s nizom if-else naredbi.
Naredba break moˇze se ispustiti na jednom ili viˇse mjesta. Efekt ispuˇstanja
naredbe break je “propadanje kˆ oda” u niˇzi case blok. Na primjer, ako bi-
smo u gornjem kˆ odu ispustili sve break narebe i ako bi operacija bila jed-
naka ’o’, onda bi bilo ispisano oduzimanje, mnoˇzenje, dijeljenje i poruka
"Nedopustena operacija!\n". Selektirani case je stoga ulazna toˇcka od
koje poˇcinje izvrˇsavanje kˆ oda. Izvrˇsavanje se nastavlja do prve break na-
redbe ili sve do kraja switch naredbe, ako nema break naredbi.
Pogledajmo sljede´ci primjer:
int i;
.....
110 POGLAVLJE 6. KONTROLA TOKA PROGRAMA
switch(i) {
case 1:
case 2:
case 3:
case 4:
case 5: printf("i < 6\n");
break;
case 6: printf("i = 6\n");
break;
default: printf("i > 6\n");
}
“Propadanje” kroz case blokove omogu´cava da se u sluˇcajevima i=1,2,3,4,5
izvrˇsi naredba printf("i < 6\n"). Kako se ne bi izvrˇsila naredba printf("i
= 6\n") morali smo staviti break nakon printf("i < 6\n").
6.4 while petlja
While petlja ima oblik
while (izraz) naredba;
naredba ´ce se izvrˇsavati sve dok izraz ima vrijednost istine, tj. sve dok je
razliˇcit od nule. naredba moˇze biti blok naredbi unutar vitiˇcastih zagrada
koji obiˇcno modificira izraz kako bi kontrolirao petlju.
Na primjer, dio kˆ oda
i=0;
while (i<10){
printf("%d\n",i);
i++;
}
ispisat ´ce brojeve 0,1,. . . ,9. while petlja se rijetko koristi u sluˇcaju kada je
broj ponavljanja petlje unaprijed poznat (kao u prethodnom primjeru); tada
je pogodnija for petlja. Petlju while ´cemo najˇceˇs´ce koristiti kada se broj
ponavljanja ne moˇze unaprijed znati, kao u ovom primjeru:
#include <stdui.h>
/* Srednja vrijednost upisanih brojeva (razlicitih od 0). */
int main(void)
{
6.5. FOR PETLJA 111
int i=0;
double sum=0.0,x;
printf(" Upisite niz brojeva !=0, ili nulu za kraj.\n");
printf(" x[0]= "); scanf("%lf",&x);
while (x!=0.0){
sum+=x;
printf(" x[%d]= ",++i);
scanf("%lf",&x);
}
sum/=i;
printf(" Srednja vrijednost = %f\n",sum);
return 0;
}
6.5 for petlja
Petlja for ima oblik
for(izraz_1; izraz_2; izraz_3) naredba;
i ekvivalentna je konstrukciji
izraz_1;
while (izraz_2){
naredba;
izraz_3;
}
izraz 1 inicijalizira parametre koji kontroliraju petlju, izraz 2 predstavlja
uvjete koji moraju biti zadovoljeni da bi se petlja nastavila izvrˇsavati, a
izraz 3 mijenja parametre koje je izraz 1 inicijalizirao.
for petlja zapoˇcinje inicijalizacijom kontrolnih parametara. izraz 2 se
izraˇcunava na poˇcetku svakog prolaza kroz petlju, a izraz 3 se izraˇcunava
na kraju svakog prolaza kroz petlju.
Niti jedan od tri izraza ne treba biti prisutan. Ako nema srednjeg izraza
pretpostavlja se da je njegova vrijednost 1. Stoga je for(;;); beskonaˇcna
petlja koja ne radi niˇsta.
Na primjer, sljede´ci kˆ od raˇcuna skalarni produkt dva vektora:
float x[100000], y[100000], xydot;
double tmp;
112 POGLAVLJE 6. KONTROLA TOKA PROGRAMA
.........
tmp=0.0; // inicijalizacija varijable
// u kojoj se sumira
for(i=0;i<100000;i++) tmp += x[i]*y[i];
xydot=tmp;
Ovdje smo upotrijebili varijablu sumacije u dvostrukoj preciznosti kako bismo
smanjili greˇske zaokruˇzivanja.
Kao sloˇzeniji primjer napiˇsimo implementaciju funkcije atoi iz stan-
dardne biblioteke (zaglavlje <stdlib.h>), koja prevodi niz znakova koji pred-
stavlja cjeli broj u njegovu numeriˇcku vrijednost. Funkcija treba preskoˇciti
sve poˇcetne bjeline, uˇcitati predznak i redom proˇcitati sve znamenke te ih
prevesti u broj tipa int.
#include <ctype.h>
/* atoi : prevodi string s[] u cijeli broj */
int atoi(const char s[])
{
int i,n,sign;
for(i=0;isspace(s[i]);i++) /* preskace sve bjeline */
; /* prazna naredba */
sign=(s[i]==’-’)?-1:1;
if(s[i]==’+’ || s[i]==’-’) i++; /* preskoci predznak */
for(n=0; isdigit(s[i]);i++)
n=10*n+(s[i]-’0’);
return sign*n;
}
Ovdje koristimo funkcije isspace i isdigit koje su deklarirane u datoteci
zaglavlja <ctype.h>. Obje funkcije uzimaju jedan znak i vra´caju cijeli broj
(int). isspace vra´ca broj razliˇcit od nule (istina) ako je znak bjelina, prijelaz
u novi red ili tabulator. U suprotnom vra´ca nulu (laˇz). Analogno, isdigit
vra´ca broj razliˇcit od nule (istina) ako je znak znamenka, a nulu (laˇz) ako
nije.
U prvoj for petlji preskoˇcene su sve bjeline. Zatim je uvjetnim operato-
rom odreden predznak broja (koji ne mora biti prisutan). Ako je predznak
prisutan sljede´ca if naredba ga preskaˇce. Sada znamo da su sve znamenke
pred nama brojevi. Posljednja for petlja izvrˇsava se dok ne naidemo na znak
koji nije broj. Znamenke se pretvaraju u brojeve u naredbi
n=10*n+(s[i]-’0’);
6.5. FOR PETLJA 113
Tu se oslanjamo na ˇcinjenicu da se u svim skupovima znakova koji su u upo-
trebi znamenke nalaze jedna iza druge u rastu´cem poretku. Tada s[i]-’0’
daje numeriˇcku vrijednost znamenke s[i].
6.5.1 Operator zarez
U jednoj for naredbi mogu´ce je izvrˇsiti viˇse inicijalizacija i promjena
brojaˇca. U tome nam pomaˇze operator zarez. Zarez (,) koji separira dva
izraza je ustvari operator. Izrazi separirani zarezom izraˇcunavaju se slijeva
na desno i rezultat ˇcitavog izraza je vrijednost desnog izraza. Na primjer,
mogli bismo pisati
i=(i=3,i+4);
i dobili bismo rezultat i=7. Operator zarez koristi se uglavnom u for naredbi.
Pokaˇzimo to na funkciji invertiraj koja invertira niz znakova.
#include <string.h>
/* invertiraj : invertiraj znakovni niz */
void invertiraj(char s[]) {
int c,i,j;
for(i=0,j=strlen(s)-1;i<j;i++,j--) {
c=s[i]; s[i]=s[j]; s[j]=c;
}
}
Funkcija strlen deklarirana je u datoteci zaglavlja <string.h> i daje duljinu
znakovnog niza (bez nul-znaka). Uoˇcimo da smo za pomo´cnu varijablu c
mogli koristiti varijablu tipa int (umjesto char).
Napomenimo joˇs da zarezi u pozivu funkcije (ako ona ima viˇse argu-
menata) nisu operatori i stoga nije garantirano izraˇcunavanje argumenata
funkcije slijeva na desno.
6.5.2 Datoteka zaglavlja <stdlib.h>
Funkcija atoi koju smo realizirali u primjeru u prethodnoj sekciji im-
plementirana je u standardnoj biblioteci i njen prototip se nalazi u datoteci
zaglavlja <stdlib.h>. U toj datoteci postoje i prototipovi nekih drugih
funkcija za konerziju od kojih navodimo:
double atof(const char *s) prevodi s u double,
int atoi(const char *s) prevodi s u int ,
long atol(const char *s) prevodi s u long .
114 POGLAVLJE 6. KONTROLA TOKA PROGRAMA
Pored ostalih navedimo joˇs i ove funkcije iz <stdlib.h>:
void exit(int status) normalno zaustavljanje programa,
int abs(int n) absolutna vrijednost,
long labs(long n) absolutna vrijednost.
6.6 do - while petlja
do-while petlja ima oblik
do
naredba;
while (izraz);
naredba ´ce se izvrˇsavati sve dok izraz ima vrijednost istine, tj. sve dok je
razliˇcit od nule. naredba moˇze biti blok naredbi unutar vitiˇcastih zagrada
koji obiˇcno modificira izraz kako bi zaustavio petlju. Za razliku od while
petlje vrijednost izraza se kontrolira na kraju prolaza kroz petlju. Petlja se
stoga izvrˇsava barem jednom.
Na primjer, dio kˆ oda
i=0;
do{
printf("%d\n",i);
i++;
} while (i<10);
ispisat ´ce brojeve 0,1,. . . ,9.
Kao primjer napiˇsimo funkciju itoa koja pretvara cijeli broj u znakovni
niz (string).
/* itoa : pretvara n u znakovni niz s */
void itoa(int n, char s[])
{
int i,sign;
if((sign=n)<0) n=-n; /* zapamtimo predznak */
i=0;
do {
s[i++]=n%10+’0’;
} while((n/=10)>0);
if(sign<0) s[i++]=’-’;
s[i]=’\0’;
6.7. NAREDBE BREAK I CONTINUE 115
invertiraj(s);
}
Algoritam slaˇze znamenke broja u obrnutom redoslijedu pa stoga koristimo
funkciju invertiraj() iz prethodne sekcije. Ostatak u dijeljenju s 10 sva-
kog cijelog broja je njegova posljednja dekadska znamenka. Stoga znamenke
dobivamo pomo´cu operatora %. Nako ˇsto izdvojimo posljednju znamenku
broj dijelimo s 10 (cjelobrojno dijeljenje) kako bismo ju eliminirali. Taj se
postupak obavlja u do--while petlji. Nakon zavrˇsetka te procedure dobive-
nom nizu znakova dodajemo predznak i nul-znak te ga prosljedujemo funkciji
invertiraj.
6.7 Naredbe break i continue
Naredba break sluˇzi za zaustavljanje petlje i izlazak iz switch naredbe.
Moˇze se koristiti sa for, while i do-while petljom. Pri nailasku na naredbu
break kontrola programa se prenosi na prvu naredbu iza petlje ili switch
naredbe unutar koje se break nalazi.
Naredba continue koristi se unutar for, while i do-while petlji. Nakon
nailaska na continue preostali dio tijela petlje se preskaˇce i program nas-
tavlja sa sljede´cim prolazom kroz petlju. U sluˇcaju while i do-while petlje
to znaˇci da se program nastavlja s izraˇcunavanjem testa petlje. Kod for
petlje program se nastavlja s naredbom inkrementacije brojaˇca, budu´ci da
ona dolazi nakon tijela petlje. Nakon toga se prelazi na izraˇcunavanje testa
petlje.
Primjer upotrebe break naredbe u while petlji:
int i;
while(1){
scanf("%d",&i);
if (i<0) break;
.........
}
while(1) je beskonaˇcna petlja. Iz nje se izlazi ukoliko se uˇcita negativan
broj. Kˆ od koji bi samo preskakao negativne vrijednosti (i ne bi ih obradivao)
mogao bi se izvesti naredbom continue:
int i;
while{1){
scanf("%d",&i);
if (i<0) continue;
116 POGLAVLJE 6. KONTROLA TOKA PROGRAMA
.........
}
Sada nam u dijelu kˆ oda koji obraduje pozitivne brojeve treba neki drugi
naˇcin izlaza iz petlje.
Pogledajmo joˇs jedan primjer continue naredbe:
i=0;
while(i < 10)
{
ch=getchar();
if(ch == ’\n’) continue;
putchar(ch);
i++;
}
Ovaj ´ce kˆ od proˇcitati 10 znakova ne raˇcunaju´ci znak za prijelaz u novi red.
Naime, nakon continue, petlja se nastavlja iza posljednje naredbe u tijelu.
U while petlji to znaˇci da se preskaˇce naredba i++; koja pove´cava brojaˇc i
prva sljede´ca naredba je ispitivanje uvjeta i < 10.
Naredba continue u for petlji malo je drugaˇciji. Pogledajmo primjer
for(i=0; i < 10; ++i)
{
ch=getchar();
if(ch == ’\n’) continue;
putchar(ch);
}
Nakon izvrˇsenja naredbe continue dolazimo ponovo iz posljednje naredbe u
tijelu petlje, tj. putchar(ch); se preskaˇce. No u ovom sluˇcaju prva sljede´ca
naredba je pove´canje brojaˇca, ++i; a zatim testiranje i < 10. Znak za
prijelaz u novi red je sada ukljuˇcen u ukupan zbroj uˇcitanih znakova.
Ako se naredba break nalazi u petlji koja je ugnjeˇzdena u nekoj drugoj
petlji, onda se pomo´cu break izlazi samo iz dublje petlje.
6.8 goto naredba
goto naredba prekida sekvencijalno izvrˇsavanje programa i nastavlja iz-
vrˇsavanje s naredbom koja je oznaˇcena labelom koja se pojavljuje u goto.
Oblik joj je
goto label;
6.8. GOTO NAREDBA 117
gdje je label identifikator koji sluˇzi za oznaˇcavanje naredbe kojom se nas-
tavlja program. Sintaksa je
label: naredba;
Na primjer,
scanf("%d",&i);
while{i<=100){
.......
if (i<0) goto error;
.......
scanf("%d",&i);
}
.......
/* detekcija greske */
error: {
printf("Greska : negativna vrijednost!\n");
exit(-1);
}
Labela na koju se vrˇsi skok mora biti unutar iste funkcije kao i goto naredba.
Drugim rijeˇcima, pomo´cu goto se ne moˇze iza´ci iz funkcije.
Naredbe break i continue mogu se izvesti pomo´cu goto naredbe. Na
primjer, kˆ od
for(...) {
.......
if (...) continue;
........
}
je ekvivalentan s
for(...) {
.......
if (...) goto cont;
.......
cont: ;
}
Sliˇcno vrijedi i za continue unutar while i do-while petlje.
Program napisan uz upotrebu goto naredbe op´cenito je teˇze razumjeti
od programa koji ju ne koriste. Nadalje, neke upotrebe goto naredbe su
118 POGLAVLJE 6. KONTROLA TOKA PROGRAMA
posebno zbunjuju´ce. Na primjer, skok u if ili else blok izvan if naredbe,
ili skok u blok naredbu s preskokom deklaracija lokalnih varijabli na poˇcetku
bloka. Op´cenito stoga upotrebu goto naredbe treba izbjegavati.
Poglavlje 7
Funkcije
Funkcija je programska cjelina koja uzima neke ulazne podatke, izvrˇsava
odreden niz naredbi i vra´ca rezultat svog izvrˇsavanja pozivnom programu.
Pomo´cu funkcija razbijamo sloˇzene programske zada´ce na niz jednostav-
nijih cjelina. Time postiˇzemo ve´cu jasno´cu programa i olakˇsavamo budu´ce
modifikacije. Svaka funkcija treba biti osmiˇsljena tako da obavlja jednu dobro
definiranu zada´cu te da korisnik funkcije ne mora poznavati detalje njene im-
plementacije da bi ju koristio. Tada je funkciju mogu´ce koristiti u razliˇcitim
programima, kao ˇsto je to sluˇcaj s funkcijama iz standardne biblioteke.
7.1 Definicija funkcije
Definicija funkcija ima oblik
tip_podatka ime_funkcije(tip_1 arg_1, ... ,tip_n arg_n)
{
tijelo funkcije
}
gdje je tip podatka tip podatka koji ´ce funkcija vratiti kao rezultat svog
izvrˇsavanja. Unutar zagrada nalazi se deklaracija formalnih argumenata
funkcije. Prvi argument arg 1 je varijabla tipa tip 1 itd. Deklaracije pojedi-
nih argumenata medusobno se odvajaju zarezom. Unutar vitiˇcastih zagrada
pojavljuje se tijelo funkcije koje se sastoji od deklaracija varijabli i izvrˇsnih
naredbi.
Funkcija vra´ca rezultat svog izvrˇsavanja pomo´cu naredbe return. Op´ci
oblik te naredbe je
return izraz;
119
120 POGLAVLJE 7. FUNKCIJE
Vrijednost izraza se vra´ca dijelu programa koji poziva funkciju. Izraz se moˇze
staviti u oble zagrade ali to nije nuˇzno. Vrijedi sljede´ce pravilo:
• Funkcija moˇze vratiti aritmetiˇcki tip, strukturu, uniju ili pokazivaˇc ali
ne moˇze vratiti drugu funkciju ili polje.
Ako je tip izraza u naredbi return razliˇcit od tipa podatka koji funkcija
vra´ca, izraz ´ce biti konvertiran u tip podatka. Takvu situaciju treba naravno
izbjegavati.
Na primjer, sljede´ca funkcija pretvara mala slova (engleske abecede) u
velika. Formalni argument je samo jedan i tipa je char; vra´cena vrijednost
je takoder tipa char. Ime funkcije je malo u veliko.
char malo_u_veliko(char z)
{
char c;
c= (z >= ’a’ && z <= ’z’) ? (’A’ + z - ’a’) : z;
return c;
}
Funkcija pretpostavlja da slova abecede imaju rastu´ce uzastopne kodove te
da mala slova prethode velikim ili obratno. Ta je pretpostavka ispunjena za
ASCII naˇcin kodiranja, koji je najraˇsireniji, ali nije na primjer za EBCDIC
naˇcin kodiranja.
Funkcija se poziva navodenjem svog imena i liste stvarnih argumenata.
Funkciju iz prethodnog primjera u glavnom programu pozvamo na sljede´ci
naˇcin:
int main(void)
{
char malo, veliko;
printf("Unesite malo slovo: ");
scanf("%c", &malo);
veliko = malo_u_veliko(malo);
printf("\nUneseno slovo pretvoreno u veliko = %c\n",veliko);
return 0;
}
U gornjem programu malo je stvarni argument koji se kopira na mjesto for-
malnog argumenta z. Znak koji funkcija vra´ca smjeˇsten je u varijablu veliko
prilikom poziva
7.1. DEFINICIJA FUNKCIJE 121
veliko = malo_u_veliko(malo);
Vaˇzno je primijetiti da funkcija prima kopiju stvarnog argumenta tako da
nikakva manipulacija unutar same funkcije ne moˇze promijeniti stvarni ar-
gument (malo u naˇsem primjeru).
Funkcija koja vra´ca neku vrijednost najˇceˇs´ce se poziva u izrazu pridruˇzi-
vanja kao u gornjem primjeru, no funkciju je mogu´ce pozvati i unutar svakog
drugog izraza koji uzima tip podatka koji ona vra´ca. Na primjer, u glavnom
programi mogli smo pisati
printf("\nUneseno slovo pretvoreno u veliko = %c\n",
malo_u_veliko(malo));
i tako eliminirati varijablu veliko. Analogno, pri pozivu funkcije na mjestu
stvarnih argumenata mogu se nalaziti izrazi umjesto varijabli. Pri tome ´ce
izrazi prvo biti izraˇcunati, a onda ´ce izraˇcunate vrijednosti biti proslijedene
funkciji. Na primjer, funkciju iz matematiˇcke biblioteke
double sqrt(double)
koja raˇcuna drugi korijen broja moˇzemo pozvati na sljede´ci naˇcin:
double x,y;
.......
y=sqrt(2*x-3)
i varijabla y ´ce sadrˇzavati vrijednost

2x −3.
Ukoliko se programski tok unutar funkcije grana, onda je mogu´ce imati
viˇse return naredbi unutar iste funkcije. Na primjer, prethodna funkcija
mogla je biti napisana u obliku
char malo_u_veliko(char z)
{
if(z >= ’a’ && z <= ’z’)
return(’A’ + z - ’a’);
else
return z;
}
Kada funkcija ne vra´ca nikakvu vrijednost onda se za tip “vra´cene vri-
jednosti” koristi kljuˇcna rijeˇc void. Na primjer, u funkciji
122 POGLAVLJE 7. FUNKCIJE
void maximum(int x, int y)
{
int z;
z=(x>=y) ? x : y;
printf("\nMaksimalna vrijednost =%d",z);
return;
}
void oznaˇcava da nikakva vrijednost ne´ce biti vra´cena u pozivni program.
Naredba return samo transferira programski tok u pozivni program i stoga
uz nju ne stoji izraz. U takvoj situaciji naredba return moˇze biti izosta-
vljena, no radi preglednosti programa bolje ju je zadrˇzati.
Funkcija koja ne uzima nikakve argumente definira se na sljede´ci naˇcin:
tip_podatka ime_funkcije(void)
{
tijelo funkcije
}
Kljuˇcna rijeˇc void unutar zagrada oznaˇcava da funkcija ne uzima argumente.
Takva se funkcija poziva s praznim zagradama kao
ime_funkcije();
ili najˇceˇs´ce u izrazu pridruˇzivanja
varijabla=ime_funkcije();
Zagrade su obavezne, one ustvari informiraju prevodioc da je simbol koji
stoji ispred zagrade, ime funkcije, ime neke funkcije.
Tijelo funkcije sastoji se od deklaracija varijabli i izvrˇsnih naredbi. De-
klaracije varijabli moraju prethoditi prvoj izvrˇsnoj naredbi. Budu´ci da na
poˇcetku svakog bloka moˇzemo deklarirati varijable, koje su onda lokalne za
taj blok (vidi sekciju 9.1.1), imamo mogu´cnost definiranja lokalnih varijabli
na poˇcetku svakog bloka unutar tijela funkcije; na primjer, u tijelu if naredbe
i sliˇcno.
To je pravilo izmijenjeno u standardu C99. Prema njemu deklaracije varijabli ne
moraju prethoditi prvoj izvrˇsnoj naredbi u bloku, tako da se izvrˇsne naredbe i deklaracije
mogu ispreplitati.
Napomena. U tradicionalnom C-u definicija funkcije je imala oblik
tip_podatka ime_funkcije(arg_1, ... ,arg_n)
tip_1 arg_1;
...
tip_n arg_n;
{
tijelo funkcije
}
7.2. DEKLARACIJA FUNKCIJE 123
Na primjer,
char malo_u_veliko(z)
char z;
{
char c;
c= (z >= ’a’ && z <= ’z’) ? (’A’ + z - ’a’) : z;
return c;
}
Takva definicija je dozvoljena i u ANSI C-u, no standard C99 ju proglaˇsava zastarjelom,
ˇsto znaˇci da ju u budu´cnosti prevodioci moˇzda ne´ce podrˇzavati. U svakom sluˇcaju takvu
sintaksu treba izbjegavati.
7.2 Deklaracija funkcije
Svaka bi funkcija prije svoga poziva u programu trebala biti deklarirana.
Deklaracija informira prevodilac o imenu funkcije, broju i tipu argumenata
te tipu povratne vrijednosti (tipu funkcije). Uloga deklaracije (prototipa) je
omogu´citi prevodiocu kontrolu ispravnosti poziva funkcije.
Ako je funkcija definirana u istoj datoteci u kojoj se poziva, prije svog
prvog poziva, onda definicija sluˇzi kao deklaracija te zasebna deklaracija
nije potrebna. Takva se situacija nalazi u jednostavnim programima koji
su smjeˇsteni u jednu datoteku i u kojima su sve funkcije definirane prije
main() funkcije. Ukoliko funkcija f1() poziva funkciju f2() treba paziti da
je f2() definirana prije f1(). U takvoj situaciji nikakve posebne deklaracije
nisu potrebne.
Ako ˇzelimo da su funkcije koje koristimo definirane nakon main() funk-
cije ili op´cenitije iza mjesta na kojem se pozivaju, trebamo koristiti njihove
deklaracije, odnosno prototipove.
Deklaracija funkcije ima oblik
tip_podatka ime_funkcije(tip_1 arg_1, ... ,tip_n arg_n);
Vidimo da je posve ista definiciji s tom razlikom da nema tijela funkcije i
deklaracija zavrˇsava toˇcka-zarezom. Deklaracija se treba pojaviti prije poziva
funkcije ukoliko funkcija nije prije toga definirana. Definicija i deklaracija
moraju biti u skladu.
Ilustrirajmo to na jednom primjeru. Prvo, funkcija moˇze biti definirana
prije svog poziva:
#include <stdio.h>
124 POGLAVLJE 7. FUNKCIJE
void maximum(int x, int y)
{
int z;
z=(x>=y) ? x : y;
printf("\nMaksimalna vrijednost =%d\n",z);
return;
}
int main(void)
{
int x,y;
printf("Unesite dva cijela broja: ");
scanf("%d %d", &x,&y);
maximum(x,y);
return 0;
}
U trenutku svog poziva prevodilac zna da je maximum funkcija koja uzima
dva argumenta tipa int i ne vra´ca niˇsta.
Ako ˇzelimo definiciju funkcije smjestiti nakon funkcije main() uvodimo
deklaraciju, odn. prototiop funkcije:
#include <stdio.h>
int main(void)
{
int x,y;
void maximum(int x, int y);
printf("Unesite dva cijela broja: ");
scanf("%d %d", &x,&y);
maximum(x,y);
return 0;
}
void maximum(int x, int y)
{
int z;
z=(x>=y) ? x : y;
printf("\nMaksimalna vrijednost =%d\n",z);
return;
}
7.2. DEKLARACIJA FUNKCIJE 125
Ovdje je funkcija maximum() deklarirana kao i druge varijable u glavnom
programu. Primijetimo da varijable x i y deklarirane u deklaraciji
void maximum(int x, int y);
imaju samo formalni znaˇcaj, i mogu se ispustiti. Formalno govore´ci te dvije
varijable nisu vidljive izvan prototipa funkcije i stoga nisu u koliziji s varija-
blama istog imena deklariranim u funkciji main() (vidi sekciju 9.1.3).
Druga mogu´cnost, koja se ˇceˇs´ce koristi, je deklarirati funkciju izvan main()
funkcije kao u ovom sluˇcaju.
#include <stdio.h>
void maximum(int, int);
int main(void)
{
int x,y;
printf("Unesite dva cijela broja: ");
scanf("%d %d", &x,&y);
maximum(x,y);
return 0;
}
void maximum(int x, int y)
{
int z;
z=(x>=y) ? x : y;
printf("\nMaksimalna vrijednost =%d\n",z);
return;
}
Uoˇcimo da smo ovdje u prototipu ispustili nazive varijabli. Prevodiocu su
potrebni samo broj i tipovi varijabli koje funkcija uzima. Spomenimo joˇs da
ovakvo ispuˇstanje imena varijabli u prototipu ne treba raditi ako imena vari-
jabli podsjeˇcaju na njihovu funkciju i time sluˇze dokumentiranju programa.
Prototipovi su uvedeni u C sa standardom C90 (ANSI standardom). Radi
kompatibilnosti sa starijim programima dozvoljeno je koristiti i funkcije koje
nisu prethodno deklarirane. Pri tome vrijedi sljede´ce pravilo:
• Prevodilac pretpostavlja da funkcija vra´ca podatak tipa int i ne pravi
nikakve pretpostavke o broju i tipu argumenata.
U tom sluˇcaju kaˇzemo da je funkcija definirana implicitno pravilima prevodioca.
ˇ
Cinjenica da prevodilac ne pravi nikakve pretpostavke o broju i tipu argu-
126 POGLAVLJE 7. FUNKCIJE
menata znaˇci samo da nije u stanju provjeriti je li poziv funkcije korektan ili
nije. Ukoliko nije to ´ce biti otkriveno tek prilikom izvrˇsavanja programa. Za
korektan poziv potrebno je da se stvarni i formalni argumenti slaˇzu u broju
i tipu (preciznije u sljede´coj sekciji).
U starijim programima mogu´ce je na´ci deklaraciju funkcije tipa
double f();
Takva deklaracija informira prevodilac o tipu funkcije (double), ali ne i o argumentima.
Prevodilac ponovo ne radi nikakve pretpostavke o broju i tipu argumenta. Takve dekla-
racije treba izbjegavati.
Spomenimo na ovom mjestu joˇs i to da u C++ gornja deklaracija znaˇci
double f(void);
U C++ sve funkcije moraju imati prototip.
Napomena. Mogu´cnost pozivanja funkcija koje nisu prethodno dekla-
rirane (prototipom ili definicijom) te funkcija s nepotpunim deklaracijama
dozvoljeno je jedino zbog kompatibilnosti sa starijim programima pisanim u
tradicionalnom C-u. U svim novim programima imperativ je koristiti proto-
tipove.
7.3 Prijenos argumenata
Argumenti deklarirani u definiciji funkcije nazivaju se formalni argumenti.
Izrazi koji se pri pozivu funkcije nalaze na mjestima formalnih argumenata
nazivaju se stvarni argumenti.
Prilikom poziva funkcije stvarni argumenti se izraˇcunavaju (ako su izrazi)
i kopiraju u formalne argumente. Funkcija prima kopije stvarnih argumenata
ˇsto znaˇci da ne moˇze izmijeniti stvarne argumente. Sljede´ci primjer ilustrira
naˇcin prijenosa argumenata.
#include <stdio.h>
void f(int x) {
x+=1;
printf("\nUnutar funkcije x=%d",x);
return;
}
int main(void)
{
int x=5;
printf("\nIzvan funkcije x=%d",x);
f(x);
printf("\nNakon poziva funkcije x=%d",x);
7.3. PRIJENOS ARGUMENATA 127
return 0;
}
Rezultat izvrˇsavanja programa ´ce biti:
Izvan funkcije x=5
Unutar funkcije x=6
Nakon poziva funkcije x=5
Istaknimo najvaˇznija pravila o prijenosu argumenata.
• Broj stvarnih argumenata pri svakom pozivu funkcije mora biti jednak
broju formalnih argumenata.
• Ako je funkcija ispravno deklarirana (ima prototip), tada se stvarni
argumenti ˇcije se tip razlikuje od odgovaraju´cih formalnih argumenta
konvertiraju u tip formalnih argumenata, isto kao pri pridruˇzivanju.
Takva konverzija pri tome mora biti mogu´ca.
• Ukoliko je funkcija na mjestu svog poziva deklarirana implicitno pra-
vilima prevodioca, tada prevodioc postupa na sljede´ci naˇcin: Na svaki
stvarni argument cjelobrojnog tipa primijenjuje se integralna promo-
cija (konverzija argumenata tipa short i char u int), a svaki stvarni
argument tipa float konvertira se u tip double. Nakon toga broj i
tip (konvertiranih) stvarnih argumenta mora se podudarati s brojem i
tipom formalnih argumenata da bi poziv funkcije bio korektan.
• Redosljed izraˇcunavanja stvarnih argumenat nije definiran i moˇze ovi-
siti o implemetaciji.
Pogledajmo na primjer sljede´ci program:
#include <stdio.h>
int main(void)
{
float x=2.0;
printf("%d\n",f(2)); // Neispravno
printf("%d\n",f(x)); // Ispravno
return 0;
}
128 POGLAVLJE 7. FUNKCIJE
int f(double x) {
return (int) x*x;
}
U prvom pozivu funkcije int f(double) glavni program ´ce funkciji poslati
cjelobrojno 2 kao argument, a funkcija ´ce argument interpretirati kao re-
alan broj dvostruke preciznosti. To ´ce dati ili pogreˇsan rezultat ili prekid
izvrˇsavanja programa. U drugom pozivu funkcije argument tipa float bit
´ce konvertiran u double i funkcija ´ce primiti ispravan argument. Uoˇcimo da
ako definiramo funkciju f() tako da uzima argument tipa float, onda niti
jedan poziv ne bi bio korektan.
Uvedemo li funkcijski prototip (ˇsto dobar stil programiranja nalaˇze)
int f(double);
int main(void)
{
float x=2.0;
printf("%d\n",f(2));
printf("%d\n",f(x));
return 0;
}
int f(double x) {
return (int) x*x;
}
funkcija ´ce u oba sluˇcja biti ispravno pozvana jer ´ce cjelobrojni argument 2
biti konvertiran u tip double.
S druge strane kˆ od
int main(void)
{
float x=2.0;
printf("%d\n",f(2)); /* greska */
printf("%d\n",f(x)); /* ispravno */
return 0;
}
double f(double x) {
return x*x;
}
7.4. INLINE FUNKCIJE 129
ne bi bio uspjeˇsno preveden. Naime, funkcija f je uvedena u glavnom pro-
gramu bez eksplicitne deklaracije pa prevodilac pretpostavlja da se radi o
funkciji tipa int (tj. o funkciji koja vra´ca vrijednost tipa int). Stoga defini-
ciju double f(double x) prevodilac shva´ca kao redefiniranje simbola f, ˇsto
nije dozvoljeno.
Prijenos argumenata funkciji u C-u vrˇsi se po vrijednosti. To znaˇci da se
stvarni argumenti kopiraju u formalne argumente te funkcija stvarne argu-
mente ne moˇze dohvatiti. Ukoliko se ˇzeli da funkcija mijenja stvarne argu-
mente, onda ona mora dobiti njihove adrese (vidi sekciju 11.2).
Ako je polje argument funkcije, onda se ono ne prenosi funkciji “po vri-
jednosti”, ve´c funkcija dobiva pokazivaˇc na prvi element polja pomo´cu kojeg
moˇze dohvatiti svaki element polja putem indeksa polja. Razlog za ovakav
naˇcin prijenosa polja je u tome ˇsto se dimenzija polja ne zna unaprijed, a ko-
piranje svih elemenata velikih polja bilo bi vrlo neefikasno (vidi sekciju 10.5).
Ukoliko funkcija ima argument tipa polja koje se u njoj ne mijenja, onda ga
treba deklarirati s modifikatorom const (sekcija 11.5).
7.4 Inline funkcije
Svaki poziv funkcije predstavlja odreden utroˇsak procesorskog vremena. Procesor
treba zaustaviti izvrˇsavanje glavnog programa, spremiti sve podatke nuˇzne za njegov nas-
tavak nakon izlaska iz funkcije, predati funkciji argumente i poˇceti izvrˇsavati kˆ od funkcije.
Kod malih funkcija, kao ˇsto je to na primjer
double f(double x) {
return x*x;
}
sam poziv funkcije moˇze uzeti viˇse procesorskog vremena nego izvrˇsavanje kˆ oda funkcije i
time znaˇcajno usporiti program ako se funkcija ˇcesto poziva.
Da bi se to izbjeglo C99 dozvoljava da se funkcija deklarira inline:
inline double f(double x) {
return x*x;
}
Kljuˇcna rijeˇc inline je sugestija prevodiocu da ekspandira tijelo funkcije na mjestu na
kojem se ona poziva, izbjegavaju´ci tako poziv funkcije. Prevodilac nije duˇzan ispuniti taj
zahtijev na svakom mjestu.
Osnovno ograniˇcenje kod upotrebe inline funkcije je ˇsto njena definicija (a ne samo
deklaracija) mora biti vidljiva na mjestu poziva funkcije. To ne predstavlja problem kod
statiˇcih funkcija, no ako je funkcija definirana u nekoj drugoj datoteci, taj ´ce uvjet biti
naruˇsen. Tada se postupa tako da se definicija funkcije stavi u datoteku zaglavlja koja se
potom ukljuˇci u svaku .c datoteku koja funkciju koristi.
130 POGLAVLJE 7. FUNKCIJE
7.5 Rekurzivne funkcije
C dozvoljava da se funkcije koriste rekurzivno, odnosno da pozivaju same
sebe. Na primjer, za raˇcunanje n! = 1· 2· 3 · · · n moˇzemo napisati rekurzivnu
funkciju
long faktorijeli(long n) {
if(n<=1) return 1;
else return n*faktorijeli(n-1);
}
Funkcija (rekurzivno) poziva samu sebe n-1 puta kako bi izraˇcunala n!.
Uoˇcimo da nakon poziva funkciji faktorijeli s argumentom n, strogo ve´cim
od 1, dolazi do poziva funkcije faktorijeli s argumentom n-1. U toj funk-
ciji dolazi do poziva funkcije faktorijeli s argumentom n-2 i tako dalje,
sve dok ne dode do poziva funkcije faktorijeli s argumentom 1. Tada naj-
dublje ugnjeˇzdena funkcija faktorijeli vrati vrijednost 1. Funkcija koja ju
je pozvala vrati vrijednost 2*1, sljede´ca vrati 3*2*1 itd. dok konaˇcno prvo
pozvana funkcija ne vrati vrijednost n!.
f(4)
f(3)
f(2)
f(1) → f(1) = 1
f(2) → 2f(1) = 2
f(3) → 3f(2) = 6
f(4) → 4f(3) = 24
Uoˇcimo da svaka rekurzivna funkcija mora imati odreden kriterij izlaska
iz rekurzije. U ovom sluˇcaju je to uvjet n<=1.
Rekurzivno koriˇstenje funkcija ˇcesto rezultira vrlo elegantnim algorit-
mima no ono ima i svojih nedostataka. Osnovni je taj ˇsto je npr. u ovom
sluˇcaju bilo potrebno n poziva funkcije da bi se izraˇcunao broj n!. Nasuprot
tome, faktorijele moˇzemo raˇcunati i nerekurzivnim postupkom kao npr. u
funkciji
long faktorijeli(long n) {
long f=1;
for(;n>1;n--) f*=n;
return f;
}
7.5. REKURZIVNE FUNKCIJE 131
ˇsto je efikasnije jer trebamo samo jedan poziv funkcije.
Pogledajmo joˇs jedan primjer rekurzivne funkcije:
void unos(void){
char znak;
if((znak=getchar())!=’\n’) unos();
putchar(znak);
}
Funkcija uˇcitava znakove sa standardnog ulaza sve dok ne naide do prijelaza
u novu liniju i ispisuje uˇcitane znakove. Ako izvrˇsimo program
#include <stdio.h>
void unos(void);
int main(void)
{
printf("\n Unesite niz znakova: ");
unos();
return 0;
}
dobit ´cemo ovaj rezultat:
Unesite niz znakova: Zdravo
ovardZ
Prvo je ispisan znak za prijelaz u novi red, a zatim niz Zdravo u obrnutom
redoslijedu.
Svaki poziv funkcije unos uˇcita jedan znak sa standardnog ulaza i spremi
ga u lokalnu varijablu znak. Ukoliko uˇcitani znak nije znak prijelaza u novi
red ponovo se poziva funkcija unos.
Svaka varijabla definirana unutar funkcije vidljiva je samo u toj funkciji
(vidi sekciju 9.1.1). Ona se kreira kod poziva funkcije i nestaje nakon izlaza
iz funkcije. Stoga pri svakom novom pozivu funkcije unos kreira se nova
varijabla s imenom znak. Budu´ci da varijabla definirana u funkciji (unutarnja
varijabla) nije vidljiva izvan te funkcije, prisutnost viˇse razliˇcitih varijabli s
imenom znak je dozvoljeno.
Dakle, pri svakom novom pozivu funkcije unos bit ´ce proˇcitan i pohra-
njen jedan znak sve dok ne naidemo do znaka za prijelaz u novi red. Tada
pozvana funkcija ispiˇse znak za prijelaz u novi red i izade. Funkcija koja ju je
pozvala ispisat ´ce znak koji je proˇcitala (znak ’o’) i predaje kontrolu funkciji
132 POGLAVLJE 7. FUNKCIJE
koja ju je pozvala. Na taj se naˇcin svi upisani znakovi ispisuju u obrnutom
redoslijedu.
U ovom jednostavnom primjeru vidi se drugi nedostatak rekurzivnog pro-
grama. Pri svakom novom rekurzivnom pozivu funkcije kreiraju se ponovo
sve lokalne varijable. U gornjem primjeru koristili smo onoliko varijabli koliko
je bilo upisanih znakova, mada bi jedna bila sasvim dovoljna.
7.6 Funkcije s varijabilnim brojem argume-
nata
Funkcije scanf i printf primjeri su funkcija koje primaju varijabilan
broj argumenata. Datoteka zaglavlja <stdarg.h> sadrˇzi neke definicije i
makro naredbe (vidi sekciju 8) koje omogu´cavaju programeru pisanje valstitih
funkcija s varijabilnim brojem argumenata.
U <stdarg.h> je definiran tip podatka va list koji predstavlja poka-
zivaˇc na listu nedeklariranih argumenata. Stoga na poˇcetku funkcije treba
definirati varijablu tipa va list. Na primjer:
va_list pa;
Varijabla pa se inicijalizira pomo´cu funkcije va start. Ona uzima kao prvi
argument varijablu pa, a kao drugi argument posljednji deklarirani argument
funkcije. To znaˇci da funkcija mora imati barem jedan deklarirani argument.
Kao rezultat varijabla pa ´ce pokazivati na prvi nedeklarirani argument funk-
cije.
Kada se ˇzeli proˇcitati jedan nedeklarirani argument koristi se va arg.
Pri tome moramo znati tip argumenta. va arg uzima kao prvi argument
pokazivaˇc pa, a kao drugi tip argumenta te vra´ca vrijednost argumenta i
pove´cava pokazivaˇc pa tako da pokazuje na sljede´ci nedeklarirani argument.
Na primjer, ako je argument na koji pa pokazuje tipa int i vali varijabla
tipa int, onda bismo imali poziv oblika
vali=va_arg(pa,int);
Nakon njega pa ´ce pokazivati na sljede´ci nedeklarirani argument funkcije.
Nedeklarirani argumenti funkcije pri pozivu funkcije podvrgnuti su stan-
dardnoj promociji koja vrijedi za funkcije bez prototipa: manji integralni
tipovi konvertiraju se u int, a float u double.
Konaˇcno, deklaracija funkcije s varijabilnim brojem argumenata ima oblik:
tip ime_funkcije(tip_1 arg1, tip_2 arg_2,...);
7.6. FUNKCIJE S VARIJABILNIM BROJEM ARGUMENATA 133
Ovdje je dat primjer s dva deklarirana argumenata, mada broj deklariranih
argumenata moˇze biti bili koji broj ve´ci ili jednak 1. Tri toˇcke iza pos-
ljednjeg deklariranog argumenta oznaˇcava da funkcija uzima varijabilan broj
argumenata. Pokaˇzimo jedan jednostavan primjer. Napisat ´cemo funkciju
koja sumira n brojeva tipa double koji su joj dani kao argumenti. Pri tome
je n zadan kao prvi argument.
#include <stdio.h>
#include <stdarg.h>
double suma(int, ...);
int main(void)
{
double s,t;
s=suma(3,1.0,2.0,3.0);
t=suma(5,1.0,2.0,3.0,4.0,5.0);
printf("%g %g\n",s,t);
return 0;
}
double suma(int n,...)
{
va_list ap;
double total=0.0;
int i;
va_start(ap,n);
for(i=0; i<n; ++i)
total += va_arg(ap,double);
va_end(ap);
return total;
}
Uoˇcimo da kroz deklarirane argumente moramo na neki naˇcin funkciji dati
informaciju sa koliko je argumenata pozvana i kojeg su tipa. Funkcije printf
i scanf to rade putem kontrolnog stringa.
Nakon ˇsto su svi parametri proˇcitani potrebno je pozvati funkciju va end
koja zavrˇsava proces ˇcitanja argumenta.
Poglavlje 8
Preprocesorske naredbe
Prije prevodenja izvornog koda u objektni ili izvrˇsni izvrˇsavaju se pre-
procesorske naredbe. Svaka linija izvornog koda koja zapoˇcinje znakom #
predstavlja
1
jednu preporocesorsku naredbu. Njih izvrˇsava zaseban dio pre-
vodioca koji se naziva preprocesor, i koji prije samog procesa prevodenja na
osnovu preprocesorskih naredbi mijenja izvorni kˆ od.
Op´ci oblik preprocesorskih naredbi je
#naredba parametri
i one nisu sastavni dio jezika C te ne podlijeˇzu sintaksi jezika. Svaka prepro-
cesorska naredba zavrˇsava krajem linije (a ne znakom toˇcka-zarez). Neke od
preprocesorskih naredbi su
#include #define #undef #if #ifdef #ifndef #elif #else
8.1 Naredba #include
Naredba #include moˇze se pojaviti u dva oblika:
#include "ime_datoteke"
ili
#include <ime_datoteke>
U oba sluˇcaja preprocesor ´ce obrisati liniju s #include naredbom i ukljuˇciti
sadrˇzaj datoteke ime datoteke u izvorni kˆ od, na mjestu #include naredbe.
1
Znaku # mogu prethoditi bjeline, no neki stariji prevodioci (makroprocesori) zahtije-
vaju da # bude prvi znak u liniji.
134
8.2. NAREDBA #DEFINE 135
Ako je ime datoteke navedeno unutar navodnika, onda preprocesor dato-
teku traˇzi u direktoriju u kojem se izvorni program nalazi. Ime datoteke
navedeno izmedu oˇstrih zagrada signalizira da se radi o sistemskoj datoteci
(kao npr. stdio.h), pa ´ce preprocesor datoteku traˇziti na mjestu odredenom
operacijskim sustavom. Pod unix-om to je najˇceˇs´ce direktorij /usr/include
a postoji i mogu´cnost da se prevodiocu da instrukcija (-I) da te datoteke
traˇzi u unaprijed zadanom direktoriju.
Datoteke zaglavlja se najˇceˇs´ce koriste za ukljuˇcivanje sljede´cih veliˇcina:
• Simboliˇckih konstanti — U stdio.h tako imamo EOF, NULL itd.
• Makro funkcija — Na primjer getchar() koji je obiˇcno definiran kao
getc(stdin) gdje je getc() makro funkcija.
• Deklaracije funkcija — U string.h je deklariran niz funkcija za rad sa
stringovima.
• Deklaracije struktura — U stdio.h se definira struktura FILE.
• Definicije tipova — Na primjer size t, time t, itd.
Datoteka ukljuˇcena u izvorni kˆ od pomo´cu #include naredbe moˇze i sama
sadrˇzavati #include naredbe.
8.2 Naredba #define
Njena je forma sljede´ca:
#define ime tekst_zamjene
Preprocesor ´ce od mjesta na kome se #define naredba nalazi do kraja dato-
teke svako pojavljivanje imena ime zamijeniti s tekstom tekst zamjene. Do
zamjene ne´ce do´ci unutar znakovnih nizova, tj. unutar dvostrukih navodnika.
Tako ´ce na primjer dio izvornog kˆ oda
#define PI 3.14
......
x=2*r*PI;
prije prevodenja biti zamijenjen s
......
x=2*r*3.14;
136 POGLAVLJE 8. PREPROCESORSKE NAREDBE
no u naredbi printf("PI"); do zamjene ne bi doˇslo. Svako ime definirano
u nekoj #define naredbi nazivamo makro.
Naredba #define moˇze se koristiti i bez teksta zamjene kao
#define ime
Nakon te naredbe ime je definirano, ˇsto se moˇze ispitivati pomo´cu #if na-
redbe. Ako je neko ime definirano pomo´cu #define, definicija se moˇze
poniˇstiti pomo´cu naredbe #undef (vidi sekciju 8.4).
8.3 Parametrizirana #define naredba
Naredba #define osim za definiranje simboliˇckih konstanti sluˇzi i za de-
finiranje parametriziranih makroa.
U parametriziranoj #define naredbi simboliˇcko ime i tekst koji zamije-
njuje simboliˇcko ime sadrˇze argumente koji se prilikom poziva makroa zami-
jenjuju stvarnim argumentima. Sintaksa je sljede´ca:
#define ime(argumenti) text_zamjene
Jedan primjer parametriziranog makroa je
#define max(A,B) ((A)>(B) ? (A) : (B))
gdje su A i B argumenti. Ako se u kodu pojavi naredba
x=max(a1,a2);
preprocesor ´ce ju zamijeniti s
x=((a1)>(a2) ? (a1) : (a2));
Formalni argumenti (parametri) A i B zamijenjeni su sa stvarnim argumen-
tima a1 i a2. Ako pak na drugom mjestu imamo naredbu
x=max(a1+a2,a1-a2);
ona ´ce biti zamijenjena s
x=((a1+a2)>(a1-a2) ? (a1+a2) : (a1-a2));
Vidimo da je parametrizirani makro vrlo sliˇcan funkciji no u njemu nema
funkcijskog poziva i prenoˇsenja argumenata, pa je stoga efikasniji.
Sliˇcnost makroa i funkcije moˇze zavarati. Ako bismo makro max pozvali
na sljede´ci naˇcin
8.3. PARAMETRIZIRANA #DEFINE NAREDBA 137
max(i++,j++);
varijable i, j ne bi bile inkrementirane samo jednom (kao pri funkcijskom
pozivu) ve´c bi ve´ca varijabla bila inkrementirana dva puta.
Argumente makroa treba stavljati u zagrade kako bi se izbjegla situacija
ilustrirana u sljede´cem primjeru: ako definiramo
#define kvadrat(x) x * x
onda bi kvadrat(x+1) dao x+1 * x+1, ˇsto oˇcito nije ono ˇsto smo htjeli.
Zadatak. Pretpostavimo da smo definirali
#define max(A,B) (A)>(B) ? (A) : (B)
ˇ
Sto bi bio rezultat poziva
#define x=y+max(0.1,0.2);
ovisno o y?
Unutar dvostrukih navodnika parametar makroa ne´ce biti zamijenjen
stvarnim argumentom. Na primjer, ako imamo definiciju
#define PSQR(X) printf("Kvadrat od X je %d.\n",((X)*(X)));
onda bi poziv
PSQR(5);
ispisao
Kvadrat od X je 25.
To oˇcito nije ono ˇsto smo htjeli, jer je X u znakovnom nizu tretiran kao obiˇcan
znak, a ne kao parametar makroa. To se moˇze ispraviti pomo´cu operatora #.
To je operator koji makro parametar pretvara u string. Korektana definicija
bi bila:
#include <stdio.h>
#define PSQR(X) printf("Kvadrat od " #X " je %d.\n",((X)*(X)));
int main(void)
{
int y=5;
PSQR(y);
PSQR(2+4);
return 0;
}
138 POGLAVLJE 8. PREPROCESORSKE NAREDBE
Program ´ce ispisati
Kvadrat od y je 25.
Kvadrat od 2+4 je 36.
Napomena. Neke su “funkcije” deklarirane u <stdio.h> ustvari makroi,
na primjer getchar i putchar. Isto tako, funkcije u <ctype.h> uglavnom su
izvedene kao makroi.
Definiciju nekog imena moˇze se poniˇstiti pomo´cu #undef. Na primjer,
#undef getchar
U #define naredbi tekst zamjene se prostire od imena koje definiramo do
kraja linije. Ako ˇzelimo da ime bude zamijenjeno s viˇse linija teksta moramo
koristiti kosu crtu (\) na kraju svakog reda osim posljednjeg. Na primjer,
makro za inicijalizaciju polja moˇzemo definirati na sljede´ci naˇcin:
#define INIT(polje, dim) for(int i=0; i < (dim); ++i) \
(polje)[i]=0.0;
Osnovna prednost parametriziranih makroa pred obiˇcnim funkcijama je
u brzini izvrˇsavanja – makro nam ˇstedi jedan funkcijski poziv. To moˇze biti
znaˇcajno ukoliko se makro poziva veliki broj puta. Prednost moˇze biti i to
ˇsto mako ne kontrolira tip svojih argumenata. Tako npr. makro kvadrat(x)
moˇzemo koristiti s x bilo kojeg skalarnog tipa. S druge strane osnovni je
nedostatak ˇsto prevodilac ne moˇze provjeriti korektnost poziva makroa za
vrijeme prevodenja. U tom smislu je inline funkcija bolje rjeˇsenje.
Generalna je konvencija da se parametrizirani makroi piˇsu velikim slo-
vima.
8.4 Uvjetno ukljuˇcivanje
Pomo´cu preprocesorskih naredbi #if, #else, #elif moˇzemo uvjetno uk-
ljuˇcivati ili iskljuˇcivati pojedine dijelove programa. Naredba #if ima sljede´ci
oblik:
#if uvjet
blok naredbi
#endif
Ukoliko je uvjet ispunjen blok naredbi izmedu #if uvjet i #endif bit ´ce
ukljuˇcen u izvorni kˆ od; ako uvjet nije ispunjen blok ne´ce biti ukljuˇcen.
8.4. UVJETNO UKLJU
ˇ
CIVANJE 139
Uvjet koji se pojavljuje u #if naredbi je konstantan cjelobrojni izraz.
Nula se interpretira kao laˇz, a svaka vrijednost razliˇcita od nule kao istina.
Simboliˇcka imena se prije izraˇcunavanja izraza zamijenjuju svojim vrijednos-
tima. Ukoliko se u uvjetu pojavi simboliˇcko ime koje nije prije toga definirano
nekom #define naredbom, ono se zamijenjuje nulom.
Najˇceˇs´ce se ukljuˇcivanje odnosno iskljuˇcivanje dijela kˆ oda pomo´cu na-
redbe #if ˇcini u ovisnosti o tome da li je neka varijabla definirana ili nije.
Tu nam pomaˇze izraz defined(ime) koji daje 1 ako je ime definirano, a 0
ako nije. Na primjer,
#if !defined(__datoteka.h__)
#define __datoteka.h__
/* ovdje dolazi datoteka.h */
#endif
Ako varijabla datoteka.h nije definirana ona ´ce u sljede´coj #define na-
redbi biti definirana i datoteka.h ´ce biti ukljuˇcena. U suprotnom ´ce cijela
datoteka.h biti jednostavno preskoˇcena. To je standardna tehnika kojom
se izbjegava viˇsestruko ukljuˇcivanje .h datoteka u program (provjerite npr.
datoteku stdio.h).
Budu´ci da se konstrukcije #if defined i #if !defined ˇcesto pojavljuju
postoje kra´ci izrazi s istim znaˇcenjem: #ifdef i #ifndef. Tako smo pret-
hodnu konstrukciju mogli napisati u obliku
#ifndef __datoteka.h__
#define __datoteka.h__
/* ovdje dolazi datoteka.h */
#endif
Zagrade oko varijabli nisu obavezne.
Napomena. #ifdef ime i #if ime su ekvivalentne naredbe ako je ime
simboliˇcko ime koje, kad je definirano, daje cjelobrojni konstantan izraz,
razliˇcit od nule.
Sloˇzene if naredbe grade se pomo´cu #else i #elif, koji ima znaˇcenje
else if. Na primjer,
#if SYSTEM == SYSV
#define DATOTEKA "sysv.h"
#elif SYSTEM == BSD
140 POGLAVLJE 8. PREPROCESORSKE NAREDBE
#define DATOTEKA "bsd.h"
#elif SYSTEM == MSDOS
#define DATOTEKA "msdos.h"
#else
#define DATOTEKA "default.h"
#endif
Ovdje se testira ime SYSTEM kako bi se ukljuˇcila prava datoteka zaglavlja.
U razvoju programa korisno je ispisivati ˇsto ve´ci broj medurezultata kako
bismo mogli kontrolirati korektnost izvrˇsavanja programa. Nakon ˇsto je pro-
gram zavrˇsen i testiran sav suviˇsan ispis treba eliminirati. U tome nam
pomaˇze uvjetno ukljuˇcivanje kˆ oda kao ˇsto se vidi na sljede´cem primjeru:
int x;
......
scanf("%d",&x);
#ifdef DEBUG
printf("Debug:: x=%d\n",x);
#endif
Ukoliko je varijabla DEBUG definirana, uˇcitana vrijednost ´ce biti ispisana.
Prevodioci pod unix-om obiˇcno imaju -D opciju, koja se koristi u obliku
-Dsimbol, i koja dozvoljava da se simbol definira na komandnoj liniji. Na
primjer, pretpostavimo da je program koji sadrˇzi prikazani dio kˆ oda smjeˇsten
u datoteku prog.c. Tada ´ce kompilacija naredbom
cc -o prog prog.c
proizvesti program u koji ispis varijable x nije ukljuˇcen. Kompilacija s na-
redbom
cc -DDEBUG -o prog prog.c
dat ´ce izvrˇsni kˆ od koji ukljuˇcuje printf naredbu, jer je sada varijabla DEBUG
definirana.
Tehnika koja se koristi u razvoju programa je sljede´ca: svi ispisi medure-
zultata ubacuju se izmedu para #ifdef DEBUG i #endif naredbi i program
se u razvojnoj fazi kompilira s -DDEBUG opcijom. Kada je program konaˇcno
zavrˇsen i testiran kompilira se bez -DDEBUG opcije. Na taj naˇcin se iz izvrˇsnog
kˆ oda izbacuju sve suviˇsne printf (i ostale) naredbe.
Napomena. Pomo´cu #if naredbe moˇzemo iskljuˇciti dio koda iz programa
na sljede´ci naˇcin:
8.5. PREDEFINIRANI MAKROI 141
#if 0
dio programa
koji iskljucujemo
#endif
Time se izbjegava problem ugnijeˇzdenih komentara.
8.5 Predefinirani makroi
C standard specificira nekoliko makroa koje moraju biti definirane. Neki
od njih su
Makro Znaˇcenje
DATE Datum preprocesiranja
TIME Vrijeme preprocesiranja
FILE Ime datoteke s izvornim kˆ odom
LINE Trenutna linija kˆ oda
func Ime funkcije.
Zadnji makro je uveden standardom C99. Na primjer,
#include <stdio.h>
int main(void)
{
printf("Ime datoteke: %s.\n",__FILE__);
printf("Datum: %s.\n",__DATE__);
printf("Vrijeme: %s.\n",__TIME__);
printf("Linija koda: %d.\n",__LINE__);
printf("Ime funkcije: %s.\n",__func__);
return 0;
}
Ovaj kˆ od na jednom sustavu ispisuje
Ime datoteke: makro_2.c.
Datum: Feb 12 2003.
Vrijeme: 19:28:06.
Linija koda: 9.
Ime funkcije: main.
Ovi se makroi najˇceˇs´ce koriste za ispis poruka o greˇskama kao u sljede´cem
primjeru:
if(n != m)
printf("Greska: linija %d, datoteka %s\n",__LINE__, __FILE__);
142 POGLAVLJE 8. PREPROCESORSKE NAREDBE
8.6 assert
Mnoge funkcije oˇcekuju da ´ce dobiti argumente koji zadovoljavaju odredene
uvjete. Na primjer, funkcija koja prima jedan argument tipa double moˇze
oˇcekivati samo pozitivne vrijednosti. Predaja negativne vrijednosti vrlo ˇcesti
signalizira da se desila greˇska u izvrˇsavanju programa. U takvim situacijama
ˇzeljeli bismo provjeriti na poˇcetku izvrˇsavanja funkcije jesu li uvjeti koje ar-
gumenti moraju zadovoljavati ispunjeni. Budu´ci da takvih provjera moˇze
biti jako puno u ve´cim programima namjera nam je iskljuˇciti sve te provjere
u konaˇcnoj verziji kˆ oda. Tu nam ponovo moˇze pomo´ci tehnika s #ifdef
DEBUG, no ANSI-C nam nudi bolju opciju, a to je makro assert.
Da bismo iskoristili makro assert trebamo ukljuˇciti standardnu datoteku
zaglavlja <assert.h>. Zatim se assert koristi kao da se radi o funkciji oblika
void assert(int izraz)
Ako je izraz jednak nuli u trenutku kada se izvrˇsava naredba
assert(izraz);
assert ´ce ispisati poruku
Assertion failed: izraz, file ime_datoteke, line br_linije
gdje je ime datoteke ime datoteke u kojoj se naredba nalazi i br linije broj
linije u kojoj se naredba nalazi. Nakon toga assert zaustavlja izvrˇsavanje
programa. Na primjer,
#include <stdio.h>
#include <math.h>
#include <assert.h>
int f(int x)
{
assert(2*x-1 >= 0);
return sqrt(2*x-1);
}
int main(void)
{
int x=-1;
printf("x=%d\n",f(x));
return 0;
}
8.6. ASSERT 143
U ovom sluˇcaju program na jednom sustavu ispisuje sljede´ce:
Assertion failed: 2*x-1 > 0, file C:\prjC\test\testA\a1.cpp, line 6
ˇ
Zelimo li iskljuˇciti assert naredbe iz programa dovoljno je prije ukljuˇcivanja
datoteke zaglavlja <assert.h> definirati NDEBUG kao u ovom primjeru:
#include <stdio.h>
#define NDEBUG
#include <assert.h>
........
Svaki assert makro ´ce sada biti reduciran na nul-naredbu.
Poglavlje 9
Struktura programa
U programskom jeziku C sve varijable moramo deklarirati prije njihove
upotrebe. Deklaracija definira tri svojstva varijable: tip, doseg i vijek tra-
janja. Pojedini elementi deklaracije zadaju se eksplicitno, pomo´cu kljuˇcnih
rije´ci jezika, ili implicitno, poloˇzajem deklaracije u programu. Isto vrijedi i
za deklaracije funkcija.
Tip varijable uvijek se uvodi eksplicitno kljuˇcnim rijeˇcima int, float,
double i drugim. Doseg i trajanje varijable odredeni su poloˇzajem deklaracije
u programu, a mogu se modificirati kljuˇcnim rijeˇcima static i extern.
9.1 Doseg varijable
Doseg varijable je dio programa u kojem je njena deklaracija vidljiva i
u kojem se stoga varijabli moˇze pristupiti putem njenog imena. Dva su
osnovna tipa dosega: to moˇze biti blok ili datoteka. Varijable s dosegom bloka
nazivamo lokalnim varijablama, dok varijable s dosegom datoteke nazivamo
globalnim. Ako je izvorni kod razbijen u viˇse datoteka, onda su globalne
varijable, deklarirane extern, vidljive i izvan datoteke u kojoj su definirane.
9.1.1 Lokalne varijable
Blok naredbi ˇcini svaki niz naredbi omeden vitiˇcastim zagradama. Na
primjer, tijelo funkcije je jedan blok naredbi.
Programski jezik C dozvoljava da se u svakom bloku deklariraju varija-
ble. Takve varijable nazivamo lokalnim. Deklaracija lokalne varijable unutar
144
9.1. DOSEG VARIJABLE 145
nekog bloka mora prethoditi prvoj izvrˇsnoj naredbi u tom bloku (prema stan-
dardu C90). U primjeru
if(n>0) {
int i; /* deklaracija varijable */
for(i=0; i<n; ++i)
....
}
nova varijabla i definirana je u bloku if naredbe koji se izvrˇsava u sluˇcaju
istinitosti uvjeta n>0.
Varijabla definirana unutar nekog bloka vidljiva je samo unutar tog bloka.
Izvan bloka ne moˇze joj se pristupiti, tj. njeno ime izvan bloka nije defini-
rano.
ˇ
Stoviˇse, izvan bloka moˇze biti deklarirana varijabla istog imena. Ta je
varijabla tada unutar bloka nedostupna, jer ju skriva varijabla deklarirana u
bloku. Na primjer,
int x,y;
......
void f(double x) {
double y;
....
}
Formalni argument funkcije vidljiv je unutar funkcije i nije dohvatljiv izvan
nje. Doseg formalnog argumenta je dakle isti kao i doseg varijable defini-
rane na poˇcetku funkcije. Stoga u prethodnom primjeru formalni argument
x i double varijabla y, deklarirana u funkciji, skrivaju int varijable x i y,
deklarirane izvan funkcije. Njih unutar funkcije nije mogu´ce dosegnuti. Na-
kon izlaza iz funkcije, cjelobrojne varijable x i y su ponovo vidljive i imaju
nepromijenjene vrijednosti, dok double varijable x i y viˇse ne postoje.
Ukratko: Doseg varijable definirane unutar nekog bloka je taj blok. Doseg
formalnog argumenta je tijelo funkcije.
9.1.2 Globalne varijable
Varijabla deklarirana u vanjskom bloku vidljiva je u svakom unutarnjem
bloku, ako u njemu nije definirana varijabla istog imena. Ako je varijabla
definirana izvan svih blokova, onda je njen doseg ˇcitava datoteka u kojoj je
definirana. Takvu varijablu nazivamo globalnom. Na primjer,
#include <stdio.h>
int x=3;
146 POGLAVLJE 9. STRUKTURA PROGRAMA
void ispisi(void) {
int y=4;
printf("x=%d, y=%d\n",x,y);
}
int main(void){
ispisi();
return 0;
}
Varijabla x vidljiva je unutar funkcije ispisi(). S druge strane kˆ od
#include <stdio.h> /* pogresno */
void ispisi(void) {
int y=4;
printf("x=%d, y=%d\n",x,y);
}
int main(){
int x=3;
ispisi();
return 0;
}
nije ispravan jer varijabla x nije definirana u bloku koji sadrˇzi definiciju
funkcije ispisi().
Globalne varijable se definiraju izvan svih funkcija i njihova je svrha prije-
nos podataka izmedu funkcija. Svaka funkcija moˇze dose´ci globalnu varijablu
i promijeniti njenu vrijednost. Na taj naˇcni viˇse funkcija moˇze komunicirati
bez upotrebe formalnih argumenta. U sljede´cem primjeru tri funkcije rade
na istom polju znakova:
#include <stdio.h>
#include <ctype.h>
char string[64];
void ucitaj(void);
void malo_u_veliko(void);
void ispisi(void);
int main(void) {
ucitaj();
malo_u_veliko();
9.1. DOSEG VARIJABLE 147
ispisi();
return 0;
}
void ucitaj() {
fgets(string,sizeof(string),stdin);
}
void malo_u_veliko() {
int i;
for(i=0;string[i] !=’\0’;i++)
string[i]=toupper(string[i]);
}
void ispisi() {
printf("%s\n",string);
}
Uoˇcimo da sve funkcije rade s istom vanjskom varijablom string. (Funk-
cija fgets() uˇcitava string iz datoteke, ovdje standardnog ulaza; vidi sek-
ciju 13.3.2).
Naglasimo joˇs da je globalna varijabla vidljiva od mjesta svoje deklaracije
do kraja datoteke u kojoj se nalazi. Stoga globalne varijable deklariramo na
poˇcetku datoteke prije svih funkcija. U primjeru,
int a;
void f(int);
int main(void) {
....
}
int b;
void f(int i) {
.....
}
varijabla a je vidljiva i u funkciji main() i u funkciji f(), dok je varijabla b
vidljiva u funkciji f(), ali ne i u funkciji main()
Funkcije su po svojoj prirodi globalni objekti. Funkcija definirana na
bilo kojem mjestu moˇze se dohvatiti iz bilo kojeg dijela programa ako ima
prototip. Definicija funkcije unutar druge funkcije nije dozvoljena.
Zadatak. Kakav ´ce biti izlaz iz sljede´ceg programa?
#include <stdio.h>
int main(void) {
148 POGLAVLJE 9. STRUKTURA PROGRAMA
int x=30;
printf("x u vanjskom bloku = %d\n",x);
while(x++ < 33) {
int x =100;
++x;
printf("x u unutarnjem bloku = %d\n",x);
}
return 0;
}
Napomena. Funkcija koja zavisi od neke globalne varijable i vrijednosti
koju ´ce u nju postaviti neka druga funkcija nije samostalna cjelina i ne moˇze
se bez modifikacija koristiti u razliˇcitim programima. K tomu, ovisnost o
globalnoj varijabli moˇze biti slabo uoˇcljiva, ˇsto oteˇzava odrˇzavanje programa.
Dobar stil programiranja stoga nalaˇze ˇsto manju upotrebu globalnih varijabli.
9.1.3 Argumenti funkcijskog prototipa
Argumenti navedeni u funkcijskom prototipu imaju doseg koji ne seˇze da-
lje od prototipa. To znaˇci da imena navedena u prototipu nisu vaˇzna i mogu
se podudarati s imenima drugih varijabli u programu.
ˇ
Stoviˇse, prevodilac
nam dopuˇsta da ispustimo imena varijabli. Jedina iznimka je deklaracija
polja varijabilne dimenzije (sekcija 10.7).
9.1.4 Lokalne varijable i standard C99
Standard C99 je uveo izmjene u varijable blokovnog dosega koje Cˇcine kompatibilnijim
s C++om. Izmjene su sljede´ce:
• Varijabla moˇze biti deklarirana bilo gdje unutar bloka, a ne samo na njegovom
poˇcetku. Deklaracije i izvrˇsne naredbe mogu se sada slobodno ispreplitati.
• Proˇsiruje se pojam bloka kako bi obuhvatio petlje for, while i do while, te naredbu
if, i u sluˇcaju kada se ne koriste vitiˇcaste zagrade.
Ideja prvog pravila je omogu´citi deklaraciju varijable ˇsto bliˇze mjestu na kojem se
koristi. Time se postiˇze ve´ca ˇcitljivost kˆ oda i olakˇsava prevodiocu zadatak optimizacije.
Drugo pravilo omogu´cava definiciju varijable unutar for naredbe kao u sluˇcaju
for(int i=0; i<10; ++i)
printf("Prema standardu C99 i= %d\n",i);
printf("i=%d\n",i); // Greska, i nije definirano
Varijabla i definirana je unutar for petlje i njen doseg je for petlja. To znaˇci da ve´c u
sljede´coj naredbi i nije definirano.
9.2. VIJEK TRAJANJA VARIJABLE 149
9.1.5 Funkcijski doseg
Napomenimo joˇs da postoji i tzv. funkcijski doseg, ali se on primijenjuje
samo na labele u goto naredbi. Funkcijski doseg znaˇci da je goto labela
vidljiva u cijeloj funkciji, bez obzira na blok u kojem se pojavljuje. Kao ˇsto
je reˇseno u sekciji 6.8, pomo´ci goto naredbe ne moˇze se iza´ci iz funkcije.
9.2 Vijek trajanja varijable
Svakoj varijabli prevodilac pridruˇzuje odredeni memorijski prostor. Vijek
trajanja varijable je vrijeme za koje joj je pridruˇzena njena memorijska loka-
cija, dakle ukupno vrijeme njene egzistencije. Prema vijeku trajanja varijable
dijelimo na automatske i statiˇcke.
9.2.1 Automatske varijable
Svaka varijabla kreirana unutar nekog bloka (dakle unutar neke funk-
cije), koja nije deklarirana s kljuˇcnom rijeˇci static, je automatska varijable.
Automatske varijable se kreiraju na ulasku u blok u kome su deklarirane i
uniˇstavaju na izlasku iz bloka. Memorija koju je automatska varijabla zauzi-
mala oslobada se za druge varijable. Na primjer,
......
void f(double x) {
double y=2.71;
static double z;
....
}
varijable x i y su automatske dok z nije, jer je deklarirana s kljuˇcnom rijeˇci
static.
Automatske varijable mogu se inicijalizirati, kao ˇsto je to sluˇcaj s vari-
jablom y. Inicijalizacija se vrˇsi pri svakom novom ulazu u blok u kome je
varijabla definirana. Tako ´ce varijabla y biti ponovo kreirana i inicijalizirana
pri svakom novom pozivu funkcije f().
Automatska varijabla koja nije inicijalizirana na neki naˇcin, na ulasku
u blok u kome je definirana dobiva nepredvidivu vrijednost. Najˇceˇs´ce je to
vrijednost koja se zatekla na pridruˇzenoj memorijskoj lokaciji.
Osim konstantnim izrazom, inicijalizaciju automatsje varijable mogu´ce je
izvrˇsiti i izrazom koji nije konstantan kao u ovom sluˇcaju:
150 POGLAVLJE 9. STRUKTURA PROGRAMA
void f(double x, int n) {
double y=n*x;
....
}
Inicijalizacija varijable y ovdje je samo pokrata za eksplicitno pridruˇzivanje
y=n*x;.
9.2.2 Identifikatori memorijske klase
Identifikatori memorijske klase su auto, extern, static i register. Oni
sluˇze preciziranju vijeka trajanja varijable. Postavljaju se u dekelaraciji va-
rijable prije identifikatora tipa varijable, tako da je op´ci oblik deklaracije
varijable:
identifikator_mem_klase tip_varijable ime_varijable;
Na primjer,
extern double l;
static char polje[10];
auto int *pi;
register int z;
9.2.3 auto
Identifikator memorijske klase auto deklarira automatsku varijablu. Vrlo
se rijetko susre´ce u programima jer za njegovu upotrebu nema drugog raz-
loga osim iskazivanja namjere programera. Naime, sve varijable definirane
unutar nekog bloka, a bez kljuˇcne rijeˇci static, su automatske varijable. Sve
varijable definirane izvan svih blokova su statiˇcke varijable,
9.2.4 register
Identifikator memorijske klase register moˇze se primijeniti samo na
automatske varijable i formalne argumente funkcije. Na primjer
f(register int m, register long n)
{
register int i;
9.2. VIJEK TRAJANJA VARIJABLE 151
........
}
Kljuˇcna rijeˇc register sugerira prevodiocu da ´ce varijabla biti ˇcesto koriˇstena
i da bi trebala biti alocirana tako da se smanji vrijeme pristupa. To najˇceˇs´ce
znaˇci smjestiti varijablu u registar mikroprocesora. Prevodilac nije duˇzan
poˇstovati deklaraciju register, tako da je ona samo sugestija prevodiocu.
Na varijablu tipa register ne moˇze se primijeniti adresni operator.
9.2.5 Statiˇcke varijable
Statiˇcke varijable alociraju se i inicijaliziraju na poˇcetku izvrˇsavanja pro-
grama, a uniˇstavaju se tek na zavrˇsetku programa. Vijek trajanja statiˇcke
varijable je cijelo vrijeme izvrˇsavanja programa. Prostor za statiˇcke vari-
jable alocira se u dijelu memorije razliˇcitom od dijela u kojem se alociraju
automatske varijable (ˇsto je standardno programski stog).
Svaka varijabla definirana izvan svih funkcija je statiˇcka. Varijabla dek-
larirana u nekom bloku (npr. funkciji) s identifikatorom memorijske klase
static je takoder statiˇcka varijabla.
Ukoliko statiˇcka varijabla nije inicijalizirana eksplicitno prevodilac ´ce je
inicijalizirati nulom. Statiˇcke je varijable mogu´ce inicijalizirati samo kons-
tantnim izrazima tako da sljede´ci kˆ od nije ispravan:
int f(int j)
{
static int i=j; // neispravno
........
}
9.2.6 Statiˇcke lokalne varijable
Statiˇcka lokalna varijabla je lokalna varijabla deklarirana s identifikato-
rom memorijske klase static. Ona postoji za cijelo vrijeme izvrˇsavanja
programa ali se moˇze dohvatiti samo iz bloka u kojem je definirana. K tome
vrijedi i sljede´ce pravilo: Statiˇcka varijabla definirana unutar nekog bloka ini-
cijalizira se samo jednom i to pri prvom ulazu u blok. Pogledajmo kako se to
svojstvo koristi u jednom primjeru.
ˇ
Zelimo napisati program koji ispisuje prvih 20 Fibonaccijevih brojeva. To
su brojevi definirani rekurzijom
F
i
= F
i−1
+ F
i−2
, (i = 3, 4, . . .) F
1
= F
2
= 1.
152 POGLAVLJE 9. STRUKTURA PROGRAMA
Glavni program imat ´ce sljede´ci oblik:
#include <stdio.h>
long fibonacci(int);
int main(void) {
int i;
for(i=1;i<=20;i++) printf("\n i= %d, F= %ld",i,fibonacci(i));
return 0;
}
Dakle, treba nam funkcija koja ´ce za svaki i izraˇcunati F
i
uz uvjet da se
brojevi raˇcunaju redom od F
1
do F
20
. Tu ´ce nam pomo´ci statiˇcke varijable:
long fibonacci(int i)
{
static long f1=1, f2=1;
long f;
f=(i<3) ? 1 : f1+f2;
f2=f1;
f1=f;
return f;
}
Statiˇcke varijable f1 i f2 bit ´ce inicijalizirane jedinicama samo pri prvom
pozivu funkcije fibonacci. Izmedu svaka dva poziva funkciji fibonacci
one zadrˇzavaju svoju vrijednost i stoga pri i-tom pozivu funkcije imamo
f1= F
i−1
i f2= F
i−2
.
Upozorenje. Kljuˇcna rijeˇc static ispred varijable definirane izvan svih
blokova ne oznaˇcava statiˇcku varijablu ve´c reducira njen doseg (vidi sljede´cu
sekciju).
9.3 Vanjski simboli
C program moˇze biti smjeˇsten u viˇse datoteka. Na primjer, svaka funkcija
definirana u programu moˇze biti smjeˇstena u zasebnu .c datoteku. Takvo
razbijanje izvornog kˆ oda ve´cih programa olakˇsava njihovo odrˇzavanje i na-
dogradnju. Pri tome je nuˇzno da se funkcije i globalne varijable definirane u
jednoj datoteci mogu koristiti i u svim ostalima.
Proces prevodenja izvornog kˆ oda smjeˇstenog u viˇse datoteka ima dvije
faze. U prvoj prevodilac prevodi izvorni kˆ od svake pojedine datoteke u objek-
tni kˆ od (datoteku s ekstenzijom .o); u drugoj linker povezuje viˇse datoteka
9.3. VANJSKI SIMBOLI 153
s objektnim kodom u izvrˇsni program. Zadatak je linkera da pronade one
simbole (imena funkcija i globalnih varijabli) koji se u pojedinoj objektnoj
datoteci koriste, ali nisu u njoj definirani. Svaki takav simbol mora imati
jednu i samo jednu definiciju u nekoj od datoteka, s kojom linker onda po-
vezuje simbol.
Na primjer, funkcija moˇze biti definirana u jednoj datoteci, a pozivati se
u viˇse drugih. U svakoj takvoj datoteci navodi se samo prototip funkcije.
Linker ´ce povezati simbol naveden u prototipu s definicijom funkcije. Pri
tome svi prototipovi (deklaracije) moraju odgovarati definiciji, i definicija
treba biti samo jedna. Analogno je s vanjskim varijablama.
Osnovno je pravilo da su imena svih vanjskih varijabli i funkcija dostupna
linkeru; sva su ta imena vanjski simboli. C nam dozvoljava da neka imena
ne eksportiramo linkeru, kako bi se smanjio broj vanjskih simbola (vidi sek-
ciju 9.3.3). To se ˇcini pomo´cu kljuˇcne rijeˇci static.
9.3.1 Funkcije
Vidjeli smo ve´c da funkcije uvijek imaju doseg datoteke, tj. funkcija de-
finirana u datoteci moˇze se pozvati bilo gdje u toj datoteci ako je na mjestu
poziva vidljiva njena deklaracija.
ˇ
Stoviˇse, ime funkcije je automatski vidljivo
linkeru, ˇsto znaˇci da se funkcija moˇze pozivati i u drugim datotekama.
Na primjer, pretpostavimo da u prvoj datoteci imamo kˆ od:
#include <stdio.h> /****** Datoteka 1 ******/
int g(int);
void f(int i) {
printf("i=%d\n",g(i));
}
int g(int i) {
return 2*i-1;
}
i da se funkcija main(), koja poziva funkciju f(), nalazi u drugoj datoteci.
Tada je u toj datoteci funkciju f() potrebno deklarirati, odnosno navesti
njen prototip:
extern void f(int); /****** Datoteka 2 *******/
int main(void) {
f(3);
}
154 POGLAVLJE 9. STRUKTURA PROGRAMA
Kljuˇcna rijeˇc extern je identifikator memorijske klase koji oznaˇcava da dek-
larirano ime (ovdje f) vanjski simbol, tj. da je poznato linkeru. Budu´ci da
su sva imena funkcija automatski poznata linkeru, extern moˇzemo ispustiti.
To znaˇci da je deklaracija
extern void f(int);
ekvivalentan s
void f(int);
Kljuˇcna rijeˇc extern kod funkcija ima stoga samo ulogu dokumentiranja
programa. Stavit ´cemo ju uvijek u prototipu funkcije koja je definirana u
nekoj drugoj datoteci, kako bi oznaˇcili da se radi o vanjskoj funkciji.
Funkcija moˇze biti deklarirana i s identifikatorm memorijske klase static.
Efekt takve deklaracije je da ime funkcije ne´ce biti eksportirano linkeru.
Takvu funkciju nije mogu´ce pozivati iz druge datoteke. Na primjer, ako bi-
smo ˇzeljeli onemoguˇciti koriˇstenje funkcije g izvan prve datoteke, modificirali
bismo prvu datoteku na sljede´ci naˇcin:
#include <stdio.h> /****** Datoteka 1 *******/
static int g(int);
void f(int i)
{
printf("i=%d\n",g(i));
}
static int g(int i) {
return 2*i-1;
}
Sada funkciju g viˇse ne moˇzemo dohvatiti iz druge datoteke, pa je sljede´ci
program neispravan:
extern void f(int); /****** Datoteka 2 *******/
extern int g(int); /* pogresno */
int main(void) {
f(3); /* Ispravno */
printf("g(2)=%d\n",g(2)); /* Neispravno */
}
jer linker ne bi pronaˇsao funkciju g.
Funkcije koje ne koristimo izvan datoteke u kojoj su definirane treba u
principu deklarirati kao statiˇcke kako njihova imena ne bi bila poznata u
ˇcitavom programu.
9.3. VANJSKI SIMBOLI 155
Uobiˇcajena je praksa deklaracije svih funkcija koje su vanjski simboli
staviti u datoteku zaglavlja koja se onda ukljuˇcuje u svaku datoteku koja te
funkcije ˇzeli koristiti, ˇcak i u onu u kojoj su funkcije definirane. Na taj se
naˇcin izbjegava mogu´ce nepodudaranje prototipova i definicija. Ukljuˇcivanje
zaglavlja u datoteku u kojoj su funkcije definirane omogu´cava prevodiocu da
provjeri jesu li prototipovi u skladu s definicijama.
To je postupak koji se koristi s funkcijama iz standardne biblioteke. Ako
na primjer, ˇzelimo koristiti funkciju printf(), koja je definirana u standard-
noj biblioteci, onda moramo ukljuˇciti datoteku <stdio.h> koja sadrˇzi njen
prototip.
9.3.2 Globalne varijable
Svaka globalna varijabla, jednako kao i svaka funkcija, je automatski vanj-
ski simbol; njeno ime je vidljivo linkeru. Takvo ponaˇsanje moˇzemo promije-
niti pomo´cu identifikatora memorijske klase static, kao i kod funkcije. U
primjeru,
static int d;
int main(void)
{
........
}
Globalna varijabla d vidljiva je samo u svojoj datoteci (nije vanjska varijabla).
Uoˇcimo da je kljuˇcna rijeˇc static ima drugaˇcije znaˇcenje kad se primjeni
na lokalnu varijablu, odnosno na globalnu varijablu. Kod lokalne varijable
static mijenja vijek trajanja varijable, a kod globalne reducira doseg.
Kada se jedna globalna varijabla koristi u viˇse datoteka, onda ona mora
biti definirana u jednoj od njih, a deklarirana u svim ostalima. Posve isto
pravilo vrijedi i za funkcije.
Kod funkcija je to pravilo lako primijeniti jer se definicija i deklaracija
funkcije jasno razlikuju. Definicija funkcije ima tijelo funkcije, dok deklara-
cija (prototip) nema. Kod varijabli razlika nije tako oˇcita.
Prevodilac mora jasno razlikovati definiciju od deklaracije varijable. Kod
definicije se za varijablu rezervira memorijski prostor, dok se kod deklaracije
samo uvodi ime varijable, i smatra se da definicija dana negdje drugdje.
Stoga je jasno da mora postojati toˇcno jedna definicija. Svaka deklaracija
varijable u kojoj se varijabla i inicijalizira nuˇzno je njena definicija. No, ako
inicijalizacija nije prisutna, onda nije jasno koja je od viˇse deklaracija iste
varijable u viˇse datoteka njena definicija.
156 POGLAVLJE 9. STRUKTURA PROGRAMA
Na ˇzalost, razliˇciti prevodioci koriste razliˇcite naˇcine za razlikovanje defi-
nicije i deklaracije. Da bi se izbjegli problemi treba usvojiti sljede´ci model.
• Definicija varijable ima eksplicitnu inicijalizaciju i identifikator memo-
rijske klase extern nije prisutan. Na primjer,
double a =0.0;
int z[3]={0,0,0};
• Sve deklaracije sadrˇze identifikator memorijske klase extern i ne sadrˇze
inicijalizaciju:
extern double a;
extern int z[];
Kljuˇcna rijeˇc extern ovdje indicira da se radi o vanjskoj varijabli, definiranoj
u nekoj drugoj datoteci. Njena uloga ovdje nije iskljuˇcivo dokumentiranje
koda.
9.3.3 Vanjska imena
Standard C99 propisuje da prevodilac treba prepoznati prva 63 znaka sva-
kog lokalnog identifikatora (imena funkcije, varijable,...) i prvih 31 znakova
vanjskih identifikatora. Standard C90 propisuje 31 znak lokalnog identifika-
tora i samo 6 znakova vanjskog identifikatora. Imena vanjskih varijabli treba
stoga drˇzati dovoljno kratkim.
Poglavlje 10
Polja
Polje je niz varijabli istog tipa koje su numerirane i mogu se dohvatiti
pomo´cu cjelobrojnog indeksa. Na primjer,
double x[3];
je deklaracija polja od tri varijable tipa double koje ˇcine polje x. Prva vari-
jabla je x[0], druga je x[1] i tre´ca je x[2]; u C-u su polja uvijek indeksirana
poˇcevˇsi od nule.
Radi efikasnosti pristupa elementi polja smjeˇstaju se na uzastopne me-
morijske lokacije. Stoga je polje element jezika C pomo´cu kojeg se realiziraju
vektori i matrice.
10.1 Definicija i inicijalizacija polja
Polje se definira jednako kao i skalarna varijabla s tom razlikom da di-
menzija polja (broj elemenata) mora biti zadana. Dimenzija se zadaje kao
pozitivni cjelobrojni izraz u uglatim zagradama.
Jednodimenzionalno polje definira se na sljede´ci naˇcin:
mem_klasa tip ime[izraz];
gdje je mem klasa memorijska klasa, tip je tip podatka, ime je ime polja, a
izraz mora biti konstantan cjelobrojni pozitivni izraz.
Deklaracija memorijske klase nije obavezna. Unutar funkcije polje dek-
larirano bez memorijske klase je automatska varijabla, a izvan svih funkcija
je statiˇcka varijabla. Unutar funkcije polje se moˇze uˇciniti statiˇckim pomo´cu
identifikatora memorijske klase static.
izraz u definiciji polja je najˇceˇs´ce pozitivna konstanta ili simboliˇcka kons-
tanta.
Polje definirano naredbom
157
158 POGLAVLJE 10. POLJA
float v[3];
je polje od tri elementa v[0], v[1], v[2]. Uoˇcimo da prvi element uvijek
ima indeks 0, drugi indeks 1 itd.
Polja se mogu inicijalizirati navodenjem vrijednosti elemenata unutar
vitiˇcastih zagrada. Sintaksa je sljede´ca:
mem_klasa tip ime[izraz]={v_1,v_2,...,v_n};
gdje je v 1 vrijednost koja ´ce biti pridruˇzena prvom elemetu polja (ime[0]),
v 2 vrijednost pridruˇzena drugom (ime[1]) itd. Na primjer,
float v[3]={1.17,2.43,6.11};
daje pridruˇzivanje v[0]=1.17, v[1]=2.43, v[2]=6.11. Prilikom inicija-
lizacije polja dimenzija ne mora biti specificirana ve´c ´ce biti automatski
izraˇcunata. Stoga moˇzemo pisati
float v[]={1.17,2.43,6.11};
i prevodilac ´ce kreirati polje dimenzije 3.
Ako je broj inicijalizacijskih vrijednosti ve´ci od dimenzije polja javlja se
greˇska. Ako je manji, onda ´ce preostale vrijednosti biti inicijalizirane nulom.
Sljede´ci program za dano polje x[dim] raˇcuna
a =
1
dim
dim−1

i=0
x
i
, b =
1
dim
¸
¸
¸
_
dim−1

i=0
(x
i
−a)
2
.
Polje x je definirano kao globalna varijabla.
#include <stdio.h>
#include <math.h>
float x[]={1.3, 2.4, 5.6, 6.7, 7.8};
int main(void) {
int i,dim=sizeof(x)/sizeof(float);
double a=0.0,b=0.0;
for(i=0;i<dim;++i) a+=x[i];
a/=dim;
for(i=0;i<dim;++i) b+=(x[i]-a)*(x[i]-a);
b=sqrt(b)/dim;
printf("Srednja vrijednost = %f, odstupanje = %f\n", a,b);
return 0;
}
10.1. DEFINICIJA I INICIJALIZACIJA POLJA 159
Dimenziju polja dobivamo naredbom
dim=sizeof(x)/sizeof(float);
Operator sizeof primijenjen na varijablu daje broj okteta potreban da za
memoriranje varijable. Ako je varijabla polje, onda sizeof daje broj okteta
potreban da se zapamti cijelo polje, pa smo stoga broj elemenata polja dobili
dijeljenjem s brojem okteta koji zauzima jedna varijabla tipa float.
Naˇzalost, ovakav naˇcin raˇcunanja broja elemenata polja nije mogu´ce promijeniti na
argument funkcije tipa polja, budu´ci da se on automatski konvertira u pokazivaˇc.
Polje x je tipa float dok su varijable a i b u kojima se vrˇsi sumacija
tipa double. Stoga u naredbi a+=x[i] i b+=(x[i]-a)*(x[i]-a) imamo
konverziju iz uˇzeg tipa u ˇsiri tip podatka. Sumiranje u varijabli ˇsireg tipa
korisno je ako se sumira veliki broj ˇclanova jer se na taj naˇcin smanjuju
greˇske zaokruˇzivanja.
Obrnuta konverzija, iz ˇsireg u uˇzi tip se deˇsava pri inicijalizaciji polja x jer
su sve konstante tipa double, na ˇsto ´ce nas prevoditelj eventualno upozoriti.
Da bi smo izbjegli upozorenje mogli smo koristiti konstante tipa float:
float x[]={1.3f, 2.4f, 5.6f, 6.7f, 7.8f};
U raˇcunu srednjeg odstupanja koristimo funkciju sqrt (drugi korijen) iz
standardne biblioteke. Kao i sve matematiˇcke funkcije koje rade s realnim
brojevima i sqrt uzima argument tipa double i vra´ca vrijednost tipa double.
Verzija funkcije sqrt koja uzima float i vra´ca float naziva se sqrtf. Nazivi
drugih float funkcija dobivaju se na isti naˇcin.
Standard C99 omogu´cava parcijanu inicijalizaciju polja. Sintaksa je kao u sljede´cem
primjeru
float x[5]={[3]=1.3f};
Ovdje se tre´ci element inicijalizira s 1.3. Neinicijalizirani elementi polja dobit ´ce vrijednost
nula, kao i kod obiˇcne inicijalizacije.
Napomena. U definiciji polja dimenzije polja moraju biti konstantni
izrazi. Zbog toga C ne dozvoljava deklaraciju polja unutar funkcije na sljede´ci
naˇcin:
void f(int n)
{
double A[n][n]; // pogresno
........
}
U funkciji f() smo pokuˇsali deklarirati matricu ˇciji je red zadan argumentom
n. Zbog gornjeg pravila, to je nemogu´ce. To ograniˇcenje ANSI C-a (C90) uk-
lonio je standard C99 uvodenjem polja varijabilne duljine (vidi sekciju 10.7).

160 POGLAVLJE 10. POLJA
10.2 Polja znakova
Polja znakova mogu se inicijalizirati stringovima. Na primjer, deklaraci-
jom
char c[]="tri";
definirano je polje od 4 znaka: c[0]=’t’, c[1]=’r’, c[2]=’i’, c[3]=’\0’.
Takav naˇcin pridruˇzivanja mogu´c je samo pri definiciji varijable. Nije dozvo-
ljeno pisati
c="tri"; /* pogresno */
ve´c trebamo koristiti funkciju strcpy deklariranu u <string.h>.
Sljede´ci program uˇcitava ime i prezime i ispisuje ih u jednom retku:
#include <stdio.h>
#include <string.h>
char ime [128];
char prezime [128];
char ime_i_prezime[128];
int main(void)
{
printf("Unesite ime:");
gets(ime);
printf("Unesite prezime:");
gets(prezime);
strcpy(ime_i_prezime,ime);
strcat(ime_i_prezime," ");
strcat(ime_i_prezime,prezime);
printf("Ime i prezime: %s\n", ime_i_prezime);
return 0;
}
Definirali smo tri globalna polja znakova, svako od po 128 znakova. Nizove
znakova unosimo pomo´cu gets naredbe. Ona automatski zamijenju znak za
prijelaz u novi red s nul znakom. U naredbi
strcpy(ime_i_prezime,ime);
funkcija strcpy kopira string ime u string ime i prezime. Pri tome se kopira
i nul znak kojim string zavrˇsava. Funkcija vra´ca pokazivaˇc na prvi string, no
u programu tu vrijednost zanemarujemo.
U naredbi
10.3. FUNKCIJE ZA RAD SA STRINGOVIMA 161
strcat(ime_i_prezime," ");
koristimo funkciju strcat koja povezuje dva stringa. U ovom sluˇcaju re-
zultat je novi string koji sadrˇzi vrijednost varijable ime i jedan razmak iza
zadnjeg slova. Dobiveni niz znakova opet zavrˇsava nul znakom. I ova funkcija
vra´ca pokazivaˇc na prvi string no u programu ga zanemarujemo. Ponovnom
upotrebom funkcije strcat dobivamo konaˇcan string koji zatim ispisujemo.
10.3 Funkcije za rad sa stringovima
Datoteka zaglavlja <string.h> deklarira niz funkcija za rad sa stringo-
vima. Najˇceˇs´ce upotrebljavane su
strlen(), strcat(), strncat(), strcmp(), strncmp(),
strcpy(), strncpy(). strchr(), strstr().
Funkciju strlen() smo ve´c sreli. Ona vra´ca duljinu stringa, tj. broj znakova
u stringu, bez zadnjeg null znaka. Prototip funkcije je
size_t strlen(const char *s);
Cjelobrojni tip size t je definiran u <stdio.h> npr. kao
typedef unsigned long size_t;
Ta je definicija ovisna o raˇcunalu pa je stoga stavljena u standardnu datoteku
zaglavlja. Funkcija strlen vra´ca vrijednost za jedan manju od operatora
sizeof.
Funkcija strcat povezuje dva stringa u jedan. Njen prototip je
char *strcat(char *s1, const char *s2);
String s2 se nadovezuje (eng. concatenation) na string s1 koji se pri tome
pove´cava dok se s2 ne mijenja. Prvi znak u polju znakova na koje pokazuje
s2 bit ´ce prepisan preko null-znaka kojim zavrˇsava s1. Ova funkcija ne vodi
raˇcuna o tome da li u polju na koje pokazuje s1 ima dovoljno mjesta za
oba stringa. Ukoliko nema do´ci ´ce do pisanja preko susjednih memorijskih
lokacija.
Funkcija strncat uzima tre´ci argument koji ima znaˇcenje maksimalnog
broja znakova koji ´ce biti dodani stringu s1. Ona nam omogu´cava pisanje
pouzdanijih programa. Prototip funkcije je
char *strncat(char *s1, const char *s2, size_t n);
162 POGLAVLJE 10. POLJA
Ako u prvih n znakova na koje pokazuje s2 nema nul-znaka, onda strncat
dodaje n znakva iz s2 na kraj niza s1 te dodaje nul-znak; dakle, ukupno se
dodaje n+1 znakova.
Sljede´ci program ilustrira upotrebu funkcije strncat. Ako cijeli ime i
prezime ne stane u polje ime i prezime program ´ce naprosto staviti ono ˇsto
stane (kvalitetnije rjeˇsenje bi bilo pomo´cu dinamiˇcki alocirane memorije).
#include <stdio.h>
#include <string.h>
#define SIZE 30
char ime_i_prezime[SIZE];
char prezime[SIZE];
int main(void){
int n;
puts("Vase ime: ");
gets(ime_i_prezime);
if(strlen(ime_i_prezime) < SIZE-2)
strcat(ime_i_prezime," ");
puts("Vase prezime: ");
gets(prezime);
n=SIZE-strlen(ime_i_prezime)-1;
strncat(ime_i_prezime,prezime,n);
puts(ime_i_prezime);
return 0;
}
Pri spajanju nizova ime i prezime i prezime moramo imati
strlen(ime_i_prezime) + strlen(prezime) + 1 <= SIZE.
Za usporedivanje stringova imamo dvije funkcije
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
Usporedivanje se vrˇsi znak po znak. Ukoliko su dva stringa jednaka funk-
cija vra´ca nulu. Ako prvi string leksikografski prethodi drugom funkcija
´ce vratiti negativnu vrijednost, a u suprotnom pozitivnu. Sljede´ci program
ilustrira leksikografski poredak stringova.
10.3. FUNKCIJE ZA RAD SA STRINGOVIMA 163
#include <stdio.h>
#include <string.h>
int main(void){
printf("%d\n", strcmp("A","A"));
printf("%d\n", strcmp("A","B"));
printf("%d\n", strcmp("B","A"));
printf("%d\n", strcmp("C","A"));
printf("%d\n", strcmp("a","A"));
printf("%d\n", strcmp("aaac","aaab"));
return 0;
}
Program ´ce redom ispisati vrijednosti 0, -1, 1, 2, 32 i 1.
Funkcija strncmp ima dodatni argument n koji predstavlja broj znakova
koji ´ce biti usporedeni. Funkcija ´ce redom usporedivati znakove u dva stringe
sve dok ne usporedi n parova ili ne naide do kraja jednog od stringova.
Funkcija strcmp uvijek usporeduje parove znakova sve do kraja jednog od
dva stringa. Nova funkcija omogu´cava ve´cu efikasnost usporedivanja jer ako,
na primjer, provjeravamo da li rijeˇc poˇcine s "astro", dovoljno je limitirati
usporedbu na prvih pet znakova.
Za kopiranje jednog znakovnog niza u drugi koristimo funkcije
char *strcpy(char *s1, const char *s2);
char *strncpy(char *s1, const char *s2, size_t n);
Obje funkcije kopiraju niz znakova na koji pokazuje s2 na lokaciju na koju
pokazuje s1. Druga verzija funkcije kopira najviˇse n znakova. Funkcija
strcpy kopira uvijek i nul-znak, dok strncpy ne´ce kopirati nul-znak ako je
n <= strlen(s2). U tom sluˇcaju treba naknadno dodati nul-znak. Obje
funkcije vra´caju s1.
Funkcija
char *strchr(const char *s, int c);
vra´ca pointer na prvo pojavljivanje znaka c u stringu na koji pokazuje s (nul-
znak se takoder moˇze traˇziti). Ako znak c nije prisutan u s bit ´ce vra´cen
nul-pointer.
Funkcija
char *strstr(const char *s1, const char *s2);
164 POGLAVLJE 10. POLJA
vra´ca pointer na prvo pojavljivanje stringa s2 u stringu na koji pokazuje s1.
Ako s2 nije prisutan u s1 bit ´ce vra´cen nul-pointer.
Datoteka zaglavlja <string.h> deklarira i mnoge druge funkcije za rad
sa stringovima. Za potpuniju informaciju vidi man-stranicu na raˇcunalu ili
[4], sekcija B3.
10.4 sscanf(), sprintf()
Ove dvije funkcije deklarirane su u zaglavlju <stdio.h>.
int sscanf(const char *s, const char *format, /* args */ ...);
int sprintf(char *s, const char *format, /* args*/ ...);
Za razliku od funkcija scanf i printf one kao prvi argument uzimaju po-
kazivaˇc na znakovni niz. Jedina razlika prema tim funkcijama je u tome ˇsto
se ˇcitanje i pisanje vrˇsi iz i u znakovni niz s, koji je dan kao prvi argument.
Tako funkcija sscanf ˇcita iz stringa s (umjesto s tastature) prema zadanom
formatu; funkcija sprintf piˇse u string s (umjesto na ekran) prema zadanom
formatu. Ove funkcije prvenstveno sluˇze za formatiranje stringova.
Ako smo, na primjer, pomo´cu kˆ oda
char line[256];
.........
.........
gets(line);
uˇcitali jedan string koji sadrˇzi dva broja tipa double i jedan tipa int, ona
moˇzemo pomo´cu sscanf izvrˇsiti daljnju konverziju
sscanf(line,"%lf %lf %d",&x,&y,&n);
gdje su x i y varijable tipa double, a n varijabla tipa int. Sliˇcno, ako
je realan broj x potrebno pretvoriti u string najlakˇse je to uˇciniti pomo´cu
funkcije sprintf:
char x_str[64];
float x;
.......
sprintf(x_str,"%12.7f",x);
10.5. POLJE KAO ARGUMENT FUNKCIJE 165
10.5 Polje kao argument funkcije
Polje moˇze biti formalni argument funkcije. U tom sluˇcaju argument se
ne prenosi po vrijednosti ve´c funkcija dobiva pokazivaˇc na prvi element polja.
Funkcija tada moˇze dohvatiti i promijeniti svaki elemet polja. U deklaraciji
(jednodimenzionalnog) polja kao formalnog argumenta dimenziju polja nije
potrebno navoditi. Na primjer, funkcija koja uzima polje realnih brojeva i
izraˇcunava srednju vrijednost moˇze biti napisana na sljede´ci naˇcin:
double srednja_vrijednost(int n, double v[]) {
int i;
double rez=0.0;
for(i=0;i<n;i++) rez+=v[i];
return rez/n;
}
Polje v je argument funkcije deklariran s double v[], bez navodenja dimen-
zije polja. Dimenzija je mogla biti navedena ali to nije nuˇzno. Broj elemenata
polja ˇciju srednju vrijednost treba izraˇcunati takoder je argument funkcije.
Unutar funkcije sa v[i] dohva´camo i-ti element polja v, a ne kopije polja,
jer je funkcija dobila pokazivaˇc na prvi element polja. Pri pozivu funkcije
koja ima polje kao formalni argument, stvarni argument je ime polja. Tako
na primjer,
int main(void) {
int n;
double v[]={1.0,2.0,3.0},sv;
n=3;
sv=srednja_vrijednost(n,v);
return 0;
}
Prevodilac ime polja v pri pozivu funkcije pretvara u pokazivaˇc na prvi ele-
ment polja. Time se dobiva na efikasnosti jer se izbjegava kopiranje (velikih)
polja.
Ako funkcija uzima polje kao formalni argument i ne mijenja njegove
elemente, onda polje treba deklarirati kao const polje. Na taj se naˇcin jasno
pokazuje da funkcija ne mijenja polje. Prevodilac ne´ce dozvoliti promjenu
polja unutar tijela funkcije, ˇstite´ci nas tako od vlastitih pogreˇsaka.
Naredba return ne moˇze biti iskoriˇstena za vra´canje polja u pozivni
program jer funkcija moˇze vratiti samo skalarnu vrijednost ili strukturu.
166 POGLAVLJE 10. POLJA
10.6 Viˇsedimenzionalna polja
Deklaracija viˇsedimenzionalnog polja ima oblik
mem_klasa tip ime[izraz_1][izraz_2]...[izraz_n];
gdje je mem klasa memorijska klasa, tip je tip podatka, ime je ime polja, a
izraz 1, . . . , izraz n su konstantni cjelobrojni pozitivni izrazi koji odreduju
broj elementa polja vezanih uz pojedine indekse. Tako se prvi indeks kre´ce
od 0 do izraz 1 - 1, drugi od 0 do izraz 2 - 1 itd.
Na primjer, polje m deklarirano sa
static float m[2][3];
predstavlja matricu s dva retka i tri stupca. Njene elemente moˇzemo pros-
torno zamisliti na sljede´ci naˇcin:
m[0][0] m[0][1] m[0][2]
m[1][0] m[1][1] m[1][2]
U prvom retku su elementi m[0][i] za i=0,1,2, a u drugom m[1][i] za
i = 0, 1, 2. Element na mjestu (i,j) matrice m je m[i][j].
Elementi viˇsedimenzionalnog polja pamte se u memoriji raˇcunala kao
jedno jednodimenzionalno polje. Pri tome su elementi poredani po recima ˇsto
znaˇci da se pri smjeˇstanju elemenata u memoriju najdesniji indeks najbrˇze
varira. Kod dvodimenzionalnog polja m poredak elemenata u memoriji bio bi
m[0][0] m[0][1] m[0][2] m[1][0] m[1][1] m[1][2]
Preciznije element m[i][j] biti ´ce na l-tom mjestu u memoriji, gdje je
l=i*MAXY+j,
a MAXY=3 je broj stupaca matrice. Poznavanje te ˇcinjenice vaˇzno je pri kons-
trukciji numeriˇckih algoritama koji rade s matricama te za razumijevanje
inicijalizacije polja. Na primjer, polje m moˇze biti inicijalizirano na sljede´ci
naˇcin:
static float m[2][3]={1.0,2.0,3.0,4.0,5.0,6.0};
Inicijalne vrijednosti ´ce biti pridruˇzene elementima matrice po recima:
m[0][0]=1.0, m[0][1]=2.0, m[0][2]=3.0,
m[1][0]=4.0, m[1][1]=5.0, m[1][2]=6.0.
10.6. VI
ˇ
SEDIMENZIONALNA POLJA 167
Kako je takav naˇcin inicijalizacije jako nepregledan, inicijalne se vrijednosti
mogu pomo´cu vitiˇcastih zagrada formirati u grupe koje se pridruˇzuju poje-
dinim recima. Tako moˇzemo pisati
static float m[2][3]={{1.0,2.0,3.0},
{4.0,5.0,6.0}
};
ˇsto daje istu inicijalizaciju polja m, ali je namjera programera puno jasnija.
Ukoliko neka od grupa ima manje elementa od dimenzije retka, ostali elemeti
´ce biti inicijalizirani nulama.
Viˇsedimenzionalna polja se definitaju rekurzivno:
• Viˇsedimenzionalno polje je jednodimenzionalno polje ˇciji su elementi
polja dimenzije manje za jedan.
Tako je npr. dvodimenzionalno polje deklarirano naredbom
float m[2][3];
jedno jednodimenzionalno polje dimenzije 2 ˇciji su elementi m[0] i m[1] tipa
float[3] (jednodimenzionalna polja dimenzija 3); to su reci matrice m. Tro-
dimenzionalno polje
float m[2][3][4];
je jednodimenzionalno polje dimenzije 2 ˇciji su elementi m[0] i m[1] dvodi-
menzionalna polja tipa float[3][4]. Elementi polja m bit ´ce stoga smjeˇsteni
u memoriju u redoslijedu m[0][][], m[1][][], odnosno prvo prva matrica
dimenzije 3×4, a zatim druga. Preciznije element m[i][j][k] bit ´ce smjeˇsten
na mjesto
i*MAXY*MAXZ+j*MAXZ+k,
gdje su MAXX=2, MAXY=3 i MAXZ=4 pojedine dimenzije polja.
Kada je viˇsedimenzionalno polje argument funkcije ono se moˇze dekla-
rirati sa svim svojim dimenzijama ili sa svim dimenzijama osim prve. Na
primjer, funkcija koja ˇcita matricu s MAXX redaka i MAXY stupaca moˇze biti
deklarirana kao
void readinput(int m[MAXX][MAXY], int n, int m)
gdje su n i m stvarni brojevi redaka i stupaca koje treba uˇcitati. Ta ista
funkcija moˇze biti deklarirana na sljede´ci naˇcin:
168 POGLAVLJE 10. POLJA
void readinput(int m[][MAXY], int n, int m)
Naime, broj redaka nije bitan za adresiranje elemenata matrice. Sve ˇsto
funkcija mora znati je da se element m[i][j] nalazi na l-tom mjestu, gdje
je l=i*MAXY+j. Stoga je samo MAXY nuˇzan pri pozivu funkcije.
Konaˇcno, budu´ci da je u deklaraciji funkcije polje isto ˇsto i pokazivaˇc na
prvi element polja (stoga ˇsto se pri pozivu funkcije vrˇsi implicitna konverzija
polja u pokazivaˇc) moˇzemo istu funkciju deklarirati na tre´ci naˇcin:
void readinput(int (*m)[MAXY], int n, int m)
Zagrade su nuˇzne da nebismo dobili “polje pokazivaˇca”.
Posve analogno, kod viˇsedimenzionalnih polja funkcija mora znati sve
dimenzije polja osim prve. Pri tome treba uoˇciti da dimenzije moraju biti
konstantni izrazi. To je bitno ograniˇcenje ANSI-C- jezika.
Sljede´ci program ilustrira inicijalizaciju trodimenzionalnog polja.
#include <stdio.h>
char A[][2][2]={ {{’a’,’b’},{’c’,’d’}},{{’e’,’f’},{’g’,’h’}}};
void f(char a[2][2]);
int main(void)
{
printf("Matrica A[0]:\n");
f(A[0]);
printf("Matrica A[1]:\n");
f(A[1]);
return 0;
}
void f(char a[2][2])
{
printf("%c %c\n",a[0][0],a[0][1]);
printf("%c %c\n",a[1][0],a[1][1]);
}
Uoˇcimo da prvu dimenziju u deklaraciji
char A[][2][2]={ {{’a’,’b’},{’c’,’d’}},{{’e’,’f’},{’g’,’h’}}};
prevodioc moˇze izraˇcunati, ali sve ostale dimenzije su nuˇzne.
10.6. VI
ˇ
SEDIMENZIONALNA POLJA 169
Budu´ci da je trodimenzionalno polje zapravo (jednodimenzionalno) po-
lje dvodimenzionalnih polja, to funkciji koja uzima dvodimenzionalno po-
lje (char a[2][2]) kao argument moˇzemo predati A[0] i A[1]. Rezultat
izvrˇsavanja programa bit ´ce:
Matrica A[0]:
a b
c d
Matrica A[1]:
e f
g h
Sljede´ci program mnoˇzi dvije matrice. Matrica A je dimenzije 2 × 2, a
matrica B ima dimenziju 2×3. Produkt je matrica C = AB dimenzije 2×3.
#include <stdio.h>
double A[2][2]={{1.0,2.0},{3.0,4.0}};
double B[2][3]={{0.0,1.0,0.0},{1.0,0.0,1.0}};
double C[2][3];
int main(void) {
int i,j,k;
for(i=0;i<2;++i)
for(j=0;j<3;++j)
for(k=0;k<2;++k)
C[i][j]+=A[i][k]*B[k][j];
printf("Matrica C:\n");
for(i=0;i<2;++i){
for(j=0;j<3;++j) printf("%f ",C[i][j]);
printf("\n");
}
return 0;
}
Mnoˇzenje matrica vrˇsi se u trostrukoj petlji. Budu´ci da poredak petlji moˇze
biti proizvoljan imamo 3! = 6 verzija tog algoritma. Uoˇcimo da smo u
programu iskoristili ˇcinjenicu da se statiˇcka varijabla (polje C) automatski
inicijalizirana nulama. Rezultat izvrˇsavanja programa je
Matrica C:
2.000000 1.000000 2.000000
4.000000 3.000000 4.000000
170 POGLAVLJE 10. POLJA
U unutarnjoj petlji redak matrica A mnoˇzi se sa stupcem matrice B. Ve´cina
raˇcunala danas ima hijerarhijski organiziranu memoriju u kojoj se bliske me-
morijske lokacije mogu dohvatiti brˇze od udaljenih.
1
Pri mnoˇzenju jednog
retka matrice A i jednog stupca matrice B treba dohvatiti elemente stupca
matrice B. Ali, uzastopni elementi jednog stupca nalaze se na memorijskim
lokacijama koje su medusobno udaljene za duljinu retka. Kod velikih ma-
trica ta je udaljenost velika, ˇsto rezultira sporijim kˆ odom. Stoga je efikasnija
verzija algoritma u kojoj je petlja po j unutarnja:
......
for(i=0;i<2;++i)
for(k=0;k<2;++k)
for(j=0;j<3;++j)
C[i][j]+=A[i][k]*B[k][j];
......
Sada se u unutarnjoj petlji radi s recima matrica C i B.
10.7 Polja varijabilne duljine
Polja varijabilne duljine (engl. variable-length arrays) uvedena su u jezik u standardu
C99 i mnogi prevodioci ih joˇs ne implementiraju u potpunosti.
Programski jezik C (C90) pokazuje ozbiljan nedostatak u radu s matricama. Da bismo
to ilustrirali pokuˇsajmo napisati funkciju koja raˇcuna Frobeniusovu normu matrice. Ako
je A = (a
i,j
)
n
i,j=1
matrica reda n, onda je njena Frobeniusovu norma
A =

n

i,j=1
a
2
i,j
.
Funkcija bi mogla izgledati ovako:
#include <math.h>
#include <assert.h>
#define M 9
double Fnorm(int n, double A[][M]) {
double norm=0.0;
int i,j;
1
Kada treba dohvatiti varijablu na nekoj memorijskoj lokaciji dohvati se ˇcitav blok
memorije i smjesti se u brˇzu memoriju, tzv. cache. Traˇzena varijabla se sada ˇcita iz
cachea. Ako je sljede´ca varijabla koju treba dohvatiti blizu prethodne, onda je ona ve´c u
cacheu i njen dohvat je vrlo brz. Ako pak nije, onda se mora dohvatiti novi blok memorije
i spremiti u cache, ˇsto predstavlja dodatni utroˇsak vremena.
10.7. POLJA VARIJABILNE DULJINE 171
assert(n < M);
for(i=0;i<n;++i)
for(j=0;j<n;++j)
norm += A[i][j]*A[i][j];
return sqrt(norm);
}
Prevodilac zahtijeva da su dimenzije matrice u deklaraciji argumenta funkcije konstantni
izrazi. To nas prisiljava da esencijalnu dimenziju, broj stupaca, predamo funkciji kao
simboliˇcku konstantu. Drugim rijeˇcima prevodilac nam ne´ce dozvoliti da funkciji Fnorm
umjesto konstante M damo varijablu koja sadrˇzi red matrice. Posljedica toga je da naˇsa
funkcija moˇze raditi samo s matricama reda 9, a da za matricu drukˇcije dimenzije moramo
promijeniti M i rekompilirati kˆ od.
Zbog tih razloga C99 standard uvodi novi element jezika, tzv. polje varijabilne duljine.
Polje varijabilne duljine moˇze biti jedino automatsko, te stoga mora biti definirano u nekoj
funkciji ili kao argument funkcije. Osnovna razlika prema standardnim poljima u C-u je
ˇsto dimenzije polja mogu biti zadane varijablama. Tako moˇzemo definirati
int n=3;
int m=3;
double A[n][m]; // PVD
Uoˇcimo da n i m moraju biti definirani prije A. Kao argument funkcije, polje varijabilne
duljine bilo bi deklarirano na sljede´ci naˇcin:
double Fnorm(int n, int m, double A[n][m]); // a je PVD
Deklaracija
double Fnorm(double A[n][m], int n, int m); // neispravno
je neispravna zbog pogreˇsnog poretka argumenata. Standard dozvoljava da se u prototipu
ne piˇsu imena argumenata. U polju varijabilne duljine ih tada treba zamijeniti zvjezdi-
cama:
double Fnorm(int, int, double A[*][*]); // a je PVD
Pogledajmo kako bi izgledao program koji raˇcuna Frobeniusovu normu matrice.
#include <stdio.h>
#include <math.h>
double Fnorm(int n, int m, double A[n][m]);
void vandermond(int n, int m, double A[n][m]);
void print(int n, int m, double A[n][m]);
int main(void)
{
int n=3,m=3;
172 POGLAVLJE 10. POLJA
double A[n][m];
vandermond(n,m,A);
print(n,m,A);
printf("norma matrice = %f\n", Fnorm(n,m,A));
return 0;
}
void vandermond(int n, int m, double A[n][m])
{
int i,j;
for(i=0;i<n;++i)
for(j=0;j<m;++j)
A[i][j] = 1.0/(2.0+i+j);
}
double Fnorm(int n, int m, double A[n][m])
{
double norm=0.0;
int i,j;
for(i=0;i<n;++i)
for(j=0;j<m;++j)
norm += A[i][j]*A[i][j];
return sqrt(norm);
}
void print(int n, int m, double A[n][m])
{
int i,j;
for(i=0;i<n;++i){
for(j=0;j<m;++j)
printf("%f ",A[i][j]);
printf("\n");
}
}
Izlaz iz programa bi bio
0.500000 0.333333 0.250000
0.333333 0.250000 0.200000
0.250000 0.200000 0.166667
norma matrice = 0.876071
Polje varijabilne duljine moˇze biti deklarirano samo unutar neke funkcije. Ono se alocira
na programskom stogu i zato ne moˇze biti deklarirano static. Ono k tome podlijeˇze
jednom broju ograniˇcenja u odnosu na obiˇcna polja, vidi [3],
Konaˇcno, spomenimo da sliˇcnu funkcionalnost osigurava u brojnim implementacijama
jezika nestandardna funkcija alloca().
Poglavlje 11
Pokazivaˇci
Svakoj varijabli u programu pridruˇzena je memorijska lokacija ˇcija veliˇcina
ovisi o tipu varijable. Za varijablu tipa int tipiˇcno se rezervira 16 ili 32
bita, za varijablu tipa double 64 bita itd. Program dohva´ca memorijsku
lokaciju na kojoj je varijabla pohranjena pomo´cu jedinstvene adrese koja
je toj lokaciji pridruˇzena. Pri manipulacijama s varijablom tu adresu ne
moramo eksplicitno poznavati nego u tu svrhu sluˇzi ime varijable.
Programski jezik C nudi mogu´cnost rada neposredno s adresama varijabli
putem pokazivaˇca (pointera). Pokazivaˇc na neki tip je varijabla koja sadrˇzi
adresu varijable danog tipa. Na primjer, pokazivaˇc na int je varijabla koja
sadrˇzi adresu varijable tipa int.
11.1 Deklaracija pokazivaˇca
Da bi se dohvatila adresa neke varijable koristi se adresni operator &.
Ako je v varijabla danog tipa, a pv pokazivaˇc na taj tip, onda je naredbom
pv=&v;
pokazivaˇcu pv pridruˇzena adresa varijable v. Pored adresnog operatora ko-
ristimo joˇs i operator dereferenciranja * koji vra´ca vrijednost spremljenu na
adresu na koju pokazivaˇc pokazuje. Tako, ako je pv=&v, onda je *pv isto ˇsto
i v.
Pokazivaˇc na neki tip deklarira se na sljede´ci naˇcin:
tip *ime;
gdje je ime pokazivaˇca, a tip je tip podatka na koji pokazuje. Zvijezdica
oznaˇcava da se radi o pokazivaˇcu, a ne o vrijabli tipa tip. Na primjer vari-
jable pi deklarirana naredbom
173
174 POGLAVLJE 11. POKAZIVA
ˇ
CI
int *pi;
je pokazivaˇc na int. Prilikom definicije varijable ona moˇze biti inicijalizirana
kao u ovom sluˇcaju
int i=5;
int *pi=&i;
Naravno, varijabla i ˇcija se adresa uzima mora biti definirana prije nego ˇsto
se na nju primjeni adresni operator.
0xBFFFF954
0xBFFFF958
0xBFFFF962
0xBFFFF966
pi=0xBFFFF966
i=5
Adresni operator moˇze se primijeniti na operande kojima je pridruˇzena je-
dinstvena adresa. Stoga ga ne moˇzemo primijeniti na npr. aritmetiˇcke izraze
i sliˇcno. Operator dereferenciranja djeluje samo na pokazivaˇcke varijable.
Adresni operator i operator dereferenciranja su unarni operatori i imaju
isti prioritet kao ostali unarni operatori. Njihov prioritet je ve´ci od prioriteta
aritmetiˇckih operatora tako da u aritmetiˇckim izrazima *pi nije potrebno
stavljati u zagradu. Npr. uz gornje deklaracije izraz
i=2*(*pi+6);
dat ´ce i=22 jer se *pi izraˇcunava i daje 5 prije aritmetiˇckih operacija.
Operator dereferenciranja moˇze se pojaviti na lijevoj strani jednakosti tj.
moˇzemo imati
*pi=6;
ˇsto je ekvivalentno s i=6. Adresni operator, s druge strane, ne moˇze se
pojaviti na lijevoj strani jednakosti.
Deklaracija int *pi indicira da je *pi objekt tipa int. Sintaksa dek-
laracije varijable imitira sintaksu izraza u kojem se ona pojavljuje. Isto se
odnosi i na deklaraciju funkcija. U primjeru
int *f(char *);
11.2. POKAZIVA
ˇ
CI I FUNKCIJE 175
f je funkcija koja uzima pokazivaˇc na char i vra´ca pokazivaˇc na int. Dek-
laracija sugerira da *f(s) mora biti tipa int (s je pokazivaˇc na char ili
jednostavno niz znakova).
Pokazivaˇc se u printf naredbi ispisuje s kontrolnim znakom %p:
#include <stdio.h>
int main(void) {
int i=5;
int *pi=&i;
printf("i= %d, adresa od i= %p\n",i,pi);
return 0;
}
11.2 Pokazivaˇci i funkcije
Pokazivaˇci mogu biti argumenti funkcije. U tom sluˇcaju funkcija moˇze
promijeniti vrijednost varijable na koju pokazivaˇc pokazuje.
Uzmimo da ˇzelimo napisati funkciju zamjena koja uzima dva cijelobrojna
argumenta x i y i zamijenjuje njihove vrijednosti: x preslikava u y, a y u x.
Funkciju bismo mogli ovako napisati:
void zamjena(int x, int y) { /* POGRESNO */
int temp=x;
x=y;
y=temp;
}
Ova funkcija ne daje traˇzeni rezultat zbog prijenosa argumenta po vrijed-
nosti. Pri pozivu funkcije zamjena(a,b) ona dobiva kopije stvarnih argu-
menata a i b koje medusobno zamijenjuje, ali to nema nikakvog utjecaja na
stvarne argumente. Stoga, funkcija treba uzeti pokazivaˇce na varijable ˇcije
vrijednosti treba zamijeniti. Poziv funkcije treba biti
zamjena(&a,&b);
a funkcija treba imati oblik
void zamjena(int *px, int *py) {
int temp=*px;
*px=*py;
*py=temp;
}
176 POGLAVLJE 11. POKAZIVA
ˇ
CI
Kada je polje argument funkcije, onda funkcija ne dobiva kopiju ˇcitavog
polja ve´c samo pokazivaˇc na prvi element polja. Pri pozivu fukciji se daje
samo ime polja (bez uglatih zagrada) jer ono predstavlja pokazivaˇc na prvi
element.
ˇ
Cinjenica da funkcija koja uzima polje kao argument oˇcekuje pokazivaˇc
na prvi element polja moˇze se iskoristiti da se funkciji dade samo dio polja.
Na primjer, funkcija f u programu
char z[100];
void f(char *);
.....
f(&z[50]);
dobit ´ce samo zadnjih 50 elemenata polja z.
Uoˇcimo da funkciju koja uzima kao argument jednodimenzionalno polje
moˇzemo deklarirati i kao funkciju koja uzima pokazivaˇc na tip polja, kao ˇsto
to pokazuje sljede´ci primjer:
#include <stdio.h>
int a[3]={1,2,3};
void f(int *);
void g(int []);
int main(void) {
f(a); g(a);
return 0;
}
void f(int *x) {
int j;
for(j=0;j<3;++j) printf("%d ",x[j]);
printf("\n");
}
void g(int x[]) {
int j;
for(j=0;j<3;++j) printf("%d ",x[j]);
printf("\n");
}
U oba sluˇcaja bit ´ce ispisano polje a.
Nadalje, funkcija moˇze vratitit pokazivaˇc kao u sljede´cem primjeru.
11.3. OPERACIJE NAD POKAZIVA
ˇ
CIMA 177
#include <stdio.h>
#include <stdlib.h>
char *tocka(char *niz)
{
char *p;
for(p=niz; *p !=’\0’; ++p)
if(*p == ’.’) return p;
return NULL;
}
int main(void) {
char *p="bigjob.com";
printf("Pokazivac na prvi znak=%p,\n"
"pokazivac na tocku= %p\n",p,tocka(p));
return 0;
}
Funkcija tocka vra´ca pokazivaˇc na char. Tu je vaˇzno primijetiti da funkcija
ne smije vratiti pokazivaˇc na lokalnu varijablu. Takav pokazivaˇc ne pokazuje
na korektnu memorijsku lokaciju jer se lokalna varijabla uniˇstava nakon iz-
laska iz funkcija. Iznimka tog pravila je statiˇcka lokalna varijabla. Naime,
statiˇcka varijabla postoji za cijelo vrijeme izvrˇsavanja programa i ako funk-
cija vrati pokazivaˇc na nju, ona se moˇze koristiti i izvan funkcije; vidi primjer
u sekciji 11.6.
11.3 Operacije nad pokazivaˇcima
11.3.1 Poveˇcavanje i smanjivanje
Aritmetiˇcke operacije dozvoljene nad pokazivaˇcima konzistentne su sa
svrhom pokazivaˇca da pokazuju na varijablu odredenog tipa. Ako je pi
pokazivaˇc tipa int, onda ´ce pi+1 biti pokazivaˇc na sljede´cu varijablu tipa
int u memoriji. To znaˇci da dodavanje jedinice pokazivaˇcu ne pove´cava
adresu koju on sadrˇzi za jedan, ve´c za onoliko koliko je potrebno da nova
vrijednost pokazuje na sljede´cu varijablu istog tipa u memoriji.
#include <stdio.h>
int main(void)
178 POGLAVLJE 11. POKAZIVA
ˇ
CI
{
float x[]={1.0,2.0,3.0},*px;
px=&x[0];
printf("Vrijednosti: x[0]=%g, x[1]=%g, x[2]=%g\n",
x[0],x[1],x[2]);
printf("Adrese : x[0]=%x, x[1]=%x, x[2]=%x\n",
px,px+1,px+2);
return 0;
}
U ovom primjeru vidimo da ´ce pokazivaˇc biti inkrementiran dovoljno da
pokaˇze na sljede´cu float vrijednost. Uoˇcimo da smo pokazivaˇce ispisali u
formatu %x kao heksadecimalni cijeli broj (usporedite s ispisom u formatu
%p).
Svakom pokazivaˇcu mogu´ce je dodati i oduzeti cijeli broj. Stoga ako je
px pokazivaˇc i n varijabla tipa int, onda su dozvoljene operacije
++px --px px+n px-n
Pokazivaˇc px+n pokazuje na n-ti objekt nakon onog na kog pokazuje px.
Unarni operatori & i * imaju viˇsi prioritet od aritmetiˇckih operatora i
operatora pridruˇzivanja. Stoga u izrazu
*px += 1;
dolazi do pove´canja za jedan vrijednosti na koju px pokazuje, a ne samog
pokazivaˇca. Isti izraz bismo mogli napisati kao
++*px;
stoga ˇsto je asocijativnost unarnih operatora zdesna na lijevo pa se prvo pri-
mijenjuje dereferenciranje, a zatim inkrementiranje. Iz tog razloga, ˇzelimo
li koristiti postfiks notaciju operatora inkrementiranja, moramo koristiti za-
grade:
(*px)++;
Izraz *px++ inkrementirao bi pokazivaˇc nakon ˇsto bi vratio vrijednost na koju
px pokazuje.
11.3. OPERACIJE NAD POKAZIVA
ˇ
CIMA 179
11.3.2 Pokazivaˇci i cijeli brojevi
Pokazivaˇcu nije mogu´ce pridruˇziti vrijednost cjelobrojnog tipa. Iznimku
jedino predstavlja nula. Naime, C garantira da nula nije legalna adresa i
omogu´cava da se nula pridruˇzi bilo kojoj pokazivaˇckoj varijabli s ciljem da
se signalizira kako varijabla ne sadrˇzi legalnu adresu. Legalno je pisati
double *p=0;
To je naroˇcito korisno kod automatskih varijabli koje pri pozivu funkcije
imaju nedefiniranu vrijednost.
ˇ
Cesto se koristi u ovu svrhu simboliˇcka kons-
tanta NULL
#define NULL 0
........
double *p=NULL;
(simboliˇcka konstanta NULL definirana je u <stdio.h>). Pokazivaˇce je osim
s drugim istovrsnim pokazivaˇcem mogu´ce usporedivati i s nulom, tako da je
mogu´ce pisati
if(px != 0) ...
Usporedivanje s drugim cijelim brojevima nije dozvoljeno:
if(px == 0xBFFFF986) ... // POGRESNO
11.3.3 Usporedivanje pokazivaˇca
Pokazivaˇce istog tipa moˇzemo medusobno usporedivati pomo´cu relacijskih
operatora. Takva operacija ima smisla ako pokazivaˇci pokazuju na isto polje.
Ako su px i py dva pokazivaˇca istog tipa, onda je mogu´ce koristiti izraze
px < py px > py px == py px != py
Rezultat tih operacija je 1 ili 0 ovisno o tome da li je reacija zadovoljena ili
ne.
11.3.4 Oduzimanje pokazivaˇca
Jedan pokazivaˇc moˇze se oduzeti od drugoga ukoliko oni pokazuju na isto
polje. Ako su px i py dva pokazivaˇca na isto polje te ako je py > px, tada je
py-px+1 broj elemenata izmedu px i py, ukljuˇcuju´ci krajeve. Uoˇcimo da je
py-px vrijednost cjelobrojnog tipa (a ne pokazivaˇckog). Na primjer, funkcija
koja daje broj znakova u stringu moˇze biti napisana na sljede´ci naˇcin:
180 POGLAVLJE 11. POKAZIVA
ˇ
CI
int strlen(char *s)
{
char *p=s;
while(*p != ’\0’) p++;
return p-s;
}
1 2 3 4 5 6 \0
s
p
p-s=6
Mogu´ce je da na nekim sustavima razlika dva pokazivaˇca ne stane nuˇzno
u varijablu tipa int. Stoga se u datoteci zaglavlja <stddef.h> definira cje-
lobrojni tip podatka ptrdiff t ˇcija je ˇsirina dovoljna da primi razliku bilo
koja dva pokazivaˇca.
Gore navedene operacije su jedine koje su dozvoljene s pokazivaˇcima.
Navedimo ih sve ponovo.
• Pokazivaˇcu moˇze biti pridruˇzena adresa (npr. px=&x);
• Pokazivaˇcu moˇze biti pridruˇzen pokazivaˇc istog tipa (npr. px=py);
• Pokazivaˇcu moˇze biti pridruˇzena nula (npr. px=0 ili px=NULL);
• Pokazivaˇcu moˇze biti dodana ili oduzeta cjelobrojna varijabla (npr.
px+3, ++px, --px itd.);
• Dva pokazivaˇca mogu biti oduzeta ukoliko pokazuju na isto polje;
• Dva pokazivaˇca mogu biti povezana relacijskim operatorom ako poka-
zuju na isto polje.
11.3.5 Primjer
Kao primjer napiˇsimo verziju funkcije strcpy iz standardne biblioteke.
Prva verzija ima oblik
void strcpy(char *s, char *t)
{
int i=0;
while((s[i] = t[i]) != ’\0’) i++;
}
11.3. OPERACIJE NAD POKAZIVA
ˇ
CIMA 181
Funkcija kopira polje znakova na koje pokazuje t u polje na koje pokazuje s.
Kopiranje se zaustavlja kada se kopira nul znak ’\0’.
Istu funkciju moˇzemo izvesti i bez uglatih zagrada, koriste´ci aritmetiku
pokazivaˇca.
void strcpy(char *s, char *t)
{
while((*s = *t) != ’\0’) {
s++; t++;
}
}
Ovdje koristimo viˇsi prioritet operatora dereferenciranja od operatora pri-
druˇzivanja i inkrementiramo pokazivaˇce umjesto indeksa. Budu´ci da unarni
operatori imaju asocijativnost zdesna na lijevo gornji kˆ od moˇzemo skratiti i
pisati
void strcpy(char *s, char *t)
{
while((*s++ = *t++) != ’\0’) ;
}
Pokazivaˇci s i t bit ´ce pove´cani nakon ˇsto pridruˇzivanje bude izvrˇseno.
Konaˇcno, kˆ od moˇzemo joˇs malo skratiti ako uoˇcimo da je ...!=’\0’ us-
poredivanje izraza s nulom, pa moˇzemo pisati
void strcpy(char *s, char *t)
{
while(*s++ = *t++) ;
}
Naravno, ovakav kˆ od slabo izraˇzava namjeru programera te ga treba izbje-
gavati.
11.3.6 Generiˇcki pokazivaˇc
Pokazivaˇci na razliˇcite tipove podatatka op´cenito se ne mogu pridruˇzivati.
Na primjer,
char *pc;
int *pi;
.....
pi=pc; /* NEISPRAVNO */
182 POGLAVLJE 11. POKAZIVA
ˇ
CI
Razlog je u tome ˇsto konverzija pokazivaˇca na jedan tip u pokazivaˇc na
neki drugi tip moˇze dovesti do promjene interne reprezentacije pokazivaˇca.
Ipak, svako takvo pridruˇzivanje je dozvoljeno uz eksplicitnu promjenu tipa,
tj. upotrebu cast operatora:
char *pc;
int *pi;
.....
pi=(int *) pc; /* ISPRAVNO */
Ako ponovna izvrˇsimo konverziju u polazni tip pokazivaˇca, nije garantirano
da se vrijednost pokazivaˇca ne´ce promijeniti.
Pokazivaˇc moˇze biti deklariran kao pokazivaˇc na void i tada govorimo o
generiˇckom pokazivaˇcu.
void *p;
Pokazivaˇc na bilo koji tip moˇze se konvertirati u pokazivaˇc na void i obratno,
bez promjene pokazivaˇca.
double *pd0,*pd1;
void *p;
.....
p=pd0; /* ISPRAVNO */
pd1=p; /* ISPRAVNO */
Osnovna uloga generiˇckog pokazivaˇca je da omogu´ci funkciji da uzme poka-
zivaˇc na bilo koji tip podatka.
double *pd0;
void f(void *);
.....
f(pd0); /* O.K. */
S druge strane, ako se ˇzeli da funkcija primi upravo pokazivaˇc na double,
onda formalni argument treba deklarirati kao pokazivaˇc na double.
Generiˇcki pokazivaˇc se ne moˇze dereferencirati, pove´cavati i smanjivati.
U sljede´cem primjeru funkcija print uzima generiˇcki pokazivaˇc ptr i je-
dan argument tipa char koji govori na kakav objekt ptr pokazuje. Argument
se zatim ispisuje na odgovaraju´ci naˇcin.
#include <stdio.h>
#include <stdlib.h>
11.4. POKAZIVA
ˇ
CI I JEDNODIMENZIONALNA POLJA 183
void print(const char c, void *ptr)
{
int *pi;
double *px;
char *pc;
if(c == ’i’){
pi=ptr; printf("i=%d\n",*pi);
}
else if(c == ’f’){
px=ptr; printf("x=%f\n",*px);
}
else{
pc=ptr; printf("c=%s\n",pc);
}
}
int main(void) {
double a=8.0;
int j=8;
char *s="string";
void *p;
p=&a; print(’f’,p);
p=&j; print(’i’,p);
p=s; print(’s’,p);
return 0;
}
11.4 Pokazivaˇci i jednodimenzionalna polja
Pokazivaˇci i polja su usko vezani. Ime jednodimenzionalnog polja je kons-
tantan pokazivaˇc na prvi elemet polja. Ako imamo polje x i pokazivaˇc istog
tipa px, onda px nakon naredbe
px=&x[0];
pokazuje na prvi element polja x. Isti efekt postiˇzemo ako napiˇsemo
px=x;
184 POGLAVLJE 11. POKAZIVA
ˇ
CI
ˇ
Stoviˇse, imamo da je *(px+i)==x[i] i dvije forme moˇzemo koristiti ravno-
pravno. U skladu s time, ako imamo pokazivaˇc px, onda moˇzemo koristiti
notaciju px[i] umjesto *(px+i).
Pokazivaˇci i polja su stoga gotovo ekvivalentni. Svaka funkcija f() koja
kao argument uzima jednodimenzionalno polje nekog tipa moˇze se deklarirati
kao
tip_rez f(tip x[])
ili kao
tip_rez f(tip *x)
Ipak postoje i bitne razlike. Polje x nije l-vrijednost, tj. naredbe tipa
x= ...;
nisu dozvoljene. Isto tako, ime polja je konstantan pokazivaˇc pa nije do-
zvoljeno pisati x++, x-- i sliˇcno. Ali, u izrazu x+1 bit ´ce iskoriˇstena poka-
zivaˇcka aritmetika. To moˇzemo iskoristiti u sljede´coj situaciji: uzmimo da
imamo funkciju f(float *) koja oˇcekuje polje tipa float i da kao stvarni
argument funkciji ˇzelimo predati dio polja x od ˇsestog do zadnjeg elemeta.
Tada funkciju f() moˇzemo pozvati s
f(&x[5]);
ili
f(x+5);
Da bismo naglasili razliku izmedu polja i pokazivaˇca pogledajmo sljede´ce
dvije deklaracije:
char poruka[]="Dolazim odmah.";
char *pporuka="Dolazim odmah.";
U prvoj se deklarira i inicijalizira polje od 15 znakova koje je mogu´ce dohvatiti
i mijenjati putem indeksa u uglatim zagradama. U drugoj deklaraciji dek-
larira se pokazivaˇc na char i inicijalizira adresom znakovnog niza "Dolazim
odmah.". Sve ˇclanove niza moˇzemo dohvatiti putem pokazivaˇca, ali modifi-
ciranje niza putem pokazivaˇca je nedefinirana operacija. Naime, prevodilac
ima slobodu konstantan niz znakova smjestiti bilo gdje u memoriji pa stoga
ne moˇze garantirati da ´ce se vrijednosti niza mo´ci mijenjati. S druge strane
pokazivaˇc pporuka moˇze pokazivati i na svaku drugu char varijablu.
11.5. POKAZIVA
ˇ
CI I CONST 185
11.5 Pokazivaˇci i const
Vidjeli smo da modifikator const moˇzemo koristiti za definiciju konstanti.
Na primjer
const double pi=3.14159;
Jednako tako moˇzemo ga primijeniti na pokazivaˇce. Uzmimo sljede´ci primjer
double polje[5]={0.1,0.2,0.3,0.4,0.5};
const double *pp=polje;
Pokazivaˇc pp deklariran je kao pokazivaˇc na konstantan double. To znaˇci
da on pokazuje na konstantnu varijablu tipa double. Pokazivaˇc pp smo
inicijalizirali s nekonstantnim poljem polje, ˇsto je dozvoljeno. Prevodilac
nam jedino ne´ce dozvoliti mijenjanje elemenata polja putem pokazivaˇca pp.
*pp=3.2; // nije dozvoljeno
pp[3]=1.0; // nije dozvoljeno
polje[3]=1.0; // O.K.
Sam pokazivaˇc moˇzemo slobodno mijenjati
pp++; // O.K.
Ako definiramo konstantnu varijablu, onda samo pokazivaˇc na konstantan
tip moˇze pokazivati na tu varijablu. Na primjer,
const double polje[5]={0.1,0.2,0.3,0.4,0.5};
const double *pp=polje; /* ISPRAVNO */
double *pt=polje; /* NIJE DOZVOLJENO */
Pokazivaˇc na konstantan tip se deklarira kao argument funkcije da bi
se pokazalo da funkcija ne´ce korititi pokazivaˇc za mijenjanje varijable na
koju pokazuje. Funkcija koja samo ispisuje elemente polja mogla bi biti
deklarirana na ovaj naˇcin:
void print_array(const double *array, int n);
Takva ´ce funkcija prihvatiti konstantno i nekonstantno polje kao argument.
Mogu´ce je definirati konstantan pokazivaˇc na nekonstantan tip. Treba
samo pomaknuti const u definiciji. Na primjer,
double polje[5]={0.1,0.2,0.3,0.4,0.5};
double * const pp=polje;
pp = &polje[1]; /* NIJE DOZVOLJENO */
*pp=56.9; /* DOZVOLJENO */
186 POGLAVLJE 11. POKAZIVA
ˇ
CI
Ovakav pokazivaˇc pokazuje uvijek na istu lokaciju. Konaˇcno, moˇzemo defi-
nirati i konstantan pokazivaˇc na konstantan tip:
double polje[5]={0.1,0.2,0.3,0.4,0.5};
const double * const pp=polje;
pp = &polje[1]; /* NIJE DOZVOLJENO */
*pp=56.9; /* NIJE DOZVOLJENO */
11.6 Polja pokazivaˇca
Polje pokazivaˇca ima deklaraciju
tip_pod *ime[izraz];
Na primjer
int *ppi[10];
ppi je polje od 10 pokazivaˇca na int. Analogno se definiraju i viˇsedimenzionalna
polja pokazivaˇca. Uoˇcimo da uglate zagrade imaju viˇsi prioritet od operatora
dereferenciranja. Stoga gornja deklaracija predstavlja polje od 10 elemenata
koji su pokazivaˇci na tip int, a ne pokazivaˇc na polje od 10 varijabli tipa
int. U ovom drugom sluˇcaju morali bismo pisati
int (*ppi)[10];
Polja pokazivaˇca koriste se umjesto dvodimenzionalnih polja kada treba
zapamtiti niz polja razliˇcite duˇzine. Na primjer,
char *mjeseci[]={ "sijecanj", "veljaca", "ozujak",
"travanj", "svibanj", "lipanj",
"srpanj", "kolovoz", "rujan",
"listopad", "studeni", "prosinac"};
Ovdje smo iskoristili ˇcinjenicu da pokazivaˇc na char moˇzemo inicijalizirati
stringovima. Pojedine znakove moˇzemo dohvatiti indeksiranjem. Na primjer,
mjeseci[2][2] je jednako ’u’.
Polje mjeseci moˇzemo iskoristiti u funkciji koja za zadani broj vra´ca
pripadni mjesec.
char *ime_mjeseca(int n)
{
static char *mjeseci[]={ "Nekorektan broj mjeseca",
"sijecanj", "veljaca", "ozujak",
11.7. POKAZIVA
ˇ
CI I VI
ˇ
SEDIMENZIONALNA POLJA 187
"travanj","svibanj", "lipanj",
"srpanj","kolovoz", "rujan",
"listopad", "studeni", "prosinac"};
return (n<1 || n>12) ? mjeseci[0] : mjeseci[n];
}
Uoˇcimo da smo polje mjeseci deklarirali static kako ne bi bilo uniˇsteno na
izlazu iz funkcije.
11.7 Pokazivaˇci i viˇsedimenzionalna polja
U C-u dvodimenzionalno polje je polje ˇciji je svaki element jedno jedno-
dimenzionalno polje. Stoga, ako je
static int x[MAXX][MAXY];
jedno dvodimenzionalno polje, onda je x[i][j] element na mjestu (i,j), dok
je x[i] polje od MAXY elemenata. Sliˇcno je i s viˇsedimenzionalnim poljima.
Ako imamo
static int x[2][3][4];
onda je x polje dimenzije 2 ×3 × 4 dok je x[i] polje od tri elementa, svaki
od kojih je polje od 4 elemeta; x[i][j] je polje od 4 elemeta.
Operacija indeksiranja E1[E2] identiˇcna je s *(E1+E2) i stoga je to ko-
mutativna operacija (ˇsto nije naroˇcito korisno). Na primjer, ako je x polje
onda drugi element polja moˇzemo zapisati kao x[1] ili kao 1[x].
Kod dvodimenzionalnog polja izraza oblika x[2][3] interpretira se na
sljede´ci naˇcin:
x[2][3] -> *(x[2]+3) -> *(*(x+2)+3).
Ovdje je
x pokazivaˇc na jednodimenzionalno polje ˇciji su elemeti polja;
x+2 je pokazivaˇc na tre´ce polje;
*(x+2) tre´ce polje, dakle pokazivaˇc na prvi element tre´ceg polja;
*(x+2)+3 je tada pokazivaˇc na ˇcetvrti element tog polja;
*(*(x+2)+3) je sam taj element.
188 POGLAVLJE 11. POKAZIVA
ˇ
CI
Na analogan naˇcin tretiraju se tri i viˇsedimenzionalna polja.
Prilikom deklaracije viˇsedimenzionalnog polja (ali ne i definicije) mogu se
koristiti dvije ekvivalentne forme: potpuna
tip_pod ime[izraz_1][izraz_2]....[izraz_n];
ili bez prve dimenzije
tip_pod ime[][izraz_2]....[izraz_n];
ili pomo´cu pokazivaˇca
tip_pod (*ime)[izraz_2]....[izraz_n];
U zadnjem primjeru su zagrade nuˇzne jer bi u suprotnom imali polje poka-
zivaˇca na tip.
11.7.1 Matrica kao pokazivaˇc na pokazivaˇc
Osnovni nedostatak programskog jezika C u numeriˇckim primjenama je
naˇcin na koji se matrice predaju funkcijama. Standard C99 je to rijeˇsio
uvode´ci polja varijabilne duljine. Ovdje ´cemo pokazati jedno rjeˇsenje pro-
blema koje se ne oslanja na PVD.
Neka je definirano na primjer polje
double A[1024][1024];
Tada definiramo polje pokazivaˇca
double *aa[1024];
Ove pokazivaˇce inicijaliziramo pokazivaˇcima na retke matrice A:
for(i=0;i<1024;++i) aa[i]=A[i];
Zatim funkciju koja uzima matricu A deklariramo kao
void f(double **aa, int n, int m)
gdje su n i m dimenzije matrice. Sama matrica je deklarirana kao pokazivaˇc
na pokazivaˇc na double. Funkciju f() ne pozivamo s poljem A ve´c s poljem
pokazivaˇca aa. Na primjer,
f(aa,n,m);
Unutar funkcije f element A[i][j] moˇzemo dohvatiti kao aa[i][j]. Slijedi
jedan kompletan primjer:
11.8. DINAMI
ˇ
CKA ALOKACIJA MEMORIJE 189
#include <stdio.h>
double A[1024][1024];
void f(double **aa, int n, int m);
int main(void)
{
int i;
double *aa[1024];
A[56][1000]=123.445;
for(i=0;i<1024;++i) aa[i]=A[i];
f(aa,56,1000);
return 0;
}
// ispisi element na mjestu (i,j)
void f(double **aa, int i, int j)
{
printf("%f\n",aa[i][j]);
}
Program ´ce naravno ispisati 123.44500. Ovom tehnikom postiˇzemo to da
funkcija f ne mora znati stvarne dimenzije matrice A da bi s njom mogla
raditi. Cijena koju smo morali platiti je alokacija jednog polje pokazivaˇca
duljine n, gdje je n broj redaka matrice.
Uoˇcimo da bi poziv
f(A,n,m); /* POGRESNO */
bio nekorektan i prevodilac bi javio greˇsku. Naime, pri pozivu funkcije dolazi
do konverzije polja u pokazivaˇc na prvi element, ali ta se konverzija vrˇsi samo
jednom. Dvodimenzionalno polje tada postaje pokazivaˇc na jednodimenzi-
onalno polje, ˇsto nije isto ˇsto i pokazivaˇc na pokazivaˇc.
11.8 Dinamiˇcka alokacija memorije
Varijable i polja moˇzemo alocirati za vrijeme izvrˇsavanja programa prema
potrebama za memorijskim prostorom. U tu svrhu sluˇzimo se funkcijama
malloc i calloc deklariranim u <stdlib.h>. Funkcije malloc i calloc
deklarirane su na sljede´ci naˇcin
190 POGLAVLJE 11. POKAZIVA
ˇ
CI
void *malloc(size t n);
void *calloc(size t n, size t size);
void *realloc(void *ptr, size t n);
void free(void *ptr);
size t je cjelobrojni tip bez predznaka definiran u <stddef.h>, dovoljno
ˇsirok da primi vrijednost koju vra´ca sizeof operator.
Funkcija malloc uzima jedan argument n koji predstavlja broj bajtova
koji treba alocirati i rezervira memorijski blok od n bajtova.
1
Funkcija vra´ca
pokazivaˇc na rezervirani blok memorije ili NULL ako zahtijev za memorijom
nije mogao biti ispunjen. Vra´ceni pokazivaˇc je generiˇcki, tj. tipa void* pa ga
stoga prije upotrebe treba konvertirati u potrebni tip pokazivaˇca. Tipiˇcan
primjer upoterbe funkcije malloc je sljede´ci:
double *p;
......
p=(double *) malloc(128*sizeof(double));
if(p==NULL) {
printf("Greska: alokacija memorije nije uspjela!\n");
exit(-1);
}
Uoˇcimo da operatorom sizeof postiˇzemo neovisnost o stvarnoj duljini vari-
jable double. Ispitivanje je li pokazivaˇc koji malloc vra´ca NULL pointer je
nuˇzno za ispravno funkcioniranje programa.
Funkcija calloc uzima dva argumenta. Prvi je broj je broj varijabli za
koje ˇzelimo rezervirati memoriju, a drugi je broj bajtova koji svaka varijabla
zauzima. Funkcija vra´ca pokazivaˇc na blok memorije dovoljno velik da primi
polje od n objekata veliˇcine size, ili NULL ako zahtijev za memorijom nije
mogao biti ispunjen. Memorija se inicijalizira nulama tako da je svaki bit
postavi na nulu. Primjer upotrebe je
int *p;
......
p=(int *) calloc(128,sizeof(int));
if(p==NULL)
{
printf("Greska: alokacija memorije nije uspjela!\n");
exit(-1);
}
1
Jedan bajt u C-u je duljina varijable char u bitovima. Najˇceˇs´ce iznosi uobiˇcajenih
osam bitova.
11.9. POKAZIVA
ˇ
C NA FUNKCIJU 191
Primijetimo da svi bitovi postavljeni na nulu ne reprezentiraju nuˇzno, na svakoj hardwa-
reskoj platformi, nulu u sustavu s pokretnim zarezom i nul-pokazivaˇc.
Funkcija realloc uzima kao prvi argument pokazivaˇc na memoriju aloci-
ranu s malloc ili calloc funkcijom i mijenja njenu veliˇcinu, ˇcuvaju´ci sadrˇzaj
memorije. Ako je potrebno, sadrˇzaj ´ce biti kopiran na novu lokaciju. Nova
veliˇcina memorijskog bloka (u bajtovima) dana je u drugom argumentu.
Funkcija vra´ca pokazivaˇc na alocirani blok, ili NULL ako realokacija nije us-
pjela. U sluˇcaju neuspjeha realokacije stari memorijski blok ostaje nepromi-
jenjen. Ukoliko realloc vrati pokazivaˇc razliˇcit od prvog argumenta, onda
je stari memorijski blok dealociran, a stari sadrˇzaj je premjeˇsten na novu
lokaciju.
Ako je nova dimenzija memorijskog bloka ve´ca od stare, onda se dodaje
nova memorija koja, kao i kod malloca, nije inicijalizirana. Ako je nova
dimenzija bloka manja od stare, onda se samo dealocira dio memorije.
Memoriju alociranu pomo´cu funkcija malloc i calloc potrebno je deloci-
rati pomo´cu funkcije free. Ona uzima pokazivaˇc na alocirani blok memorije
i oslobada memoriju.
free(p);
Memorijski prostor treba delocirati ˇcim viˇse nije potreban.
11.9 Pokazivaˇc na funkciju
Pokazivaˇc na funkciju deklarira se kao
tip_pod (*ime)(tip_1 arg_1,tip_2 arg_2, ...,tip_n arg_n);
ime je tada pokazivaˇc na funkciju koja uzima n argumenata tipa tip 1 do
tip n i vra´ca vrijednost tipa tip pod. Zagrade su obavezne. U primjeru
int (*pf)(char c, double a);
pf je pokazivaˇc na funkciju koja uzima dva argumenta, prvi tipa char, a drugi
tipa double te vra´ca int. Kod deklaracija (koje nisu definicije) imena vari-
jabli se mogu ispustiti. Tako ako funkcija g uzima kao argument pokazivaˇc
na funkciju gornjeg tipa i ne vra´ca niˇsta, onda bismo deklarirali
void g(int (*)(char, double));
Ako je pf pokazivaˇc na funkciju onda je (*pf) moˇze koristiti kao ime funkcije.
Uzmimo kao primjer funkciju koja raˇcuna pribliˇzno integral zadane funk-
cije po trapeznoj formuli.
192 POGLAVLJE 11. POKAZIVA
ˇ
CI
#include <stdio.h>
#include <math.h>
double integracija(double, double, double (*)(double));
int main(void)
{
printf("Sinus: %f\n",integracija(0,1,sin));
printf("Kosinus: %f\n",integracija(0,1,cos));
return 0;
}
double integracija(double a, double b, double (*f)(double))
{
return 0.5*(b-a)*((*f)(a)+(*f)(b));
}
Funkcija integracija uzima donju i gornju granicu integracije i pokazivaˇc
na funkciju koju treba integrirati. Kao i kod polja, ime funkcije se kao stvarni
argument neke funkcije konvertira u pokazivaˇc na funkciju. Zato smo funkciju
integracija moglo pozvati kao
integracija(0,1,sin)
Na funkciju moˇzemo primijeniti adresni operator tako da smo mogli pisati
integracija(0,1,&sin)
i dobili bismo isti rezultat.
Unutar same funkcije integracija pokazivaˇc na funkciju mora se nalaziti
unutar zagrada je bi *f(a), zbog viˇseg prioriteta zagrada, bio interpretiran
kao dereferenciranje objekta f(a).
11.10 Argumenti komandne linije
U sistemskim okruˇzenjima kao ˇsto su dos i Unix, programu se mogu pre-
dati odredeni parametri, tzv. argumenti komandne linije, tako da se navedu
na komandnoj liniji iza imena programa, separirani razmacima. Na primjer,
naredba (program) cp u operacijskom sustavu Unix koja vrˇsi kopiranje da-
toteka poziva se sa dva parametra:
cp ime1 ime2
11.10. ARGUMENTI KOMANDNE LINIJE 193
gdje je ime1 ime datoteke koju ˇzelimo kopirati, a ime2 ime datoteke na koju
ime1 ˇzelimo kopirati. Programi pisani u C-u takoder mogu primiti argumente
komandne linije. Tu funkcionalnost osigurava funkcija main().
Funkcija main() prihva´ca dva argumenta: argc tipa int i polje poka-
zivaˇca na char, obiˇcno nazvan argv. Nova forma funkcije main() glasi:
int main(int argc, char *argv[])
{ .... }
Mehanizam je sljede´ci: operacijski sustav u varijablu argc smjeˇsta broj ar-
gumenta komandne linije koji su utipkani pri startanju programa, uve´can za
jedan. Ako nema argumenata komandne linije, onda je argc=1. U argv se
nalaze pokazivaˇci na argumente komandne linije, spremljeni u argv[0] do
argv[argc-1]. Pri tome je argv[0] uvijek pokazivaˇc na string koji sadrˇzi
ime programa koji se izvrˇsava. Ostali parametri su smjeˇsteni redom kojim su
upisani na komandnoj liniji.
2
Nadalje argv[argc] mora biti nul-pokazivaˇc.
Na primjer, program koji samo ispisuje argument komandne linije koji su
mu dani izgledao bi ovako:
/* program args */
#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
for(i=0;i<argc; i++)
printf("%s%s", argv[i],(i<argc-1) ? "," : ".");
printf("\n");
return 0;
}
Pozovemo li program args narebom
args ovo su neki parametri
on ´ce ispisati
args,ovo,su,neki,parametri.
Operacijski sustav Unix, odnosno shell koji se koristi ekspandirat ´ce speci-
jalne znakove ?, * i [] prije nego ˇsto ih preda pozivnom programu. Viˇse
detalja moˇze se na´ci priruˇcniku za dani shell (sh, ksh, csh ili bash, vidi joˇs
odgovaraju´ce man-stranice).
2
Ako shvatimo ime programa kao argument komandne linije, onda argc daje toˇcno
broj argumenata komandne linije.
194 POGLAVLJE 11. POKAZIVA
ˇ
CI
11.11 Sloˇzene deklaracije
Pri ˇcitanju sloˇzenih deklaracija osnovno pravilo je da je deklaracija objekta
u skladu s naˇcinom koriˇstenja objekta. Ako deklariramo
int *f(void);
onda *f() mora biti tipa int. To znaˇci da je f funkcija koja ne uzima
argumente i vra´ca pokazivaˇc na int.
Prilikom interpretacije deklaracije uzimaju se u obzir prioriteti pojedinih
operatora. Ti prioriteti mogu se promijeniti upotrebom zagrada, ˇsto kompli-
cira ˇcitanje deklaracija. Tako je
int (*f)(void);
deklaracija pokazivaˇca na funkciju koja ne uzima argumente i vra´ca int.
Evo nekoliko primjera:
int *p; /* p je pokazivac na int */
int *p[10]; /* p je polje od 10 pokazivaca na int */
int (*p)[10]; /* p je pokazivac na polje od 10 elemenata
tipa int */
int *f(void); /* p je funkcija koja ne uzima argumente i
vraca pokaziva\v c na int */
int p(char *a); /* p je funkcija koja uzima pokazivac na
char i vraca int */
int *p(char *a); /* p je funkcija koja uzima pokazivac na
char i vraca pokazivac na int */
int (*p)(char *a); /* p je pokazivac na funkciju koja uzima
pokazivac na char i vraca int */
int (*p(char *a))[10]; /* p je funkcija koja uzima pokazivac
na char i vraca pokazivac na polje
od 10 elemenata tipa int int */
int p(char (*a)[]); /* p je funkcija koja uzima pokazivac
na polje znakova i vraca int */
11.11. SLO
ˇ
ZENE DEKLARACIJE 195
int (*p)(char (*a)[]); /* p je pokazivac na funkciju koja
uzima pokazivac na polje znakova
i vraca int */
int *(*p)(char (*a)[]); /* p je pokazivac na funkciju koja
uzima pokazivac na polje znakova
i vraca pokazivac na int */
int *(*p[10])(char *a); /* p je polje od 10 pokazivaca na
funkciju koja uzima pokazivac na
char i vraca pokazivac na int */
Poglavlje 12
Strukture
Polja grupiraju ve´ci broj podataka istog tipa. Strukture sluˇze grupiranju
viˇse podataka razliˇcitih tipova.
12.1 Deklaracija strukture
Struktura se deklarira na sljede´ci naˇcin:
struct ime {
tip_1 ime_1;
tip_2 ime_2;
.... ....
tip_n ime_n;
};
struct je rezervirano ime, ime je naziv koji dajemo strukturi, a unutar
vitiˇcastih zagrada su pojedini ˇclanovi strukture. Na primjer, moˇzemo defini-
rati strukturu tocka koja definira toˇcku u ravnini
struct tocka {
int x;
int y;
};
Ova struktura sadrˇzi dva ˇclana istog tipa, no ˇclanovi strukture mogu biti bilo
kojeg tipa, osnovni tipovi, pokazivaˇci, polja i druge strukture.
12.1.1 Varijable tipa strukture
Deklaracija strukture samo definira tip podatka, ali ne rezervira memorij-
ski prostor. Nakon ˇsto je struktura deklarirana moˇzemo definirati varijable
196
12.1. DEKLARACIJA STRUKTURE 197
tog tipa:
mem_klasa struct ime varijabla1, varijabla2,...;
gdje je mem klasa memorijska klasa, ime je naziv strukture, a varijabla1,
varijabla2,.. varijable koje definiramo. Na primjer,
struct tocka p1,p2;
Ovdje su varijable p1 i p2 tipa tocka i za njih je ovdje rezerviran potreban
memorijski prostor.
Varijable tipa strukture mogu´ce je definirati unutar deklaracije strukture
na sljede´ci naˇcin:
mem_klasa struct ime {
tip_1 ime_1;
tip_2 ime_2;
.... ....
tip_n ime_n;
} varijabla1, varijabla2,...;
tako smo mogli pisati
struct tocka {
int x;
int y;
} p1,p2;
Takav naˇcin deklaracije strukture i definicije varijabli pogodan je kada va-
rijable tipa struture uvodimo samo na jednom mjestu. U tom sluˇcaju ne
moramo davati ime strukturi, tj. moˇzemo pisati
struct {
int x;
int y;
} p1,p2;
Naravno, druge varijable tog tipa na drugom mjestu nije mogu´ce definirati.
12.1.2 Inicijalizacija strukture
Varijabla tipa strukture moˇze se inicijalizirati prilikom definicije na ovaj
naˇcin:
mem_klasa struct ime varijabla={v_1, v_2,..., v_n};
198 POGLAVLJE 12. STRUKTURE
pri ˇcemu se v 1 pridruˇzuje prvom ˇclanu strukture, v 2 drugom itd.
Uzmemo li strukturu
struct racun {
int broj_racuna;
char ime[80];
float stanje;
};
onda moˇzemo inicijalizirati varijablu kupac na ovaj naˇcin:
struct racun kupac={1234,"Goran S.", -234.00};
Sliˇcno se moˇze inicijalizirati i polje struktura
struct racun kupci[]={34,"Ivo R.", 456.00,
35,"Josip S.", 234.00,
36,"Dalibor M.",00.00};
12.1.3 Struktura unutar strukture
Strukture mogu sadrˇzavati druge strukture kao ˇclanove. Pravokutnik
moˇze biti odreden donjim lijevim (pt1) i gornjim desnim vrhom (pt2):
struct pravokutnik {
struct tocka pt1;
struct tocka pt2;
};
Pri tome deklaracija strukture tocka mora prethoditi deklaraciji strukture
pravokutnik.
Ista imena ˇclanova mogu se koristiti u razliˇcitim strukturama.
12.2 Rad sa strukturama
12.2.1 Operator toˇcka
ˇ
Clanovima strukture moˇze se pristupiti individualno putem operatora
toˇcka (.). Ako je var varijabla tipa strukture koja sadrˇzi ˇclan memb, onda je
var.memb
ˇclan memb u strukturi var. Ako je pt varijabla tipa struct tocka,
12.2. RAD SA STRUKTURAMA 199
struct tocka pt;
onda su pt.x i pt.y int varijable koje daju x i y koordinatu toˇcke pt. Ako
je rect varijabla tipa struct pravokutnik,
struct pravokutnik rect;
onda su rect.pt1.x i rect.pt1.y, x i y koordinate donjeg lijevog vrha
pravokutnika itd. Udaljenost toˇcke pt do toˇcke (0,0) izraˇcunali bismo ovako
double dist, sqrt(double);
dist=sqrt((double) pt.x * pt.x + (double) pt.y * pt.y);
Operator toˇcka (.) separira ime varijable i ime ˇclana strukture. Spada u
najviˇsu prioritetnu grupu i ima asocijativnost slijeva na desno. Zbog najviˇseg
prioriteta toˇcka operatora izraz
++varijabla.clan;
je ekvivalentan s ++(varijabla.clan); isto tako &varijabla.clan je ek-
vivalentan s &(varijabla.clan). Adresni operator moˇze se primijeniti na
ˇcitavu strukturu i na pojedine njene ˇclanove. Broj memorijskih lokacija koje
struktura zauzima moˇze se dobiti pomo´cu sizeof operatora kao i za jednos-
tavne tipove podataka. Taj broj moˇze biti ve´ci od sume broja memorijskih
lokacija za pojedine ˇclanove strukture.
Kada struktura sadrˇzi polje kao ˇclan strukture, onda ˇclanovi polja doseˇzu
izrazom
varijabla.clan[izraz]
Na primjer, ako je kupac varijabla tipa struct racun, onda osmi znak u
imenu kupca moˇzemo dohvatiti izrazom kupac.ime[7]. Ako pak imamo
polje struktura, onda pojedini ˇclan elementa polja doseˇzemo izrazom
polje[izraz].clan
Na primjer, ako je varijabla kupci polje tipa struct racun, onda broj raˇcuna
osmog kupca doseˇzemo izrazom kupci[7].broj racuna.
U zadnja dva primjera dolazi do izraˇzaja socijativnost, jer su uglate za-
grade i operator toˇcka istog prioriteta. Njihova asocijativnost je slijeva na
desno, tako da se operandi grupiraju prvo oko lijevog operatora.
S ˇclanovima strukture postupa se jednako kao i s obiˇcnim varijablama
istog tipa. Isto vrijedi i za sloˇzene ˇclanove strukture, polja, strukture itd.
200 POGLAVLJE 12. STRUKTURE
12.2.2 Struktura i funkcije
Jedine operacije koje se mogu provoditi na strukturi kao cjelini su pri-
druˇzivanje i uzimanje adrese pomo´cu adresnog operatora. Struktura moˇze
biti argument funkcije koja tada dobiva kopiju strukture koja je stvarni argu-
ment funkcije. Isto tako funkcija moˇze vratiti strukturu u pozivni program.
Na primjer, sljede´ca funkcija uzima dvije strukture tipa tocka kao argumente
i vra´ca strukturu tipa tocka koja je suma dvaju argumenata:
struct tocka suma(struct tocka p1, struct tocka p2)
{
p1.x += p2.x;
p1.y += p2.y;
return p1;
}
12.3 Strukture i pokazivaˇci
Pokazivaˇc na strukturu definira se kao i pokazivaˇc na osnovne tipove
varijabli. U primjeru
struct tocka {
int x;
int y;
} p1, *pp1;
p1 je varijabla tipa struct tocka, a pp1 je pokazivaˇc na strukturu tipa
tocka. Pokazivaˇc moˇzemo inicijalizirati adresom varijable:
pp1= &p1;
Pojedini ˇclan strukture moˇze se putem pokazivaˇca dose´ci izrazom (*pp1).x i
(*pp1).y. Uoˇcimo da su zagrade nuˇzne jer operator toˇcka ima viˇsi prioritet
od operatora dereferenciranja. Izraz *pp1.x je ekvivalenta s *(pp1.x), ˇsto
nije korektno formiran izraz.
12.3.1 Operator strelica (->)
C nudi semantiˇcki jednostavniji naˇcin dohva´canja ˇclana strukture putem
pokazivaˇca: ako je ptvar pokazivaˇc na strukturu, a clan jedan ˇclan struk-
ture, onda je izraz
ptvar->clan
12.3. STRUKTURE I POKAZIVA
ˇ
CI 201
ekvivalentan s (*ptvar).clan. Tako je p1.x isto ˇsto i pp1->x itd.
Ako je ˇclan strukture i sam stuktura, onda moˇzemo kombinirati operatore
-> i . da bismo doˇsli podˇclana uloˇzene strukture:
ptvar->clan.podclan
Ako je neki ˇclan strukture polje, onda elemente polja dostiˇzemo izrazom
ptvar->clan[izraz]
Struktura moˇze sadrˇzavati ˇclanove koji su pokazivaˇci. Budu´ci da . i ->
operatori imaju viˇsi prioritet od * operatora, vrijednost na koju ˇclan pokazuje
moˇzemo dohvatiti pomo´cu
*var.clan
ili
*ptvar->clan
Sliˇcno, zbog najviˇseg prioriteta koji imaju operatori . i -> izrazi poput
++ptvar->clan i ++ptvar->clan.podclan
ekvivalentni su izrazima
++(ptvar->clan) i ++(ptvar->clan.podclan).
Izraz
(++ptvar)->clan
pove´cat ´ce pokazivaˇc na strukturu prije nego ˇsto se dohvati clan strukture.
Pri tome se adresa koju sadrˇzi ptvar pove´ca za onoliko okteta koliko iznosi
veliˇcina strukture.
Evo joˇs nekoliko promjera izraza s -> i .; neka je
struct pravokutnik r, *pr=&r;
Tada su sljede´ci izrazi ekvivalentni:
r.pt1.x
pr->pt1.x
(r.pt1).x
(pr->pt1).x
Tu se koristi asocijativnost operatora . i ->, koja je slijeva na desno.
202 POGLAVLJE 12. STRUKTURE
12.3.2 Sloˇzeni izrazi
Kada imamo pokazivaˇc na strukturu s ˇclanom koji je pokazivaˇc mogu se
javiti sloˇzeniji izrazi. Na primjer, neka je
struct {
int n;
char *ch;
} *pt;
Tada moˇzemo imati izraze
(++pt)->ch (pt++)->ch
pri ˇcemu prvi pove´cava pokazivaˇc pt, a zatim vra´ca ch ˇclan strukture, dok
drugi prvo vra´ca ch ˇclan strukture, i nakon toga pove´cava pokazivaˇc pt. U
ovom drugom primjeru zagrada nije potrebna, tj. moˇzemo pisati
pt++->ch
(asocijativnost od ++ je zdesna na lijevo, a -> slijeva na desno).
Izraz
*pt->ch++
pove´cat ´ce clan ch nakon ˇsto dohvati vrijednost na koju pt->ch pokazuje.
Naime, operator -> ima najviˇsi prioritet i prvi se primijenjuje. Unarni ope-
ratori imaju isti prioritet i asocijativnost D->L. To znaˇci da se prvo primi-
jenjuje operator inkrementiranja na pokazivaˇc pt->ch, a tek onda operator
dereferenciranja. Kako je operator inkrementiranja u postfiks formi, to ´ce
do inkrementiranja do´ci nakon ˇsto se dohvati vrijednost na koju pt->ch po-
kazuje. Da bismo inkrementirali sam objekt na koji pt->ch pokazuje treba
pisati (*pt->ch)++. Konaˇcno,
*pt++->ch
´ce inkrementirati pt nakon ˇsto dohvati objekt na koji ch pokazuje.
Ne treba niti spominjati da ovakve izraze treba izbjegavati, razbijati ih u
viˇse izraza i maksimalno se koristiti zagradama kako bi znaˇcenje izraza bilo
jasnije.
12.4. SAMOREFERENTNE STRUKTURE 203
12.4 Samoreferentne strukture
Za implementaciju tipova podataka kao ˇsto su vezane liste i stabla ko-
ristimo samoreferentne strukture. To su strukture u kojima je jedan ili viˇse
ˇclanova pokazivaˇc na strukturu istog tipa. Jedan primjer je
struct element {
char ime[64];
struct element *next;
};
Struktura element ima ˇclan next koji je pokazivaˇc na strukturu tipa element.
Taj nam pokazivaˇc sluˇzi za povezivanje istovrsnih podataka u listu tako ˇsto
´ce next pokazivati na sljede´ci element u listi; posljednji element ima u polju
next upisanu vrijednost NULL. Na taj naˇcin dobivamo jednostruko povezanu
listu.
12.4.1 Jednostruko povezana lista
Ilustrirajmo upotrebu samoreferentne strukture na jednom jednostavnom
primjeru jednostruko povezane liste ˇciji su elementi znakovni nizovi. Poˇcetak
programa ima sljede´ci oblik:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* oznaka kraja liste */
#define KRAJ (struct ime *) 0
/* deklaracija samoreferentne strukture */
struct ime {
char *p_ime;
struct ime *next;
};
/* pokazivac na prvi element liste (globalna varijabla) */
struct ime *start=KRAJ;
Struktura ime sadrˇzi pokazivaˇc na niz znakova p ime i pokazivaˇc na sljede´ci
element u listi next.
204 POGLAVLJE 12. STRUKTURE
Pokazivaˇc na prvi element liste start definiran je kao globalna varijabla
i inicijaliziran nul-pokazivaˇcem koji oznaˇcava kraj liste. U ovom trenutku
lista je posve definirana i ne sadrˇzi niti jedan element.
Element se u vezanu listu moˇze dodati i izbrisati na bilo kojem mjestu.
Napiˇsimo funkciju unos koja dodaje novi element na kraj liste.
/* dodavanje novog elementa na kraj liste */
void unos(void) {
struct ime *zadnji, *novi;
char line[128];
printf("Dodavanje novog elementa na kraj liste.\n");
novi = (struct ime *) malloc(sizeof(struct ime));
if(novi == NULL) {
printf("Nema dovoljno memorije ... ");
exit(-1);
}
novi->next=KRAJ;
printf("Unesite ime > ");
scanf(" %[^\n]",line);
novi->p_ime=(char *) malloc(strlen(line)+1);
if(novi->p_ime == NULL) {
printf("Nema dovoljno memorije ...");
exit(-1);
}
strcpy(novi->p_ime,line);
if(start==KRAJ) /* prazna lista */
start=novi;
else { /* pronadji kraj liste */
for(zadnji=start; zadnji->next != KRAJ; zadnji=zadnji->next)
; // ne radi nista
/* neka zadnji pokazuje na novi element */
zadnji->next=novi;
}
}
Kˆ od prvo alocira memoriju za novi element. Budu´ci da ´ce on biti zadnji u
listi, njegov next ˇclan se postavlja na KRAJ. Zatim se alocira memorija za
novo ime koje se uˇcitava u spremnik line pomo´cu scanf funkcije i kopira
na rezerviranu memoriju sa strcpy. Kˆ od ne uzima u obzir eventualni pretek
spremnika line.
12.4. SAMOREFERENTNE STRUKTURE 205
KRAJ start
Novi element je sada inicijaliziran i lokalna varijabla novi pokazuje na
njega. Ukoliko je lista prazna poˇcetak liste inicijaliziramo s novi. Ako nije,
nalazimo zadnji element i njegov next ˇclan postavljamo na novi koji time
postaje zadnji element liste.
Sljede´ca funkcija ispisuje listu.
void ispis(void) { /* Ispis liste */
struct ime *element;
int i=1;
if(start == KRAJ ) {
printf("Lista je prazna.\n");
return;
}
printf("Ispis liste \n");
for(element=start; element != KRAJ; element=element->next) {
printf("%d. %s\n",i,element->p_ime); i++;
}
return;
}
Ukoliko lista nije prazna u jednoj for petlji se prolazi svim njenim ˇclanovima.
Za pretraˇzivanje liste koristimo istu for petlju kojom obilazimo ˇcitavu
listu.
void trazi(void) { /* Pretrazivanje liste */
struct ime *test;
char line[128];
int i;
printf("Nalazenje imena u listi.\n");
printf("Unesite ime ");
scanf(" %[^\n]",line);
i=1;
for(test=start; test != KRAJ; test=test->next) {
if( !strcmp(test->p_ime,line) ) {
printf("Podatak je %d. u listi\n",i);
return;
206 POGLAVLJE 12. STRUKTURE
}
i++;
}
printf("Podatak nije u listi.\n");
}
Funkcija vrˇsi test usporedivanja upisanog imena sa svakim podatkom u listi
linearno, poˇcevˇsi od prvog do zadnjeg elementa liste. Ukoliko ime nije u listi
bit ´ce ispisano: Podatak nije u listi.
Brisanje elementa iz liste je neˇsto sloˇzenije.
void brisi(void) /* Brisanje elementa iz liste */
{
struct ime *test, *tmp;
char line[128];
int i;
printf("Brisanje imena iz liste.\n");
if(start == KRAJ){
printf("Lista je prazna.\n");
return;
}
printf("Unesite ime ");
scanf(" %[^\n]",line);
/* ako je prvi element onaj koji trazimo */
if( !strcmp(start->p_ime,line) ) {
printf("Podatak je 1. u listi\n");
tmp=start;
start=start->next;
free(tmp->p_ime);
free(tmp);
return;
}
i=1;
for(test=start; test != KRAJ; test=test->next) {
i++;
if(!(tmp=test->next)) break; // elm. nije nadjen
if( !strcmp(tmp->p_ime,line) )
{
printf("Brisemo podatak br. %d u listi\n",i);
test->next=test->next->next;
12.4. SAMOREFERENTNE STRUKTURE 207
free(tmp->p_ime);
free(tmp);
return;
}
}
printf("Podatak nije u listi.\n");
return;
}
Kod brisanja elementa iz liste prvo provjeravamo da li je lista prazna. Ako
nije, unosimo ime koje ˇzelimo izbrisati i provjeravamo da li je ono na prvom
mjestu u listi. To radimo stoga ˇsto je brisanje elementa na prvom mjestu
neˇsto jednostavnije od brisanja proizvoljnog elementa. Dovoljno je samo
start postaviti da pokazuje na drugi element: start=start->next. Budu´ci
da je memorija za svaki element alocirana dinamiˇcki, prilikom brisanja ele-
menta treba ju osloboditi. Stoga se stara vrijednost pokazivaˇca start pamti
u lokalnoj varijabli tmp i memorija se oslobada pozivom funkcije free(tmp).
Kod brisanja elementa na proizvoljnom mjestu porebno je imati pokazivaˇc
na element koji prethodi elementu za brisanje jer njegov next ˇclan treba
preusmjeriti na element iza onog koji briˇsemo. Stoga u petlji
for(test=start; test != KRAJ; test=test->next)
provjeravamo pokazuje li tmp=test->next na element za brisanje. Ako da,
onda se test->next postavlja na test->next->next, prvi element iza onog
kojeg briˇsemo i time je izvrˇseno izbacivanje iz liste. Ostaje joˇs samo delocirati
memoriju izbrisanog elementa.
U ovom kˆ odu postoji problem zadnjeg elemeta. Ako je test zadnji ele-
ment, onda je tmp=test->next=KRAJ pa bi tmp->p ime u strcmp funkciji
dalo greˇsku. Stoga tesiramo taj pokazivaˇc i izlazimo iz petlje pomo´cu break
naredbe ako smo na zadnjem elementu.
Konaˇcno da bismo kompletirali program uvodimo funkciju menu koja is-
pisuje menu s mogu´cim operacijama
int menu(void) {
int izbor;
printf("\n\n\n");
208 POGLAVLJE 12. STRUKTURE
printf("\n OPERACIJE : ");
printf("\n =========");
printf("\n 1. Unos novog elemeta ");
printf("\n 2. Ispis liste ");
printf("\n 3. Pretrazivanje liste ");
printf("\n 4. Brisanje iz liste ");
printf("\n 5. Izlaz ");
printf("\n izbor = ");
scanf("%d",&izbor);
return izbor;
}
i dajemo glavni program:
int main(void) {
int izbor;
do {
switch(izbor=menu()) {
case 1:
unos();
break;
case 2:
ispis();
break;
case 3:
trazi();
break;
case 4:
brisi();
break;
case 5:
break;
default:
printf("\n Pogresan izbor.");
}
} while(izbor != 5);
return 0;
}
12.5. TYPEDEF 209
12.5 typedef
Pomo´cu kljuˇcne rijeˇci typedef moˇzemo postoje´cim tipovima podataka
dati nova imena. Na primjer, ako neki podaci tipa double imaju znaˇcenje
mase, onda moˇzemo pisati
typedef double Masa;
i Masa ´ce postati sinonim za double. Nakon toga moˇzemo deklarirati varija-
ble tipa double kao
Masa m1,m2,*pm1;
Masa elementi[10];
i tako dalje. Op´cenito typedef ima oblik
typedef tip_podatka novo_ime_za_tip_podatka;
Vidimo da je typedef vrlo sliˇcan preprocesorskoj naredbi #define, no kako
typedef interpretira prevodilac mogu´ce su sloˇzenije supstitucije. Na primjer,
pokazivaˇc na double moˇzemo nazvati Pdouble:
typedef double * Pdouble;
i moˇzemo pisati
Pdouble px;
void f(Pdouble,Pdouble);
px= (Pdouble) malloc(100*sizeof(Pdouble));
Znaˇcenje simbola koji se uvodi typedef deklaracijom je sljede´ce: Ako
se ispusti kljuˇcna rijeˇc typedef iz deklaracije, dobiva se obiˇcna deklaracija
varijable u kojoj deklarirani simbol postaje varijabla. Deklarirani simbol
predstavlja tip koji bi ta varijabla imala.
typedef se ˇcesto koristi sa strukturama kako bi se eliminirala potreba da
se pri deklaraciji varijabli navodi struct. Na primjer, ako imamo
struct tocka {
int x;
int y;
};
onda varijable tipa tocka deklariramo kao
struct tocka p1, *pp1;
210 POGLAVLJE 12. STRUKTURE
Umjesto toga moˇzemo iskoristiti typdef:
typedef struct {
int x;
int y;
} tocka;
i sada moˇzemo pisati
tocka p1, *pp1;
ili
struct pravokutnik {
tocka pt1;
tocka pt2;
};
Sloˇzenija je situacija ako ˇzelimo napraviti typedef samoreferentne struk-
ture. Tada moramo uvesti jednu prethodnu deklaraciju tipa. Na primjer,
typedef struct element *Pelement;
typedef struct element {
char ime[64];
Pelement next;
} Element;
Sada moˇzemo definirati
Element root;
i sliˇcno.
Sljede´ca ˇcesta upotreba typedef-ova je za skraˇcivanje kompleksnih dek-
laracija kao ˇsto su to pokazivaˇci na funkcije. Na primjer, nakon
typedef int (*PF)(char *, char *);
PF postaje ime za pokazivaˇc na funkciju koja uzima dva pokazivaˇca na char
i vra´ca int. Sada umjesto deklaracije
void f(double x, int (*g)(char *, char *))
{ ......
moˇzemo pisati
12.6. UNIJA 211
void f(double x, PF g)
{ ......
typedef se koristi za parametrizaciju kˆ oda koja omogu´cava ve´cu preno-
sivost kˆ oda s jednog na drugo raˇcunalo: na primjer size t je izveden kao
typedef. Drugo, uvodenje smislenih imena za standardne tipove podataka
pridonosi boljoj dokumentaciji programa.
12.6 Unija
Unija kao i struktura sadrˇzi ˇclanove razliˇcitog tipa, ali dok kod strukture
svaki ˇclan ima zasebnu memorijsku lokaciju, kod unije svi ˇclanovi dijele istu
memorijsku lokaciju. Memorijska ´ce lokacija biti dovoljno ˇsiroka da u nju
stane najˇsiri ˇclan unije.
Sintaksa unije analogna je sintaksi strukture. Op´cenito imamo
union ime {
tip_1 ime_1;
tip_2 ime_2;
.... ....
tip_n ime_n;
};
Varijabla tipa ime moˇze se deklarirati pri deklaraciji unije ili odvojeno, kao
kod struktura.
union ime x,y;
Na primjer,
union pod {
int i;
float x;
} u, *pu;
Komponentama varijable u moˇzemo pristupiti preko operatora . ili operatora
->. Tako su u.i i pu->i varijable tipa int, a u.x i pu->x varijable tipa float.
Osnovna svrha unije je uˇsteda memorijskog prostora. Na istoj memo-
rijskoj lokaciji moˇzemo ˇcuvati varijable razliˇcitih tipova. Pri tome moramo
paziti da uniji pristupamo konsistentno. Na primjer, u dijelu programa u
kojem smo uniju u inicijalizirali kao varijablu tipa int moramo joj pristupiti
kao varijabli tipa int i obratno. Ako u uniju upiˇsemo vrijednost jednog tipa,
212 POGLAVLJE 12. STRUKTURE
a onda vrijednost unije proˇcitamo kao varijablu drugog tipa rezultat ´ce ovisiti
o raˇcunalu i najvjerojatnije ´ce biti besmislen.
Uniju pod moˇzemo iskoristiti kako bismo ispisali binarni zapis broja s
pokretnim zarezom na sljede´ci naˇcin:
u.x=0.234375;
printf("0.234375 binarno = %x\n",u.i);
Ovaj se kˆ od oslanja na ˇcinjenicu da se (na danom raˇcunalu) za pam´cenje
varijabli tipa float i int koristi isti broj okteta.
Poglavlje 13
Datoteke
U ovom poglavlju opisujemo funkcije iz standardne ulazno-izlazne bibli-
oteke koje sluˇze za rad s datotekama. Program koji koristi ove funkcije mora
ukljuˇciti datoteku zaglavlja <stdio.h>.
13.1 Vrste datoteka
Datoteka je imenovano podruˇcje u sekundarnoj memoriji, najˇceˇs´ce tvr-
dom disku (disketi, CD-u i sliˇcno), koje sluˇzi za smjeˇstaj podataka. Sa sta-
noviˇsta operacijskog sustava datoteka je sloˇzen objekt koji se obiˇcno sastoji
od viˇse povezanih fragmenata smjeˇstenih na razliˇcitim lokacijama u sekun-
darnoj memoriji. Kako operacijski sustav brine o svim detaljima rada s
datotekama, C promatra datoteku posve funkcionalno kao cjelinu u koju je
mogu´ce upisati niz znakova ili ga iz nje proˇcitati.
Preciznije, u C-u je datoteka kontinuirani niz okteta
1
(eng. stream) koje
je mogu´ce dohvatiti individualno. Prilikom ˇcitanja iz datoteke niz okteta
je usmjeren od datoteke prema programu; program moˇze ˇcitati ulazni niz
znak-po-znak. Kod pisanja u datoteku niz znakova ide od programa prema
datoteci. Najmanja jedinica koja moˇze biti proˇcitan ili upisana je jedan znak
(oktet).
Ovakva apstraktna definicija datoteke omogu´cava da se tastatura i ekran
raˇcunala tretiraju kao datoteke. S tastature je mogu´ce samo ˇcitati, dok je
na ekran mogu´ce samo pisati.
1
Niz znakova, odn. niz varijabli tipa char.
213
214 POGLAVLJE 13. DATOTEKE
13.1.1 Tekstualne i binarne datoteke
ANSI standard poznaje dvije vrste datoteka: tekstualne i binarne. Raz-
lika medu njima je u naˇcinu na koji se interpretira sadrˇzaj datoteke.
Binarna datoteka je niz podataka tipa char. Budu´ci da se svaki tip
podatka u C-u (int, double, struct ...) moˇze preslikati u niz vrijednosti
tipa char, u binarnu je datoteku mogu´ce upisati podatke onako kako su
reprezentirani u raˇcunalu.
Teksualna datoteka je niz znakova podijeljenih u linije. Svaka linija sadrˇzi
nula ili viˇse znakova iza kojih slijedi znak za prijelaz u novi red ’\n’. Teks-
tualne datoteke su namijenjene pohranjivanju tekstualnih podataka.
Vidimo da pojam binarne datoteke odgovara naˇsoj prethodnoj definiciji
datoteke kao niza znakova. Tekstualna datoteka je isto tako niz znakova ali se
on interpretira na malo drugaˇciji naˇcin. Potreba za razlikovanjem izmedu tek-
stualnih i binarnih datoteka pojavila se stoga ˇsto razliˇciti operacijski sustavi
koriste razliˇcite naˇcine oznaˇcavanja kraja linije: programski jezik C oznaˇcava
kraj linije znakom ’\n’.
Pod operacijskim sustavom UNIX, znak za kraj linije je ’\n’ (kao i u
C-u) te stoga pod UNIXom nema razlike izmedu binarne i tekstualne datoteke.
Dvije vrste datoteka implementirane su na isti naˇcin.
Operacijski sustav MS-DOS kraj linije oznaˇcava s dva znaka ’\r’’\n’,
dok npr. Macintosh koristi znak ’\r’. Kada C otvori tekstualnu datoteku
kreiranu pod MS-DOSom on ´ce svaki par znakova ’\r’ ’\n’ pretvoriti u
znak ’\n’. Analogna se transformacija provodi s Macintoshovim tekstual-
nim datotekama. Pored toga neki MS-DOS editori oznaˇcavaju kraj datoteke
znakom Ctr-Z dok C nema poseban znak za kraj datoteke.
Treba uoˇciti da C omogu´cava da danu datoteku promatramo kao binarnu
ili tekstualnu prema naˇsim potrebama, neovisno o tome da li datoteka sadrˇzi
tekstualne podatke ili ne. Tekstualnu datoteku formiranu npr. pod MS-
DOSom moˇzemo otvoriti i kao tekstualnu i kao binarnu. Razlika je prikazana
na slici 13.1.1.
13.1.2 Razine ulaza/izlaza
Postoje dvije razine ulazno-izlaznih operacija. 1) Mogu´ce je neposredno
koristiti funkcije za ulaz/izlaz koje nudi operacijski sustava; to je niska razina
ulaza/izlaza. 2) Mogu´ce je koristiti standardne ulazno/izlazne funkcije ˇciji
su prototipovi dani u <stdio.h> datoteci. To je standardna visoka razina
ulaza/izlaz. Funkcije iz standardne biblioteke jezika C skrivaju od korisnika
jedan niz detalja vezanih uz operacijski sustav pa su stoga jednostavnije
za koriˇstenje od funkcija koje nudi operacijski sustav. Pored toga kˆ od koji
13.1. VRSTE DATOTEKA 215
slika 13.1.1 Tekstualna i binarna datoteka
MS-DOS
tekstualna
datoteka
Promotrimo jednostavni pokus \r\n
bacanja simetriˇcnog novˇci´ca \r\n
ili popularno nazvanu igru “pismo-grb”. \r\n
^Z
Promotrimo jednostavni pokus \r\n
bacanja simetriˇcnog novˇci´ca \r\n
ili popularno nazvanu igru “pismo-grb”. \r\n
^Z
Promotrimo jednostavni pokus \n
bacanja simetriˇcnog novˇci´ca \n
ili popularno nazvanu igru “pismo-grb”. \n
otvorena binarno
otvorena
tekstualno
koristi standardnu biblioteku lakˇse je prenosiv na druge sustave. Nasuprot
tome funkcije operacijskog sustava pruˇzaju programeru viˇse funkcionalnosti,
no takav kˆ od nije prenosiv na druge operacijske sustave. Mi ´cemo u ovom
poglavlju opisati samo neke funkcije iz standardne biblioteke.
13.1.3 Standardne datoteke
C automatski otvara tri datoteke za korisnika. To su standardni ulaz,
standardni izlaz i standardni izlaz za greˇske. Standardni ulaz je povezan s
tastaturom, a standarni izlaz i izlaz za greˇske s ekranom. Istaknimo da C
tastaturu i ekran tretira kao datoteke budu´ci da datoteka predstavlja kon-
ceptualno naprosto ulazni ili izlazni niz oktetat. Na nekim operacijskim
sustavima (Unix, DOS,..) mogu´ce je standardni ulaz, izlaz i izlaz za greˇske
preusmjeriti.
216 POGLAVLJE 13. DATOTEKE
13.2 Otvaranje i zatvaranje datoteke
FILE *fopen(const char *ime, const char *tip)
int fclose(FILE *fp)
Komunikacija s datotekama vrˇsi se preko spremnika (buffer) u koji
se privremeno pohranjuju informacije koje se ˇsalju u datoteku. Svrha je
spremnika smanjiti komunikaciju s vanjskom memorijom (diskom) i tako
pove´cati efikasnost ulazno-izlaznih funkcija. U datoteci <stdio.h> dekla-
rirana je struktura FILE koja sadrˇzi sve podatke potrebne za komunikaciju
s datotekama (ukljuˇcuju´ci veliˇcinu i poloˇzaj spremnika). Program koji ˇzeli
otvariti datoteku mora prvo deklarirati pokazivaˇc na FILE kao u ovom pri-
mjeru:
FILE *ptvar;
Datoteka mora biti otvorena pomo´cu funkcije fopen prije prve operacije pi-
sanja ili ˇcitanja. Tipiˇcno se fopen koristi na sljede´ci naˇcin:
ptvar=fopen(ime,tip);
if(ptvar==NULL)
{
printf("Poruka o gresci"):
......
}
gdje je ime ime datoteke koja se otvara, a tip jedan od sljede´cih stringova:
tip Znaˇcenje
"r" Otvaranje postoje´ce datoteke samo za ˇcitanje
"w" Kreiranje nove datoteke samo za pisanje.
"a" Otvaranje postoje´ce datoteke za dodavanje teksta.
"r+" Otvaranje postoje´ce datoteke za ˇcitanje i pisanje.
"w+" Kreiranje nove datoteke za ˇcitanje i pisanje.
"a+" Otvaranje postoje´ce datoteke za ˇcitanje i dodavanje teksta.
Pri tome treba znati da:
• Ako se postoje´ca datoteka otvori s ”w” ili ”w+” njen sadrˇzaj ´ce biti
izbrisan i pisanje ´ce poˇceti od poˇcetka.
• Ako datoteka koju otvaramo s tipom ”a” ili ”a+” ne postoji bit ´ce
kreirana.
13.2. OTVARANJE I ZATVARANJE DATOTEKE 217
• Ako se datoteka otvara s tipom ”a” ili ”a+” novi tekst ´ce biti dodavan
na kraju datoteke (”a” dolazi od eng. append).
Pomo´cu gornjih oznaka tipova datoteka ´ce biti otvorena u tekstualnom
modu. Da bi se otvorila u binarnom modu treba treba svakom tipu dodati
b. Time dolazimo do tipova
• "rb", "wb", "ab" = binarno ˇcitanje, pisanje, dodavanje;
• "rb+" ili "r+b" = binarno ˇcitanje/pisanje (otvaranje);
• "wb+" ili "w+b" = binarno ˇcitanje/pisanje (kreiranje);
• "ab+" ili "a+b" = binarno dodavanje.
Funkcija fopen vra´ca pokazivaˇc na strukturu FILE povezanu s datotekom
ili NULL ako datoteka nije mogla biti otvorena.
Na kraju programa datoteka treba biti zatvorena funkcijom fclose koja
uzima kao argument pokazivaˇc na spremnik:
fclose(ptvar);
Na primjer, otvaranje i zatvaranje datoteke primjer.dat izgledalo bi
ovako:
#include <stdio.h>
.......
FILE *fpt;
if((fpt=fopen("primjer.dat","w"))==NULL)
printf("\nGRESKA: Nije moguce otvoriti datoteku.\n");
.....
fclose(fpt);
Funkcija fclose vra´ca nulu ako je datoteka uspjeˇsno zatvorena te EOF u
sluˇcaju greˇske.
13.2.1 Standardne datoteke
stdin, stdout, stderr
U datoteci <stdio.h> definirani su konstantni pokazivaˇci na FILE
strukturu povezanu sa standardnim ulazom, izlazom i izlazom za greˇske. Ti
pokazivaˇci imaju imena stdin (standardni ulaz), stdout (standardni izlaz)
i stderr (standardni izlaz za greˇske).
218 POGLAVLJE 13. DATOTEKE
13.3 Funkcije za ˇcitanje i pisanje
Funkcije getchar, putchar, gets, puts, printf i scanf imaju ana-
logne verzije getc, putc, fgets, fputs, fprintf i fscanf koje rade s da-
totekama. Sve te funkcije kao prvi argument uzimaju pokazivaˇc na spremnik
(pokazivaˇc na FILE) povezan s datotekom.
13.3.1
ˇ
Citanje i pisanje znak po znak
Funkcije
int getc(FILE *fp)
int fgetc(FILE *fp)
vra´caju sljede´ci znak iz datoteke na koju pokazuje fp. Razlika izmedu getc
i fgetc je u tome da getc moˇze biti implementirana kao makro naredba dok
fgetc ne smije. U sluˇcaju greˇske ili kraja datoteke vra´ca se EOF. To je razlog
ˇsto je tip vra´cene vrijednosti int umjesto char. Funkcija getchar() koju
smo uveli u Poglavlju 5 implementira se kao getc(stdin).
Program u sljede´cem primjeru broji znakove u datoteci koja je navedena
kao argument komandne linije.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int ch,count=0;
FILE *fpt;
if(argc==1){ /* ime datoteke nedostaje */
printf("\nUporaba: %s ime_datoteke\n",argv[0]);
exit(-1);
}
else if((fpt=fopen(argv[1],"r"))==NULL){
printf("Ne mogu otvoriti datoteku %s\n",argv[1]);
exit(-1);
}
while((ch=fgetc(fpt))!=EOF) count++;
fclose(fpt);
printf("\nBroj znakova = %d\n",count);
13.3. FUNKCIJE ZA
ˇ
CITANJE I PISANJE 219
return 0;
}
Znak proˇcitan sa getc ili fgetc moˇze biti vra´cen u datoteku pomo´cu
funkcije
int ungetc(int c, FILE *fp);
Prvi argument je znak koji vra´camo. Za viˇse detalja vidi [7].
Znak moˇze biti upisan u datoteku pomo´cu funkcija
int putc(int c, FILE *fp)
int fputc(int c, FILE *fp)
Ponovo, razlika izmedu putc i fputc je u tome ˇsto putc moˇze biti imple-
mentirana kao makro naredba dok fputc ne smije. Funkcije vra´caju ispisani
znak ili EOF ako je doˇslo do greˇske prilikom ispisa. Funkcija putchar(c) iz
Poglavlja 5 definirana je kao putc(c,stdout).
Funkcija koja kopira jednu datoteku u drugu moˇze biti napisana na
sljede´ci naˇcin:
void cpy(FILE *fpulaz, FILE *fpizlaz) {
int c;
while((c=getc(fpulaz))!=EOF)
putc(c,fpizlaz);
}
Pomo´cu te funkcije moˇzemo napisati program cat koji uzima imena datoteka
i ispisuje datoteke na standardnom izlazu u poretku u kojem su njihova imena
navedena. Ukoliko se ne navede niti jedno ime, onda se standardni ulaz kopira
na standardni izlaz.
#include <stdio.h>
void cpy(FILE *, FILE *);
int main(int argc, char *argv[]) {
FILE *fpt;
if(argc==1) /* ime datoteke nedostaje */
cpy(stdin,stdout);
else
while(--argc>0)
220 POGLAVLJE 13. DATOTEKE
if((fpt=fopen(*++argv,"r"))==NULL){
printf("cat: ne mogu otvoriti datoteku %s\n",*argv);
exit(1);
}
else {
cpy(fpt,stdout);
fclose(fpt);
}
return 0;
}
Uoˇcimo da smo u programu posve izbjegli upotrebu brojaˇca time ˇsto inkre-
mentiramo i dekrementiramo argc i argv.
13.3.2
ˇ
Citanje i pisanje liniju po liniju
Funkcija koja uˇcitava podatke iz datoteke liniju-po-liniju je
char *fgets(char *buf, int n, FILE *fp);
Prvi argument je pokazivaˇc na dio memorije (eng. buffer) u koji ´ce ulazna
linija biti spremljena, a drugi je veliˇcina memorije na koju prvi argument
pokazuje. Tre´ci argument je pokazivaˇc na datoteku iz koje se uˇcitava.
Funkcija ´ce proˇcitati liniju ukljuˇcuju´ci i znak za prijelaz u novi red i na
kraj ´ce dodati nul znak ’\0’. Pri tome ´ce biti uˇcitano najviˇse n-1 znakova,
ukljuˇcivˇsi ’\n’. Ukoliko je ulazna linija dulja od toga, ostatak ´ce biti proˇcitan
pri sljede´cem pozivu funkcije fgets.
Funkcija vra´ca buf ako je sve u redu ili NULL ako se doˇslo do kraja datoteke
ili se pojavila greˇska.
Funkcija
char *gets(char *buf);
uvedena u Poglavlju 5 ˇcita uvjek sa standardnog ulaza. Ona ne uzima veliˇcinu
buffera kao argument i stoga se moˇze desiti da je ulazna linija ve´ca od me-
morije koja je za nju rezervirana. Stoga je bolje umjesto gets(buf) koristiti
fgetf(buf,n,stdin). Pri tome treba uzeti u obzir razliku da fgets uˇcitava
i znak ’\n’, dok gets znak za prijelaz u novi red uˇcitava, ali na njegovo
mjesto stavlja ’\0’.
Funkcija za ispis podataka u datoteku, liniju-po-liniju je
13.3. FUNKCIJE ZA
ˇ
CITANJE I PISANJE 221
int fputs(const char *str, FILE *fp);
Funkcija vra´ca nenegativnu vrijednost ako je ispis uspio ili EOF u sluˇcaju
greˇske.
fputs ispisuje znakovni niz na koji pokazuje str u datoteku na koju
pokazuje fp. Zadnji nul znak ne´ce biti ispisan i znak za prijelaz u novi red
ne´ce biti dodan.
Funkcija
int puts(const char *str);
ispisuje znakovni niz na koji pokazuje str na standardni izlaz. Na kraj niza
ona dodaje znak za prijelaz u novi red ˇsto ju razlikuje od fputs(str,stdout).
13.3.3 Prepoznavanje greˇske
Budu´ci da ulazne funkcije vra´caju EOF i u sluˇcaju kada je doˇslo do greˇske
i u sluˇcaju kada se naide na kraj datoteke postoje funkcije
int ferror(FILE *fp);
int feof(FILE *fp);
koje sluˇze za razlikovanje izmedu pojedinih situacija. ferror vra´ca broj
razliˇcit od nule (istina) ako je doˇslo do greˇske, a nulu (laˇz) ako nije. Funkcija
feof vra´ca broj razliˇcit od nule (istina) ako smo doˇsli do kraja datoteke, a
nulu (laˇz) u suprotnom.
Sljede´ci program kopira standardni ulaz na standardni izlaz i detektira
ulaznu greˇsku pomo´cu funkcije ferror.
#include <stdio.h>
#define MAXLINE 128
int main(void) {
char buf[MAXLINE];
while(fgets(buf,MAXLINE,stdin) != NULL)
if(fputs(buf,stdout)==EOF) {
fprintf(stderr,"\nIzlazna greska.\n");
exit(1);
}
if(ferror(stdin)) {
222 POGLAVLJE 13. DATOTEKE
fprintf(stderr,"\nUlazna greska.\n");
exit(2);
}
return 0;
}
13.3.4 Formatirani ulaz/izlaz
Za formatirani ispis u datoteku i upis iz datoteke moˇzemo koristiti funkcije
int fprintf(FILE *fp, const char *format,...);
int fscanf(FILE *fp, const char *format,...);
One su identiˇcne funkcijama printf i scanf s jedinom razlikom da im je prvi
argument pokazivaˇc na datoteku u koju se vrˇsi upis odn. iz koje se ˇcita.
fscanf vra´ca broj uˇcitanih objekata ili EOF ako je doˇslo do greˇske ili kraja
datoteke. fprintf vra´ca broj upisanih znakova ili negativan broj u sluˇcaju
greˇske. Konaˇcno, printf(...) je ekvivalentno s fprintf(stdout,...), a
scanf(...) je ekvivalentno s fscanf(stdin,...).
13.3.5 Binarni ulaz/izlaz
Za zapisivanje struktura mogu se pokazati pogodnije sljede´ce funkcije:
size t fread(void *ptr, size t size, size t nobj, FILE *fp);
size t fwrite(const void *ptr, size t size, size t nobj, FILE *fp);
Ove funkcije vrˇse binarno ˇcitanje i pisanje. To znaˇci da ne dolazi do konverzije
podataka iz binarnog zapisa, u kojem se ono nalaze u raˇcunalu, u znakovni
zapis koji se moˇze ˇcitati.
Funkcije fread ˇcita iz datoteke na koju pokazuje fp nobj objekata di-
menzije size i smjeˇsta ih u polje na koje pokazuje ptr. Vrijednost koju
funkcija vra´ca je broj uˇcitanih objekata. Taj broj moˇze biti manji od nobj
ako je doˇslo do greˇske ili kraja datoteke. Da bi se ispitao uzrok treba koristiti
funkcije ferror i feof.
Funkcije fwrite upisuje u datoteke na koju pokazuje fp nobj objekata
dimenzije size iz polja na koje pokazuje ptr. Vrijednost koju funkcija vra´ca
je broj upisanih objekata. Taj broj moˇze biti manji od nobj ako je doˇslo do
greˇske.
13.3. FUNKCIJE ZA
ˇ
CITANJE I PISANJE 223
Primjer upotrebe je zapis strukture u datoteku dan je u sljede´cem pro-
gramu.
#include <stdio.h>
#include <stdlib.h>
#define SIZE 2
struct account{
short count;
long total;
char name[64];
};
int main(void)
{
struct account lista[SIZE]={{3,1000L,"Ivo"},
{7,2000L,"Marko"}};
FILE *fp;
int i;
if((fp=fopen("1.dat","wb"))==NULL){
fprintf(stderr,"Error: nije moguce otvoriti"
" datoteku 1.dat.\n");
exit(-1);
}
for(i=0;i<SIZE;++i){
if(fwrite(&lista[i],sizeof(lista[i]), 1, fp) != 1){
fprintf(stderr,"Greska pri upisu u 1.dat\n");
exit(1);
}
}
fclose(fp);
return EXIT_SUCCESS;
}
Program u jednoj for petlji upisuje listu struktura account u datoteku.
Sadrˇzaj datoteke moˇze biti izlistan sljede´com funkcijom.
void Print(const char *file, int n)
{
224 POGLAVLJE 13. DATOTEKE
FILE *fp;
struct account elm;
int i;
if((fp=fopen(file,"rb"))==NULL){
fprintf(stderr,"Read, error: nije moguce otvoriti"
" datoteku %s.\n",file);
exit(-1);
}
for(i=0;i<n;++i){
if(fread(&elm,sizeof(elm), 1, fp) != 1){
fprintf(stderr,"Greska pri citanju iz %s\n",file);
exit(1);
}
printf("%d : count = %d, total = %ld, name = %s\n",
i, elm.count, elm.total, elm.name);
}
fclose(fp);
return;
}
Poziv funkcije bi bio npr. oblika Print("1.dat",2);
Osnovni nedostatak funkcija za binarni ulaz/izlaz je zavisnost o arhitek-
turi raˇcunala i prevodiocu. Naime, na razliˇcitim raˇcunalima binarni zapis iste
strukture podataka moˇze biti razliˇcit ˇsto onemogu´cava da podaci zapisani na
jednom stroju budu ispravno proˇcitani na drugom. Prednosti su brzina i
kompaktnost zapisa.
13.3.6 Direktan pristup podacima
Pomo´cu do sada uvedenih funkcija podacima moˇzemo pristupiti samo
sekvencijalno. Sljede´ce dvije funkcije nam omogu´cavaju direktan pristup.
int fseek(FILE *fp, long offset, int pos);
long ftell(FILE *fp);
Funkcija ftell uzima kao argument pokazivaˇc na otvorenu datoteku i
vra´ca trenutni poloˇzaj unutar datoteke. Kod binarnih datoteka to je broj
znakova koji prethode trenutnom poloˇzaju unutar datoteke, dok je kod teks-
tualne datoteke ta veliˇcina ovisna o implementaciji. U svakom sluˇcaju izlaz iz
13.3. FUNKCIJE ZA
ˇ
CITANJE I PISANJE 225
funkcije ftell mora dati dobar drugi argument za funkciju fseek. U sluˇcaju
greˇske, ftell ´ce vratiti vrijednost -1L.
Funkcija fseek uzima pokazivaˇc na otvorenu datoteku, te dva parame-
tra koji specificiraju poloˇzaj u datoteci. Trce´ci parametar pos indicira od
kog mjesta se mjeri offset (odmak). U datoteci stdio.h su definirane tri
simboliˇcke konstante: SEEK SET, SEEK CUR i SEEK END, koje imaju sljede´ce
znaˇcenje:
pos Nova pozicija u datoteci
SEEK SET offset znakova od poˇcetka datoteke
SEEK CUR offset znakova od trenutne pozicije u datoteci
SEEK END offset znakova od kraja datoteke
Na primjer,
Poziv Znaˇcenje
fseek(fp, 0L, SEEK SET) Idi na poˇcetak datoteke
fseek(fp, 0L, SEEK END) Idi na kraj datoteke
fseek(fp, 0L, SEEK CUR) Ostani na teku´coj poziciji
fseek(fp, ftell pos, SEEK SET) Idi na poziciju je dao prethodni
poziv ftell() funkcije
Standard postavlja neka ograniˇcenja na funkciju fseek. Tako za binarne
datoteke SEEK END nije dobro definiran, dok za tekstualne datoreke jedino su
pozivi iz prethodne tabele dobro definirani.
Napiˇsimo sada funkciju koja ´ce u datoteci potraˇziti odredeni account i
pove´cati njegovo polje total za 100, ako je ono manje od 5000.
void Povecaj(const char *file, int n)
{
FILE *fp;
struct account tmp;
long pos;
int size=sizeof(struct account);
if((fp=fopen(file,"r+b"))==NULL){
fprintf(stderr,"Povecaj, error: nije moguce otvoriti"
" datoteku %s.\n",file);
exit(-1);
}
pos = n*size;
fseek(fp, pos, SEEK_SET);
226 POGLAVLJE 13. DATOTEKE
if(fread(&tmp, size, 1, fp) != 1){
fprintf(stderr,"Greska pri citanju.\n");
exit(EXIT_FAILURE);
}
if(tmp.total < 5000L) {
tmp.total += 100;
fseek(fp, -size, SEEK_CUR);
if(fwrite(&tmp, size, 1, fp) != 1){
fprintf(stderr,"Greska pri upisu.\n");
exit(1);
}
}
fclose(fp);
return;
}
Funkcija mijenja sadrˇzaj n+1-og zapisa (brojanje zapisa poˇcinje od nule).
Prvo se otvara datoteka za ˇcitanje i upisivanje (r+b), a zatim se pokazivaˇc
pozicionira na poˇcetak n+1-og zapisa (fseek(fp, pos, SEEK SET);). Zapis
se proˇcita i ako je total manji od 5000 poveˇcava se za 100. Nakon ˇsto je
zapis promijenjen, treba ga upisati nazad u datoteke. U tu svrhu se prvo
vra´camo na poˇcetak n+1-og zapisa (fseek(fp, -size, SEEK CUR);) i onda
vrˇsimo upis.
Poglavlje 14
Operacije nad bitovima
14.1 Operatori
Programski jezik C ima jedan broj operatora ˇcije je djelovanje defini-
rano na bitovima. Takvi se operatori mogu primijeniti na cjelobrojne tipove
podataka char, short, int i long. To su sljede´ci operatori
Operator Znaˇcenje
& logiˇcko I bit-po-bit
| logiˇcko ILI bit-po-bit
^ ekskluzivno logiˇcko ILI bit-po-bit
<< lijevi pomak
>> desni pomak
∼ 1-komplement
Prve tri operacije &, | i ^ uzimaju dva operanda i vrˇse operacije na
bitovima koji se nalaze na odgovaraju´cim mjestima. Usporeduju se bitovi na
najmanje znaˇcajnom mjestu u oba operanda, zatim na sljede´cem najmanje
znaˇcajnom mjestu itd. Definicije operacija dane su u sljede´coj tabeli:
b1 b2 b1 & b2 b1 ^ b2 b1 | b2
1 1 1 0 1
1 0 0 1 1
0 1 0 1 1
0 0 0 0 0
Operacije ilustriramo s nekoliko promjera. Logiˇcko I:
a = 0100 0111 0101 0111
b = 1101 0100 1010 1001
a & b = 0100 0100 0000 0001
227
228 POGLAVLJE 14. OPERACIJE NAD BITOVIMA
Logiˇcko ILI:
a = 0100 0111 0101 0111
b = 1101 0100 1010 1001
a | b = 1101 0111 1111 1111
Ekskluzivno logiˇcko ILI:
a = 0100 0111 0101 0111
b = 1101 0100 1010 1001
a ^ b = 1001 0011 1111 1110
Logiˇcki operatori najˇceˇs´ce sluˇze maskiranju pojedinih bitova u operandu.
Maskiranje je transformacija binarnog zapisa u varijabli na odreden naˇcin.
Na primjer, pretpostavimo da ˇzelimo ˇsest najmanje znaˇcajnih bitova iz vari-
jable a kopirati u varijablu b, a sve ostale bitove varijable b staviti na nulu.
To moˇzemo posti´ci pomo´cu logiˇckog I operatora. Pretpostavimo da su vari-
jable tipa int i da je int kodiran u 16 bitova. Tada prvo definiramo masku,
konstantu koja ima sljede´ci raspored bitova:
mask = 0000 0000 0011 1111
Odmah se vidi da je mask=0x3f. Operacija koju trebamo napraviti je
a = 0100 0111 0101 0111
mask = 0000 0000 0011 1111
a & mask = 0000 0000 0001 0111
i stoga imamo b=a & 0x3f. Uoˇcimo da je maska neovisna o broju bitova
koji se koristi za tip int. Naravno, da smo htjeli izdvojiti dio najznaˇcjnijih
bitova (lijevo pozicioniranih) morali bismo precizno znati duljinu tipa int.
Da bismo izdvojili najznaˇcajnijih ˇsest bitova, a ostale postavili na nulu morali
bismo izvrˇsiti operaciju b=a & 0xfc00 (provjerite), ali taj naˇcin je zavisan o
ˇsirini tipa int.
Logiˇcko I takoder sluˇzi postavljanjem na nulu odredenih bitova. Ako
u nekoj varijabli a ˇzelimo postaviti na nulu neki bit, dovoljno je napraviti
logiˇcko I s konstantom koja na traˇzenom mjestu ima nulu, a na svim ostalim
jedinice. Na primjer,
a = 0100 0111 0101 0111
mask = 1111 1101 1111 1111
a & mask = 0100 0101 0101 0111
Ovdje smo deseti bit postavili na nulu.
Logiˇcko ILI na sliˇcan naˇcin moˇze posluˇziti maskiranju bitova. Njega ´cemo
iskoristiti kada ˇzelimo iz jedne varijable u drugu kopirati dio bitova, a sve
ostale postaviti na jedan. Na primjer, ako ˇzelimo izdvojiti ˇsest najmanje
znaˇcajnih bitova trebamo napraviti
a = 0100 0111 0101 0111
mask = 1111 1111 1100 0000
a | mask = 1111 1111 1101 0111
14.1. OPERATORI 229
i stoga imamo b=a | 0xffa. Ova operacija ovisi o duljini tipa int (odn.
onog tipa koji se koristi) no to se moˇze izbje´ci pomo´cu 1-komplementa.
Unarni operator 1-komplement (∼) djeluje tako da jedinice pretvara u
nule u nule u jedinice:
~0101 1110 0001 0101 = 1010 0001 1110 1010
odnosno, ∼0x5e15=0xa1ea.
U prethodnom primjeru bismo stoga pisali b=a | ~0x3f.
Logiˇcko ILI evidentno moˇzemo koristiti za stavljanje pojedinih bitova na
jedan.
Ekskluzivno ILI moˇzemo koristiti za postavljanje odredenih bitova na 1
ako su bili 0 i obratno. Na primjer,
a = 0100 0111 0101 0111
mask = 0000 0000 0011 0000
a ^ mask = 0100 0111 0110 0111
Vidimo da je rezultat da su bitovi 5 i 6 promijenili svoju vrijednost. Ako
ponovimo operaciju
a = 0100 0111 0110 0111
mask = 0000 0000 0011 0000
a ^ mask = 0100 0111 0101 0111
dolazimo do polazne vrijednosti.
Operatori pomaka pomi´cu binarni zapis broja nalijevo ili nadesno. Ope-
rator pomaka nalijevo << djeluje na sljede´ci naˇcin: prvi operand mora biti
cjelobrojni tip nad kojim se operacija vrˇsi, a drugi operand je broj bitova za
koji treba izvrˇsiti pomak (unsigned int). Drugi operand ne smije premaˇsiti
broj bitova u lijevom operandu. Na primjer, b=a<<6 ima sljede´ci efekt:
a = 0110 0000 1010 1100
a << 6 = 0010 1011 0000 0000
Sve se bitovi pomi´cu za 6 mjesta ulijevo. Pri tome se 6 najznaˇcajnijih bitova
gubi, a sa desne strane se mjesta popunjavaju nulama.
Pomak nadesno >> takoder djeluje nad prvim operandom dok je drugi
operand broj bitova za koji treba izvrˇsiti pomak. Bitovi na desnoj strani
(najmanje znaˇcajni) pri tome se gube, dok se na desnoj strani uvode novi
bitovi. Pri tome, ako je varijabla na kojoj se pomak vrˇsi tipa unsigned, onda
se na lijevoj strani uvode nule. Na primjer
a = 0110 0000 1010 1100
a >> 6 = 0000 0001 1000 0010
230 POGLAVLJE 14. OPERACIJE NAD BITOVIMA
Ako je varjabla a cjelobrojni tip s predznakom onda rezultat moˇze ovisiti o
implementaciji. ve´cina prevodioca ´ce na lijevoj strani uvesti bit predznaka
broja. To znaˇci da ´ce za negativan broj ispraˇznjeni bitovi na lijevoj strani
biti popunjavani jedinicama, a za pozitivan broj nulama. S druge strane,
neki prevodioci ´ce uvijek popunjavati ispraˇznjena mjesta nulama.
Logiˇcki operatori uvedeni u ovom poglavlju formiraju operatore pridruˇzivanaja
&= ^= |= <<= >>=
Neka je a=0x6db7. Tada je
izraz ekvivalentan izraz vrijednost
a &= 0x7f a = a & 0x7f 0x37
a ^= 0x7f a = a ^ 0x7f 0x6dc8
a |= 0x7f a = a | 0x7f 0x6dff
a <<= 5 a = a << 5 0xb6e0
a >>= 5 a = a >> 5 0x36d
Na kraju pokaˇzimo program koji ispisuje binarni zapis cijelog broja tipa
int.
#include <stdio.h>
int main(void) {
int a,b,i,nbits;
unsigned mask;
nbits=8*sizeof(int); /* duljina tipa int */
mask=0x1 << (nbits-1); /* 1 na najznacajnijem mjestu*/
printf("\nUnesite cijeli broj: "); scanf("%d",&a);
for(i=1;i<=nbits;++i) {
b=(a & mask) ? 1 : 0;
printf("%d",b);
if(i % 4 == 0) printf(" ");
mask >>= 1;
}
printf("\n");
return 0;
}
Program prvo odredi duljinu tipa int pomo´cu sizeof operatora i zatim
inicijalizira unsigned varijablu mask tako da bit 1 stavi na najznaˇcajnije
mjesto, a sve ostale bitove stavi na nulu.
14.2. POLJA BITOVA 231
Operacija (a & mask) ? 1 : 0 dat ´ce 1 ako je na maskiranom mjestu
bit jednak 1, odnosno nulu ako je maskirani bit 0. U naredbi mask >>= 1;
maskirni bit pomi´cemo s najznaˇcajnijeg mjesta prema najmanje znaˇcajnom
i tako ispitujemo sve bitove u varijabli.
14.2 Polja bitova
Polja bitova nam omogu´cuju rad s pojedinim bitovima unutar jedne
kompljutorske rijeˇci. Deklaracija polja bitova posve je sliˇcna deklaraciji
strukture:
struct ime {
clan 1;
clan 2;
.....
clan n;
};
s tom razlikom da ˇclanovi strukture imaju drugaˇcije znaˇcenje nego u obiˇcnoj
strukturi. Svaki ˇclan polja bitova predstavlja jedno polje bitova unutar kom-
pjutorske rijeˇci. Sintaksa je takva da iz imena varijable dolazi dvotoˇcka i
broj bitova koji ˇclan zauzima. Na primjer,
struct primjer {
unsigned a : 1;
unsigned b : 3;
unsigned c : 2;
unsigned d : 1;
};
struct primjer v;
Prva deklaracija definira strukturu razbijenu u ˇcetiri polja bitova, a, b, c
i d. Ta polja imaju duljinu 1, 3, 2 i 1 bit. Prema tome zauzimaju 7 bitova.
Poredak tih bitova unutar jedne kompjutorske rijeˇci ovisi o implementaciji.
Pojedine ˇclanove polja bitova moˇzemo dohvatiti istom sintaksom kao i
kod struktura, dakle v.a, v.b itd.
Ako broj bitova deklariran u polju bitova nadmaˇsuje jednu kompjutorsku
rijeˇc, za pam´cenje polja bit ´ce upotrebljeno viˇse kompjutorskih rijeˇci.
Jednostavan program koji upotrebljava polje bitova je sljede´ci:
#include <stdio.h>
232 POGLAVLJE 14. OPERACIJE NAD BITOVIMA
int main(void)
{
static struct{
unsigned a : 5;
unsigned b : 5;
unsigned c : 5;
unsigned d : 5;
} v={1,2,3,4};
printf("v.a=%d, v.b=%d, v.c=%d, v.d=%d\n",v.a,v.b,v.c,v.d);
printf("v treba %d okteta\n", sizeof(v));
return 0;
}
Poredak polja unutar rijeˇci moˇze se kotrolirati pomo´cu neimenovanih
ˇclanova unutar polja kao u primjeru
struct {
unsigned a : 5;
unsigned b : 5;
unsigned : 5;
unsigned c : 5;
};
struct primjer v;
Neimenovani ˇclan ˇcija je ˇsirina deklarirana kao 0 bitova tjera prevodilac
da sljede´ce polje smjesti u sljede´cu raˇcunalnu rijeˇc. Na primjer,
#include <stdio.h>
int main(void)
{
static struct{
unsigned a : 5;
unsigned b : 5;
unsigned : 0;
unsigned c : 5;
} v={1,2,3};
printf("v.a=%d, v.b=%d, v.c=%d\n",v.a,v.b,v.c);
printf("v treba %d okteta\n", sizeof(v));
return 0;
}
14.2. POLJA BITOVA 233
Polja bitova mogu se koristiti kao i druge strukture s odredenim ograni-
ˇcenjima.
ˇ
Clanovi mogu biti tipa unsigned i tipa int. Adresni operator se
ne moˇze primijeniti na ˇclana polja bitova niti se ˇclan moˇze dose´ci pomo´cu
pokazivaˇca.
Dodatak A
A.1 ANSI zaglavlja
<assert.h> <inttypes.h> <signal.h> <stdlib.h>
<complex.h> <iso646.h> <stdarg.h> <string.h>
<ctype.h> <limits.h> <stdbool.h> <tgmath.h>
<errno.h> <locale.h> <stddef.h> <time.h>
<fenv.h> <math.h> <stdint.h> <wchar.h>
<float.h> <setjmp.h> <stdio.h> <wctype.h>
234
A.2. ASCII ZNAKOVI 235
A.2 ASCII znakovi
Char Dec | Char Dec | Char Hex | Char Dec
------------------------------------------------
(nul) 0 | (sp) 32 | @ 64 | ‘ 96
(soh) 1 | ! 33 | A 65 | a 97
(stx) 2 | " 34 | B 66 | b 98
(etx) 3 | # 35 | C 67 | c 99
(eot) 4 | $ 36 | D 68 | d 100
(enq) 5 | % 37 | E 69 | e 101
(ack) 6 | & 38 | F 70 | f 102
(bel) 7 | ’ 39 | G 71 | g 103
(bs) 8 | ( 40 | H 72 | h 104
(ht) 9 | ) 41 | I 73 | i 105
(nl) 10 | * 42 | J 74 | j 106
(vt) 11 | + 43 | K 75 | k 107
(np) 12 | , 44 | L 76 | l 108
(cr) 13 | - 45 | M 77 | m 109
(so) 14 | . 46 | N 78 | n 110
(si) 15 | / 47 | O 79 | o 111
(dle) 16 | 0 48 | P 80 | p 112
(dc1) 17 | 1 49 | Q 81 | q 113
(dc2) 18 | 2 50 | R 82 | r 114
(dc3) 19 | 3 51 | S 83 | s 115
(dc4) 20 | 4 52 | T 84 | t 116
(nak) 21 | 5 53 | U 85 | u 117
(syn) 22 | 6 54 | V 86 | v 118
(etb) 23 | 7 55 | W 87 | w 119
(can) 24 | 8 56 | X 88 | x 120
(em) 25 | 9 57 | Y 89 | y 121
(sub) 26 | : 58 | Z 90 | z 122
(esc) 27 | ; 59 | [ 91 | { 123
(fs) 28 | < 60 | \ 92 | | 124
(gs) 29 | = 61 | ] 93 | } 125
(rs) 30 | > 62 | ^ 94 | ~ 126
(us) 31 | ? 63 | _ 95 | (del) 127
236 DODATAK A.
A.3 Matematiˇcke funkcije
double cos(double x) -- cos(x)
double sin(double x) -- sin(x)
double tan(double x) -- tg(x)
double acos(double x) -- arccos(x)
double asin(double x) -- arcsin(x)
double atan(double x) -- arctg(x)
double atan2(double y, double x) -- arctg(y/x)
double cosh(double x) -- ch(x)
double sinh(double x) -- sh(x)
double tanh(double x) -- th(x)
double exp(double x) -- e^x
double pow (double x, double y) -- x^y.
double log(double x) -- ln(x).
double log10 (double x ) -- log(x)
double fabs (double x ) -- |x|
labs(long n) -- |n|
double sqrt(double x) -- korijen
double ceil(double x) -- Najmanji cijeli broj veci od x
double floor(double x) -- Najveci cijeli broj manji od x
Bibliografija
[1] C-faq, http://www.eskimo.com/ scs/C-faq/top.html.
[2] B. S. Gottfried, Theory and Problems of Programming with C, Schaum’s
outline series, McGraw-Hill, 1996.
[3] S. P. Harbison III, G. L. Steele Jr., C, A Reference Manual, Fifth Edi-
tion, Prentice Hall, 2002.
[4] B. W. Kernighan, D. M. Ritchie, The C Programming Language, 2nd
Edition, 1988.
[5] W. H. Murray, III, C. H. Pappas, 8036/8026 Assembly Language Pro-
gramming, Osborne McGraw-Hill, Berkeley, 1986.
[6] S. Prata, C Primer Plus, 4th edition, SAMS, 2002.
[7] W. R. Stevens, Advanced Programming in the UNIX Enviroment, Ad-
dison Wesley, 1992.
237

Sadrˇaj z
1 Uvod 1.1 Programski jezici . . . . . . . . . . . . . . . 1.2 Osnove pisanja programa u Unix okruˇenju z 1.2.1 Editor teksta . . . . . . . . . . . . . 1.2.2 Compiler . . . . . . . . . . . . . . . . 1.2.3 man . . . . . . . . . . . . . . . . . . 1.2.4 Debugger . . . . . . . . . . . . . . . 1.2.5 Linker . . . . . . . . . . . . . . . . . 1.2.6 make . . . . . . . . . . . . . . . . . . 1.2.7 Programske biblioteke . . . . . . . . 2 Osnove programskog jezika C 2.1 Prvi program . . . . . . . . 2.2 Varijable. while petlja . . . 2.3 for petlja . . . . . . . . . . 2.4 if naredba . . . . . . . . . 2.5 Funkcije . . . . . . . . . . . 2.5.1 Funkcija main . . . . 2.5.2 Prototip funkcije . . 2.6 Polja . . . . . . . . . . . . . 2.6.1 <math.h> . . . . . . 2.7 Pokazivaˇi . . . . . . . . . . c 2.8 Polja znakova . . . . . . . . 3 Konstante i varijable 3.1 Znakovi . . . . . . . . . . . 3.2 Komentari . . . . . . . . . . 3.3 Identifikatori i kljuˇne rijeˇi c c 3.4 Osnovni tipovi podataka . . 3.4.1 Cjelobrojni podaci . 3.4.2 Znakovni podaci . . . . . . . . . . . . . . . . . . . 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 8 10 10 10 11 11 12 12 12 13 13 16 20 22 26 30 30 31 36 37 41 46 46 46 47 49 49 52

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . .

ˇ SADRZAJ 3.4.3 Logiˇki podaci . . . c 3.4.4 Realni podaci . . . 3.4.5 Kompleksni podaci Varijable i deklaracije . . . 3.5.1 Polja . . . . . . . . 3.5.2 Pokazivaˇi . . . . . c Konstante . . . . . . . . . Inicijalizacija varijabli . . Enumeracije . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3 53 53 55 55 56 57 59 63 64 66 66 67 68 69 70 71 72 74 75 76 78 78 80 81 82 83 85 85 86 87 88 89 90 91 92 93 94 95

3.5

3.6 3.7 3.8

4 Operatori i izrazi 4.1 Aritmetiˇki operatori . . . . . . . . . . . c 4.1.1 Konverzije . . . . . . . . . . . . . 4.1.2 Prioriteti i asocijativnost . . . . . 4.2 Unarni operatori . . . . . . . . . . . . . 4.2.1 Inkrementiranje, dekrementiranje 4.2.2 sizeof operator . . . . . . . . . . 4.3 Relacijski i logiˇki operatori . . . . . . . c 4.4 Operatori pridruˇivanja . . . . . . . . . . z 4.5 Uvjetni operator : ? . . . . . . . . . . 4.6 Prioriteti i redoslijed izraˇunavanja . . . c 5 Ulaz i izlaz podataka 5.1 Funkcije getchar i putchar . . . . . . . 5.2 Funkcije iz datoteke <ctype.h> . . . . . 5.3 Funkcije gets i puts . . . . . . . . . . . 5.4 Funkcija scanf . . . . . . . . . . . . . . 5.4.1 Uˇitavanje cijelih brojeva . . . . . c 5.4.2 Uˇitavanje realnih brojeva . . . . c 5.4.3 Drugi znakovi u kontrolnom nizu 5.4.4 Uˇitavanje znakovnih nizova . . . c 5.4.5 Prefiks * . . . . . . . . . . . . . 5.4.6 Maksimalna ˇirina polja . . . . . s 5.4.7 Povratna vrijednost . . . . . . . . 5.5 Funkcija printf . . . . . . . . . . . . . . 5.5.1 Ispis cijelih brojeva . . . . . . . . 5.5.2 Ispis realnih brojeva . . . . . . . ˇ 5.5.3 Sirina i preciznost . . . . . . . . . 5.5.4 Ispis znakovnih nizova . . . . . . 5.5.5 Zastavice . . . . . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

4 6 Kontrola toka programa 6.1 Izrazi i naredbe . . . . . . . . . . . . . . . . 6.1.1 Izrazi . . . . . . . . . . . . . . . . . . 6.1.2 Naredbe . . . . . . . . . . . . . . . . 6.1.3 Sloˇene naredbe . . . . . . . . . . . . z c 6.1.4 Popratne pojave i sekvencijske toˇke 6.2 Naredbe uvjetnog grananja . . . . . . . . . . 6.2.1 if naredba . . . . . . . . . . . . . 6.2.2 if-else naredba . . . . . . . . . . . 6.2.3 Primjer: Djelitelji broja . . . . . . . 6.2.4 Viˇestruka if-else naredba . . . . . s 6.2.5 Primjer. Viˇestruki izbor . . . . . . . s 6.2.6 Sparivanje if i else dijela . . . . . . 6.3 switch naredba . . . . . . . . . . . . . . . . 6.4 while petlja . . . . . . . . . . . . . . . . . . 6.5 for petlja . . . . . . . . . . . . . . . . . . . 6.5.1 Operator zarez . . . . . . . . . . . . 6.5.2 Datoteka zaglavlja <stdlib.h> . . . 6.6 do - while petlja . . . . . . . . . . . . . . 6.7 Naredbe break i continue . . . . . . . . . . 6.8 goto naredba . . . . . . . . . . . . . . . . . 7 Funkcije 7.1 Definicija funkcije . . . . . . . 7.2 Deklaracija funkcije . . . . . . 7.3 Prijenos argumenata . . . . . 7.4 Inline funkcije . . . . . . . . . 7.5 Rekurzivne funkcije . . . . . . 7.6 Funkcije s varijabilnim brojem 8 Preprocesorske naredbe 8.1 Naredba #include . . . 8.2 Naredba #define . . . . 8.3 Parametrizirana #define 8.4 Uvjetno ukljuˇivanje . . c 8.5 Predefinirani makroi . . 8.6 assert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . argumenata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

ˇ SADRZAJ 96 96 96 97 98 98 100 100 101 102 104 106 106 107 110 111 113 113 114 115 116

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . .

119 . 119 . 123 . 126 . 129 . 130 . 132 134 . 134 . 135 . 136 . 138 . 141 . 142

. . . . . . . . . . naredba . . . . . . . . . . . . . . .

9 Struktura programa 144 9.1 Doseg varijable . . . . . . . . . . . . . . . . . . . . . . . . . . 144 9.1.1 Lokalne varijable . . . . . . . . . . . . . . . . . . . . . 144

. . . 164 . . . . . . . . . . . . . . . . c c 11. . . 9. . c . . .2. . . . . . . . .3. . . .1. . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . c 11. c Vanjski simboli . . . . . .5 Primjer . . . . . . sprintf() . . . . . . . . 170 173 . . . . . . .1 Poveˇavanje i smanjivanje . . . . . . . . .3 Argumenti funkcijskog prototipa . . c 11. . . . . .2. . . . . . . . . . . . . . . . . . . .3. . 5 145 148 148 149 149 149 150 150 150 151 151 152 153 155 156 9. . . . . . . .ˇ SADRZAJ 9. . . . . .5 Funkcijski doseg . . . . . . . . . . . . . . 9. . . 9. . . . . . . . 189 11 Pokazivaˇi c 11. . . . . . .5 Pokazivaˇi i const . . . . . . . . . . . . . . . .3 10 Polja 10. . . 185 . . . . . . . c 11. . . . 175 . .3 Vanjska imena . . . . . . . . . .2 Identifikatori memorijske klase . Vijek trajanja varijable . . . . . . . .6 Generiˇki pokazivaˇ . . . . . . . . .1 Deklaracija pokazivaˇa . . . . 177 . . . . . . . . . 9. . . .4 Pokazivaˇi i jednodimenzionalna polja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9. . . . . . 157 . . . . 179 . . . . . c s 11. . . . . . . . . . . . . . . . . . . . . .3 Usporedivanje pokazivaˇa .1 Matrica kao pokazivaˇ na pokazivaˇ c c 11.2 Polja znakova . . . . 187 . . . . . . . .1. . . .8 Dinamiˇka alokacija memorije . . . . . . . . . . . . . 179 . . . 9. . . . .2 Globalne varijable . . . . . . . . . . . .7 Polja varijabilne duljine . . . . . . c 11. . .3. . 9. . . . . . .5 Statiˇke varijable . . . 9. . . .7 Pokazivaˇi i viˇedimenzionalna polja . 10. .1 Definicija i inicijalizacija polja 10. . .7.6 Statiˇke lokalne varijable . . . . . . . . .3. . . . . . . . . . .3. . . c 11. . . . . . . . . . 183 .3. .3. . . . . . . . . . 9.3 Operacije nad pokazivaˇima . . . . . . . . . . . . . . 160 . . . . .4 Oduzimanje pokazivaˇa . . . .1 Automatske varijable . . . . . . . . . . .2. . c 11. . s 10. .2 Pokazivaˇi i funkcije . . . . c 11.3. . . . . . . . . .4 Lokalne varijable i standard C99 . . . . . . . 166 . . . . . 165 .3 auto . . . . . . . . . . . . . . . . . . . . . . . . 181 . . 161 . . . 186 . . . .2 Pokazivaˇi i cijeli brojevi . . . . 11. . . . . . . . . . c 11. . . . . 10. . . . . . c 9. .4 register . . . . . . . . . . . . . .2 9. . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . 179 . . . . . . . . . . . . . . . . . . . . . . . .3. . . . . . . . . .6 Polja pokazivaˇa . . .2 Globalne varijable . . . . 9. . . . . . 180 . . . . .1. 173 . . . . . . . . . . .4 sscanf(). . . . . . c 11. . 177 . . . 9. . c 11. . . . . . . .5 Polje kao argument funkcije . . . .2. . . . . . . . . 188 . . . . . .1 Funkcije . . .6 Viˇedimenzionalna polja . . . .3 Funkcije za rad sa stringovima 10. . 157 . . . . . . 10.

. . . . . . . . . . .1 Varijable tipa strukture . . . . . . . 12. . . . . . . . . . . . 227 14.6 ˇ SADRZAJ 11.3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2 Rad sa strukturama . . .2 Sloˇeni izrazi . . . .1. . . . .3 Struktura unutar strukture 12. . . . 12. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13. . . .2. . . .3. . 215 . . . . . . . . . . . . . . . . 194 z 12 Strukture 12. . .3 Prepoznavanje greˇke . . . . . . .1 Jednostruko povezana lista 12. . .3 Strukture i pokazivaˇi . . . . . . . . . . . . . . . . . . . . . . . .3. . . 221 . 214 . . 216 . . . . . . . .9 Pokazivaˇ na funkciju . . . .4 Formatirani ulaz/izlaz . . . . . . 222 . . . . . 12. . .2 Polja bitova . . . ˇ 13. . . . .4. . .10Argumenti komandne linije . . . . . . . . . . . . . . . .3. . .1 Deklaracija strukture . .11Sloˇene deklaracije . . . 12. . . . . . . 191 c 11. . . . 218 . 224 14 Operacije nad bitovima 227 14. .1 Operator toˇka . . . . . . . . . . . 13. . . . . . . . . . .1 Operator strelica (->) . . .2. . . 13.1 Operatori . . . . . .2 Inicijalizacija strukture .3 Funkcije za ˇitanje i pisanje . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . .1. 13. . . . . . . . . . . . . . . . . . .2 Citanje i pisanje liniju po liniju 13. . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.1 Citanje i pisanje znak po znak . . . . . 12. . . 218 .6 Unija . . . . . . . . . . . . . . . . . . . . . . . . . . . . c 12. . 217 . 214 .3. . z 12. . . . . . .2 Struktura i funkcije . 13. . . . . . . . . . . . . . . 192 11. . . . . . . . . . . . . . . . . . .1. . 220 .3. 222 . . . . . .5 typedef . . . . .3 Standardne datoteke . . . . . .2. . .6 Direktan pristup podacima . . . . . . .1 Vrste datoteka . . . . . s 13. . . .2 Otvaranje i zatvaranje datoteke . 213 . . . . . . . . . . . . . . . . . . . .1 Tekstualne i binarne datoteke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 . . . . . . . . . . . . . .1 Standardne datoteke . . . . . . .4 Samoreferentne strukture . . . 231 . . . . . 12. . . . . . c 12. . . . . .1. . . . . . . . 12.3. 12. . . . . . . . . . . . . . . . . . . . 13. . . . . . . . . . . . . . . . . . .5 Binarni ulaz/izlaz . . 13. . . . . . . . . . . . . . . . 196 196 196 197 198 198 198 200 200 200 202 203 203 209 211 13 Datoteke 13. . . . . . . .3. . . . . . . . . c ˇ 13.2 Razine ulaza/izlaza . .

. . . . . . . . . . .2 ASCII znakovi . . . . 235 A. . . . . . . . . . . . . . . . . .3 Matematiˇke funkcije . . . . 236 c . . . . 234 A. . . . . . . . . .ˇ SADRZAJ A 7 234 A. . . . . . . . . . . . . . . . . . . . . . . . . . .1 ANSI zaglavlja . .

Poglavlje 1 Uvod
1.1 Programski jezici

Mikroprocesor i drugi logiˇki sklopovi raˇunala imaju svoj vlastiti proc c gramski jezik koji se naziva strojni jezik, a sastoji se od nizova binarnih rijeˇi c koje predstavljaju instrukcije logiˇkim sklopovima i podatke koje treba obrac diti. Program napisan u strojnom jeziku nazivamo izvrˇni program ili izvrˇni s s kˆd budu´i da ga raˇunalo moˇe neposredno izvrˇiti. Strojni jezik je odreden o c c z s arhitekturom raˇunala, a definira ga proizvodaˇ hardwarea. Izvrˇni program c c s je strojno zavisan, ˇto znaˇi da se kˆd napisan na jednom raˇunalu moˇe s c o c z izvrˇavati jedino na raˇunalima istog tipa. s c Pisanje instrukcija u binarnom kˆdu posve je nepraktiˇno pa su razvijeni o c simboliˇki jezici u kojima su binarne instrukcije zamijenjene mnemoniˇkim c c oznakama. Programer unosi program napisan u mnemoniˇkim oznakama c u tekstualnu datoteku pomo´u editora teksta i zatim poziva program koji c mnemoniˇke oznake prevodi u binarne instrukcije strojnog jezika. Program c koji vrˇi konverziju naziva se asembler (eng. assembler) a sam se programs ski jezik naziva asemblerski jezik ili jednostavno asembler. Program napisan u asemblerskom jeziku nazivamo izvorni program (eng. source code). Pisanje programa time postaje dvostepeni proces koji ˇine pisanje izvornog proc s grama i prevodenje izvornog programa u izvrˇni program. Programer se tako oslobada mukotrponog pisanja binarnih instrukcija te se dobiva do odredene mjere strojna neovisnost izvornog programa. Sljede´i primjer pokazuje dio izvrˇnog kˆda (strojni jezik) i njemu ekvivac s o lentan izvorni, asemblerski kˆd. Rad se o asembleru za Intelov mikroprocesor o 8086 ([5]). 8

1.1. PROGRAMSKI JEZICI Strojni jezik 00011110 00101011 11000000 10111000 10111000 00000001 10001110 11011000 Ekvivalentan PUSH SUB PUSH MOV asemblerski kˆd o DS AX,AX AX AX,MYDATA

9

MOV

DS,AX

Pisanje programa u asemblerskom jeziku daje programeru potpunu kontrolu nad svim komponentama raˇunala. Programer stoga mora poznavati c arhitekturu raˇunala za koje piˇe program te kontrolirati sve operacije koje c s raˇunalo izvrˇava. Programiranje i najjednostavnijih zada´a rezultira velikim c s c i sloˇenim programima pa se programiranje u asembleru koristi se samo za z specifiˇne zada´e vezane uz manipulacije s hardwareom. Izvorni program nac c c c pisan u asembleru nije prenosiv izmedu raˇunala razliˇite arhitekture. Zbog svih tih razloga za ve´inu programerskih zada´a koriste se viˇi programski c c s jezici. Viˇi programski jezici (C, Pascal, FORTRAN, C++, Java, Perl, Python, s . . . ) razvijeni su kako bi se prevladali nedostaci asemblerskog jezika. Oni oslobadaju programera potrebe poznavanja arhitekture raˇunala, omogu´ac c vaju prenosivost programa izmedu raˇunala razliˇitih arhitektura te brˇe i c c z jednostavnije programiranje. Programi napisani u viˇem programskom jeziku s c s o s moraju prije izvodenja pro´i postupak prevodenja u izvrˇni kˆd ˇto je zadatak prevodioca (eng. compiler) za dani jezik. Naredbe viˇeg programskog s jezika prilagodene su tipu podataka s kojima programski jezik manipulira i operacijama koje nad podacima treba vrˇiti. To je osnovna razlika naspram s asemblera koji je prilagoden naˇinu funkcioniranja mikroprocesora i drugih c logiˇkih sklopova. Zadatak je prevodioca da program napisan u viˇem proc s gramskom jeziku prevede u kˆd koji se moˇe izvrˇavati na zadanom raˇunalu. o z s c Na taj se naˇin program napisan u nekom viˇem programskom jeziku moˇe c s z izvrˇavati na svakom raˇunalu koje ima prevodilac za taj jezik. s c Programski jezik C viˇi je programski jezik op´e namjene. Razvio ga je s c Dennis Ritchie sedamdestih godina proˇlog stolje´a u Bell Telephone Laboras c tories, Inc. Opis jezika dan je u knjizi Brian W. Kernighan, Dennis M. Ritchie: The C Programming Language, Prentice-Hall, 1978. Tijekom sedamdesetih i osamdesetih godina jezik se brzo ˇirio te je American National Standard s Institute (ANSI) pristupio njegovoj standardizaciji koja je dovrˇena 1989. s godine. Novi standard uveo je znaˇajne izmjene u jezik koji se stoga, za c

10

POGLAVLJE 1. UVOD

razliku od prvotne verzije, ˇesto naziva ANSI-C. Novi je standard opisan u c knjizi Brian W. Kernighan, Dennis M. Ritchie: The C Programming Language, 2nd ed., Prentice-Hall, 1988. Danas gotovo svi moderni C-prevodioci implementiraju ANSI-C verziju jezika. ANSI standard usvojila je i Medunarodna organizacija za standarde (International Organisation for Standardization) 1990. godine (ISO C). Konaˇan ANSI/ISO standard ´emo jednostavno nazic c vati C90 standard. Godine 1999. ISO je prihvatio novi C standard koji uvodi manje dopune u C90 standard, a koji ´emo zvati C99 standard. c

1.2

Osnove pisanja programa u Unix okruˇez nju

Postupak programiranja u programskom jeziku C, pod operacijskim sustavima unix i linux, sastoji se od tri koraka: prvo se programski kˆd smjesti o u tekstualnu datoteku koja ima ekstenziju .c (npr. prvi.c). Zatim se napisani program transformira u izvrˇni kˆd pozivom programa prevodioca. Kao s o rezultat dobiva se izvrˇna datoteka ˇijim se pozivom izvrˇava napisani pros c s gram. Budu´i da se u pisanju programa redovito javljaju greˇke, tre´i korak c s c u postupku programiranja je nalaˇenje i ispravljanje greˇaka. z s

1.2.1

Editor teksta

Da bismo program upisali u tekstualnu datoteku potreban nam je editor teksta. Svaki unix sustav ima editor koji se naziva vi (visual editor) i koji se moˇe koristiti i bez grafiˇkog suˇelja. Pored vi editora mogu´e je koristiti z c c c svaki drugi editor koji ne ume´e znakove formatiranja u datoteku. c

1.2.2

Compiler

Nakon ˇto je program napisan i pohranjen u datoteku, recimo da se zove s prvi.c, potrebno je pozvati C-prevodilac. Pod unix-om njegovo je ime najˇeˇ´e cc (ili gcc ako se radi o GNU prevodiocu). Tako bismo na komandnoj c sc liniji otipkali cc prvi.c i ukoliko je program napisan bez greˇke prevodilac ´e kreirati izvrˇni program s c s i dati mu ime a.out; u sluˇaju neispravnosti programa prevodenje ´e biti c c zaustavljeno i prevodilac ´e dati poruke o pronadenim greˇkama. Nakon ˇto c s s je izvrˇni program kreiran pod unix-om je dovoljno otipkati s

ˇ 1.2. OSNOVE PISANJA PROGRAMA U UNIX OKRUZENJU ./a.out i program ´e biti izvrˇen. c s

11

1.2.3

man

Prevodilac cc ima brojne opcije koje su opisane u njegovoj man stranici (man je skra´eno od manual). Dovoljno je na komandnoj liniji utipkati c man cc i opis komande cc bit ´e izlistan. Na isti naˇin moˇemo dobiti opis bilo koje c c z naredbe operacijskog sustava te niza programa i alata. Dovoljno je otipkati man naslov gdje je naslov naredba ili program koji nas zanima. Jednako tako man ´e nam c dati informacije o funkcijama iz standardne biblioteka jezika C. Na primjer, otipkajte man scanf i dobit ´ete opis funkcije za uˇitavanje podataka scanf. c c

1.2.4

Debugger

Ukoliko program upisan u datoteku prvi.c nije ispravno napisan to se moˇe manifestirati na tri naˇina. Prvi, najpovoljniji, je onaj u kome prevodiz c lac javi poruke o greˇkama i zaustavi proces prevodenja u izvrˇni kˆd. Druga s s o mogu´nost je da proces prevodenja bude uspjeˇan i da cc kreira izvrˇnu c s s datoteku a.out, no izvrˇavanje programa zavrˇava s porukom operacijskog s s sustava o neispravnom izvrˇnom programu. Tre´a mogu´nost, ˇesto najnes c c c povoljnija, je ona u kojoj je izvrˇni program ispravan ali ne radi ono ˇto je s s programer zamislio. U svakom sluˇaju, kod pojave greˇaka potrebno je greˇke c s s prona´i, korigirati i ponoviti postupak prevodenja u izvrˇni kˆd. Budu´i da c s o c se greˇke redovito javljaju, programiranje je iterativan proces u kojem je s ispravljanje greˇaka tre´i (i najvaˇniji) korak. s c z Prilikom pronalaˇenja greˇaka mogu nam pomo´i dva alata. Jedan je lint z s c (ukucajte man lint na komandnoj liniji) koji provjerava sintaksu napisanog programa i daje detaljniju dijagnostiku od poruka o greˇkama koje daje sam s prevodilac. Drugi alat je debugger (dbx ili gdb), program pomo´u kojega c naˇ kˆd moˇemo izvrˇavati liniju po liniju, pratiti vrijednosti varijabli, zauss o z s tavljati ga na pojedinim mjestima itd. Debugger je najefikasnije sredstvo za otkrivanje razloga neispravnog funkcioniranja kˆda. o

c datoteku generira strojni kˆd i smjeˇta ga u datoo s teku istog imena ali s ekstenzijom . a zatim sve o objektne datoteke povezati. source code). c s 1. s c . z s fgets itd. c 1 Koristi se joˇ i naziv punjaˇ. Datoteke s ekstenzijom . Pri tome nam pomaˇe programski alat koji se naziva make.o. To su tekstualne datoteke koje programer o kreira zajedno s . UVOD 1.c datoteku prevesti u objektni kˆd. Zadatak je linkera kreirati izvrˇni program povezivanjem s svih objektnih programa u jednu cjelinu. On provjerava datume z o zadnje izmjene pojedinih datoteka i osigurava da se prevedu u objektni kˆd samo one datoteke koje su mijenjane nakon posljednjeg generiranja izvrˇnog s programa. Potpuniji popis moˇe se na´i u [4] ili u priruˇnicima za C-prevodilac z c c na danom raˇunalu.2.2. Potpun popis funkcija dostupnih programeru razlikuje se od raˇunala do raˇunala no c c jedan broj tih funkcija prisutan je na svakom sustavu koji ima C prevodilac. header files) i sadrˇe informacije potrebne prevodiocu za prevodenje z izvornog kˆda u strojni jezik.12 POGLAVLJE 1. Dobiveni se kˆd naziva objektni kˆd ili o o 1 objektni program. U ovoj skripti bit ´e opisan dio najˇeˇ´e koriˇtenih funkcija iz standardne bic c sc s blioteke. s s c Prevodilac za svaku .2.c datoteke zajedno.5 Linker Sloˇeniji C programi sastoje se od viˇe datoteka s ekstenzijama . Svaku pojedinu .c datoteku prevodilac prevodi u strojni jezik no time se joˇ ne dobiva izvrˇni program jer njega ˇine tek sve .6 make Pri prevodenju u izvrˇni kˆd programa koji je razbijen u viˇe datoteka s o s potrebno je svaku pojedinu . Prevodilac ´e nakon generiranja c objektnog kˆda automatski pozvati linker tako da njegovo neposredno pozio vanje nije potrebno.h nazivaju se datoteke zaglavlja (eng. eventualno i s vanjskim bibliotekama.h. koje su pohranjene u standardnim bibliotekama funkcija. z s Datoteke s ekstenzijom .c sadrˇe dijelove C programa odnosno izvorni kˆd z o (eng. exit.c i .7 Programske biblioteke Svaki sloˇeniji C-program koristi niz funkcija kao ˇto su printf.c datotekama. 1. Ukoliko povezivanje objektnog kˆda nije bilo uspjeˇno o s linker ´e dati poruke o greˇkama.

1 Prvi program Jedan jednostavan ali potpun C program izgleda ovako: #include <stdio. Program zatim kompiliramo naredbom cc prvi./a.out. Nazovimo stoga datoteku prvi.out Rezultat izvrˇavanja programa je ispis poruke s 13 .c.h> int main(void) { printf("Dobar dan. Svi ovdje uvedeni pojmovi bit ´e c detaljnije analizirani u narednim poglavljima.Poglavlje 2 Osnove programskog jezika C U ovom poglavlju dajemo pregled programskog jezika C uvode´i osnovne c elemente jezika kroz jednostavne primjere.\n"). 2. return 0.c kako bi se znalo da se radi o C-programu.c nakon ˇega prevodilac kreira izvrˇni program i daje mu ime a. Da bismo c s s izvrˇili program dovoljno je otipkati . } Program treba spremiti u datoteku s ekstenzijom .

a varijable sluˇe memorirac s z nju podataka. Funkcije sadrˇe instrukz cije koje odreduju koje ´e operacije biti izvrˇene. Tijelo sadrˇi samo dvije naredbe: poziv funkcije z printf i return naredbu: { printf("Dobar dan. void=prazan). Povratna vrijednost je tipa c c int.h> Njom se od prevodioca traˇi da ukljuˇi (include) u program datoteku stdio. Izvrˇavanje programa poˇine izvrˇavanjem funkcije main koja mora biti s c s prisutna u svakom programu.).14 Dobar dan.h z c koja sadrˇi informacije nuˇne za koriˇtenje funkcije printf i mnogih drugih. na primjer. c Tijelo funkcije je sastavljeno od deklaracija varijabli i izvrˇnih naredbi. U naˇem primjeru ime funkcije je main i ona ne s uzima niti jedan argument. Deklaracijom se uvodi ime funkcije. koja je zaduˇena za ispis podataka na z ekranu. U naˇem primjeru nes s mamo deklaracija varijabli. POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C Svi C-programi sastoje se od funkcija i varijabli. To omogu´ava da se viˇe s c c s naredbi stavi na istu liniju. Tako se u naˇem programu poziva funkcija s printf iz standardne biblioteke. } Svaka naredba zavrˇava znakom toˇka-zarez (.1 s Iza oblih zagrada dolaze vitiˇaste zagrade koje omeduju tijelo funkcije. broj i tip argumenata koje uzima i tip povratne vrijednosti. return 0. Funkcija main svoju zada´u obavlja op´enito c c pozivanjem drugih funkcija. z z s Datoteke s ekstenzijom . . ˇto je oznaka za cijeli broj.\n"). c Sljede´a linija predstavlja deklaraciju funkcije main: c int main(void) Funkcija moˇe uzimati jedan ili viˇe argumenata i obiˇno vra´a neku vrijedz s c c nost. Sve s deklaracije varijabli dolaze prije prve izvrˇne naredbe. Da bismo mogli koristiti funkcije iz standardne biblioteke zaduˇene z za ulaz i izlaz podataka program zapoˇinjemo naredbom c #include <stdio. To je deklarirano tako ˇto je u oble zagrade s stavljena kljuˇna rijeˇ void (eng. pisati 1 int=integer. header files) i njihovo stavljanje u oˇtre zagrade < > informira prevodilac da se radi o s standardnim datotekama zaglavlja. Mogli smo. koje se nalaze na unaprijed odredenim mjestima u datoteˇnom sustavu.h nazivaju se datoteke zaglavlja (eng.

Navodnici sluˇe za z ograniˇavanje konstantnih znakovnih nizova. Funkcija printf dobiva samo jedan argument: to je niz znakova "Dobar dan. Sve ˇto se nalazi izmedu ” i c s ” predstavlja niz znakova koji ˇini jednu cjelinu."). s s . c Funkcija main mora biti prisutna u svakom programu. Zatim se redom izvrˇavaju naredbe s unutar funkcije main sve dok se ne dode do return naredbe. Program smo stoga mogli z napisati u obliku #include <stdio. Pored obiˇnih znakova c c izmedu navodnika mogu se na´i i specijalni znakovi koji poˇinju znakom \ c c (eng. Funkcija printf c nakon zavrˇenog ispisa ne prelazi automatski u novi red nego je potrebno s ubaciti znak \n tamo gdje takav prijelaz ˇelimo. Ona operacijskom sustavu vra´a cjelobrojnu vrijednost koja ima znaˇenje izlaznog c c statusa. } 15 Funkcija se poziva tako da se navede njezino ime iza koga idu oble zagrade s listom argumenata koji se funkciji predaju.\n".1. printf("dan. Nula se interpretira kao uspjeˇni zavrˇetak programa. Tako \n oznaˇava prijelaz u novi red. printf("\n"). PRVI PROGRAM #include <stdio. return 0. Kad funkcija vra´a neku s s c vrijednost u pozivni program ta se vrijednost navodi u return naredbi. a svaka druga s s vrijednost signalizira zavrˇetak uslijed greˇke./a. return 0.2. Izvrˇavanje funkcije zavrˇava naredbom return.out operacijski sustav poziva funkciju main. Kada na komandnoj liniji otipkamo . backslash).h> int main(void) { printf("Dobar ").\n"). } i dobili bismo posve isti ispis. Funkcija main vra´a nulu pa je stoga zadnja naredba return 0.h> int main(void){ printf("Dobar dan.

2 Varijable. z Dvije linije /* Program koji ispisuje prvih 10 faktorijela. fakt=1. Prevodilac ignorira dio programa izmedu znakova /* i */. Tekst koji se nalazi izmedu tih znakova ima ulogu komentara koji treba olakˇati razumijevanje programa.. OSNOVE PROGRAMSKOG JEZIKA C 2.h> /* Program koji ispisuje prvih 10 faktorijela.h> koja nam je nuˇna jer koristimo funkciju printf. #include <stdio. printf(" %d %d\n". */ predstavljaju komentar unutar programa.16 POGLAVLJE 2. . s 3!.. . .. while(n<=10) { fakt=fakt*n. */ int main(void) { int n.fakt.10! ˇelimo ispisati u obliku tablice2 z 1 1 2 2 3 6 4 24 . Brojeve 1!. .fakt). 2!... } return 0. n=1. n=n+1. s Prva linija unutar funkcije main 2 n! je broj definiran formulom n! = 1 · 2 · 3 · · · · n .n. while petlja Napiˇimo program koji ispisuje prvih deset faktorijela. } Prva linija programa ukljuˇuje u program standardnu datoteku zaglavlja c <stdio.

Naredba . U C-u je potrebno deklarirati sve varijable koje se u programu koriste. Varijable realnog tipa (tzv. unije te pokazivaˇi kao posebni s c osnovni tip podatka. Nakon te naredbe z vrijednost varijable n postaje jednaka 1. WHILE PETLJA int n. Tipovi varijabli: char short int long float double jedan znak. Pored varijabli tipa int postoje c sc joˇ dvije vrste cjelobrojnih varijabli. To su varijable tipa short i varijable s tipa long. realan broj jednostruke preciznosti. “dugi” cijeli broj. On vrijednost konstante ili varijaz ble na desnoj strani pridruˇuje varijabli na lijevoj strani. brojevi s pokretnim s zarezom) pojavljuju se u dvije forme: brojevi jednostruke preciznosti float i brojevi dvostruke preciznosti double. a njihov tip je int. Pored ovih tipova podataka koje nazivamo osnovnim. imena varijabli koje se deklariraju su n i fakt. cijeli broj.fakt. 17 predstavlja deklaraciju varijabli n i fakt. postoje i varijable tipa char c u kojima se pamte znakovi. Deklaracija kao i svaka druga naredba u programu zavrˇava s znakom toˇka-zarez.2.2. postoje joˇ i sloˇeni s z tipovi podataka kao ˇto su strukture. VARIJABLE. U deklaraciji c int n. Preciznije. z Granice u kojima se moˇe kretati varijabla tipa int ovise o raˇunalu i danas z c je to najˇeˇ´e od -214748347 do 214748348. “kratki” cijeli broj. Konaˇno. polja. Znak jednakosti je operator pridruˇivanja. Tip int predstavlja cjelobrojnu varijablu koja moˇe biti pozitivna i negativna.fakt. Prva izvrˇna naredba u programu je naredba pridruˇivanja s z n=1. unutar svake funkcije (ovdje unutar funkcije main) sve varijable koje funkcija koristi treba deklarirati prije prve izvrˇne naredbe. Razlika je u tome da short pokriva manji raspon cijelih brojeva i zauzima manje memorijskog prostora. s Deklaracija se sastoji od tipa varijable koji slijede imena varijabli odvojena zarezima. realan broj dvostruke preciznosti. dok long pokriva ve´i raspon i treba c viˇe memorijskog prostora.

s while-petlja funkcionira na sljede´i naˇin: prvo se izraˇunava izraz u zac c c gradama: n<=10. tijelo petlje je skup naredbi koje se nalaze unutar vitiˇastih zagrada. n=n+1. Ona se sastoji od kljuˇne rijeˇi while. c strogo manje. Ukoliko je je on istinit (n je manji ili jednak 10) onda se izvrˇava tijelo petlje (sve naredbe unutar vitiˇastih zagrada). Ostali su: operator primjer <= a <= b >= a >= b < a < b > a > b == a == b != a != b znaˇenje c manje ili jednako. Zatim se pos c novo testira izraz n<=10 i izvrˇava tijelo petlje ako je izraz istinit. iako nihova c upotreba nije pogreˇna. ve´e ili jednako. strogo ve´e. Izvrˇavanje s s petlje prekida se kada izraz n<=10 postane neistinit. Kada se tijelo sastoji od viˇe naredbi one moraju s s biti omedene vitiˇastim zagradama. U prvoj i tre´oj naredbi pojavljuju se operacije mnoˇenja i zbrajanja. Vaˇno je razlikovati operator pridruˇivanja (=) i relacijski operator jedz z nakosti (==). Ako se sastoji z s od samo jedne naredbe nije potrebno koristiti vitiˇaste zarade. nejednako. printf(" %d %d\n".fakt)..18 while(n<=10) { .n. } POGLAVLJE 2. Program se tada nastavlja prvom naredbom koja slijedi iza tijela while petlje. c Manje ili jednako (<=) je jedan od relacijskih operatora. U tijeli while petlje imamo tri naredbe: fakt=fakt*n.. Tijelo petlje moˇe se sastojati od jedne ili viˇe naredbi. c jednako. Osc z novne aritmetiˇke operacije dane su sljede´oj tabeli: c c zbrajanje oduzimanje mnoˇenje z dijeljenje operator primjer + a+b a-b * a*b / a/b . testa n<=10 i tijela c c petlje.. OSNOVE PROGRAMSKOG JEZIKA C je while petlja.

VARIJABLE. 2/3 = 0). a drugi i tre´i su varijable n i fakt. Pri takvom pozivu on ne sadrˇi znakove z konverzije ve´ samo tekst koji treba ispisati. kao cijeli decimalni broj. Funkcija je pozvana s tri argumenta. U prvom programu je funkcija printf bila pozvana samo s jednim argumentom. U naredbi n=n+1 varijabli n pove´avamo c vrijednost za 1. Svi ostali znakovi u kontrolnom znakovnom nizu bit ´e ispisani onako kako su c uneseni. Ona ispisuje brojeve n i fakt na ekranu. je poziv funkcije printf. Ako je bar jedan operand realan broj dijeljenje predstavlja uobiˇajeno dijeljenje realnih brojeva. Naredba printf(" %d %d\n".. Prvi je konstantan niz znakova " %d %d\n".5 1. Drugi znak %d oznaˇava da na tom mjestu c treba ispisati drugi argument koji slijedi nakon kontrolnog znakovnog niza. dok z varijabla n ostaje nepromijenjena.0 6. To se radi tako da se rezultatu dijeljenja odsijeku decimale kako bi se dobio cijeli broj (npr.2.0/4 == == == == 1 1.0/4. c 6/4 6. Slovo d osnaˇava da ga treba c ispisati kao cijeli decimalni broj. Varijabla fakt pri tome mjenja vrijednost. onda je cjelobrojna (tipa int). Njihova uloga je sljede´a: Prvi znak konverzije %d c oznaˇava da na mjestu na kome se on nalazi treba ispisati prvi argument c koji slijedi nakon kontrolnog znakovnog niza.2. Ukupan efekt je pove´anje vrijednosti varijable n c za 1. Na primjer.n.0 6/4. inaˇe c c je tipa double. c . Prvi se argument naziva kontrolni c znakovni niz i on sadrˇi znakove konverzije koji se sastoje od jednog slova z kome prethodi znak %.5 Ako konstanta nema decimalnu toˇku. kontrolnim znakovnim nizom. Operacija dijeljenja s cjelobrojnim operandima daje cjelobrojni rezultat.5 1.fakt). ˇ Broj argumenata koji se daju printf funkciji moˇe biti proizvoljan. WHILE PETLJA 19 U naredbi fakt=fakt*n varijable fakt i n su pomnoˇene i rezultat je z pridruˇen varijabli fakt. Zelimo z li ispisati realan broj (float ili double) trebamo koristiti %f umjesto %d. U ovom primjeru imamo dvije bjeline i znak za prijelaz u novi red. Operator pridruˇivanje (=) djeluje tako da se prvo izraˇuna z c izraz na desnoj strani (n+1) i zatim se ta vrijednost pridruˇi varijabli na z lijevoj strani (varijabla n).

. for(i=1. double suma.h>.&n).i=i+1) { suma=suma+1.suma). Naredba printf("Unesite broj n: n= ").&n). Pozivom funkciji scanf ˇita se cijeli broj sa standardnog ulaza: c scanf(" %d".3 for petlja Treba napisati program koji za proizvoljni prirodni broj n raˇuna sumu c n k=1 1 k(k + 1) i ispisuje rezultat. c s Funkcija scanf pripada standardnoj biblioteci i deklarirana je u stanc dardnoj datoteci zaglavlja <stdio.i<=n. ispisuje znakovni niz "Unesite broj n: n= " (bez prijelaza u novi red).n. OSNOVE PROGRAMSKOG JEZIKA C 2. printf("Unesite broj n: n= ").20 POGLAVLJE 2. Program u ovom trenutku ˇeka da korisnik upiˇe jedan cijeli broj. c dok je suma realna varijabla tipa double (dvostruke preciznosi). } printf("Suma prvih %d clanova = %f\n". } Program zapoˇinje deklaracijama varijabli. #include <stdio. suma=0. Varijable n i i su cjelobrojne. return 0.h> int main(void) { int n. scanf(" %d".0/(i*(i+1)).0.i. Po strukturi je sliˇna funkciji printf.

U naˇem z s sluˇaju to je %d koji informira scanf da mora proˇitat jedan broj tipa int i c c smjestiti ga u varijablu koja je sljede´i argument funkcije (varijabla n).0/(i*(i+1)).. Ona djeluje na sljede´i naˇin. Adresa varijable dobiva se pomo´u adresnog operatora & c (&n je adresa varijable n). Prvo se varijabla i inicijalic c zira tako ˇto joj se pridruˇi vrijednost 1 (i=1). for-petlja iz naˇeg primjera moˇe se zapisati pomo´u while-petlje na s z c ovaj naˇin: c i=1.. Stoga svi argumenti funkcije scanf. n--. Nakon toga se izvrˇava naredba c s i=i+1. Inicijalizacija se izvrˇi samo s z s jednom. Program se tada nastavlja s z prvom naredbom iza tijela petlje. Zatim se testira izraz i<=n.i<=n.3.2.i=i+1) { . a ne sama varijabla n. while(i<=n) { suma=suma+1. . i=i+1. i kontrola programa se vra´a na testiranje istinitosti izraza i<=n. } je for petlja. } Naredbe oblika i=i+1 i i=i-1 kojima se brojaˇ pove´ava. Ako je rezultat testa istinit izvrˇavaju se naredbe iz tijela petlje (naredbe u s vitiˇastim zagradama). Petlja c zavrˇava kad izraz koji se testira postane laˇan. osim prvog.. c Stvarni argument funkcije scanf je memorijska adresa varijable n. FOR PETLJA 21 Njen prvi argument je niz znakova koji sadrˇi znakove konverzije. moraju imati znak & ispred sebe. Naredba for(i=1. odnosno smac c njuje za jedan sre´u se vrlo ˇesto pa za njih postoji kra´a notacija: c c c n=n+1 n=n-1 je ekvivalentno s je ekvivalentno s n++.

. u obliku c c for(inicijalizacija brojaca. Ti se operatori redovito koriste u for petljama pa bismo gornju petlju pisali u obliku: for(i=1.. 2a Koristit ´emo notaciju z1 = x1 + iy1 . } Inicijalizacija.. Operatore inkrementiranja i dekrementiranja moˇemo primijeniti na cjeloz brojne i realne varijable.h> #include <stdlib.. test i promjena brojaˇa odvajaju se znakom toˇka-zarez (..0/(i*(i+1)). z2 = x2 + iy2 .6.. Svaka petlja sadrˇi u sebi jedan ili viˇe brojaˇa kojim se kontrolira odviz s c janje petlje. c c Napomena.. U C-u postoji joˇ do-while petlja o koju ´emo objasniti u s c sekciji 6.h> . Inicijalizaciju moramo staviti izvan petlje.2 = .koji smanjuje vrijednost varijable za 1 naziva se operator dekrementiranja. promjena brojaca) { . Rjeˇenja su op´enito kompleksna i zadana su formulom √ −b ± b2 − 4ac z1. Pelja se zaustavlja testirac njem brojaˇa.4 if naredba Treba napisati program koji rijeˇava kvadratnu jednadˇbu: ax2 + bx + c = s z s c 0. a promjenu c c brojaˇa negdje unutar petlje. OSNOVE PROGRAMSKOG JEZIKA C Operator ++ naziva se operator inkrementiranja i on pove´ava vrijednost c varijable za 1. Uoˇimo joˇ da smo vitiˇaste zagrade oko tijela c s c for-petlje mogli ispustiti jer se tijelo sastoji samo od jedne naredbe.i++) suma=suma+1.). To vrijedi za sve vrste petlji.. test. U while petlji jedino testiranje brojaˇa ulazi u zagrade nakon c c kljuˇne rijeˇi while.22 POGLAVLJE 2. c #include <stdio. 2. U for petlji sva tri elementa dolaze u zagrac dama iza kljuˇne rijeˇi for. Operator -. Brojaˇ je potrebno na poˇetku inicijalizirati i zatim ga u svac c kom prolazu petlje pove´avati (ili smanjivati).i<=n.

x2=x1. c. z2=%f + i*(%f)\n".x1.h> /* Resavanje kvadratne jednadzbe. */ */ */ */ printf("Upisite koeficijente kvadratne jednadzbe: a. } else if (d == 0) { x1=.0. c: ").4. } } else { printf("Jednadzba nije kvadratna. imamo tri include naredbe. 23 /* /* /* /* Koeficijenti jednadzbe Diskriminanta Realni dijelovi korijena. a datoteku math. &a. if(a != 0.h deklarirano .b . Datoteku stdio. x2=(. y1.0) { d=b*b-4*a*c. exit(-1). } printf("z1=%f + i*(%f). y2).2. y1=0. Prvo. b. y1=sqrt(-d)/(2 * a). IF NAREDBA #include <math. y2. if (d > 0) { x1=(.y1.\n"). x2. return 0. } else{ x1=-b/(2 * a). y2=0. &c). y2 = . x2 = x1.h ukljuˇujemo radi funcija printf i scanf.sqrt (d))/(2 * a). a x^2 + b x + c = 0 */ int main(void) { double a.h radi c √ funkcije sqrt (sqrt(x)= x).b + sqrt (d))/(2 * a). y1. scanf ("%lf%lf%lf". dac toteka stdlib. U datoteci zaglavlja stdlib. x1. Imaginarni dijelovi korijena.0. x2.h ukljuˇujemo radi funkcije exit. b. d.b/(2 * a). } U programu imamo nekoliko novih elemenata. &b.

Naredbe koje se izvrˇavaju. Dio naredbe else ne mora biti c s prisutan. &c). mogu biti pojedinaˇne naredbe ili grupe naredbi omedene c vitiˇastim zagradama. x1. z c .. Prijelaz u novi red moˇemo z stoga ubaciti svugdje gdje moˇe do´i bjelina. x2. Varijable smo razbili u ˇetiri grupe c i svaku smo grupu napisali u zasebnom redu da bismo lakˇe komentirali s njihovo znaˇenje. Takav naˇin pisanja je mogu´ stoga ˇto prevodilac tretira c c c s znak za prijelaz u novi red isto kao i bjelinu. Datoteku math. } else{ .h potrebno je ukljuˇiti kada program c c koristi bilo koju matematiˇku funkciju. Uoˇimo da je znak konverzije c z c kojim se uˇitava varijabla tipa double jednak %lf. b. Ona funkcionira na ovaj naˇin: prvo se testira uvjet i ako je zadovoljen c izvrˇava se naredba1. kod ispisivanja varijabli tipa float i double funkcija printf koristi isti znak konverzije %f.0) { .. uˇitavaju se koeficijenti kvadratne jednadˇbe. c U prvoj naredbi unutar funkcije main deklariramo osam varijabli tipa double: a. tako da if naredba moˇe imati i oblik z 3 %f moˇemo ˇitati kao float. y2. y1. z c Naredbama printf("Upisite koeficijente kvadratne jednadzbe: a.... d.. c. OSNOVE PROGRAMSKOG JEZIKA C je nekoliko funkcija koje se ˇesto koriste tako da se ta datoteka ukljuˇije c c vrlo ˇesto u programe. a %lf kao long float = double. else naredba2. c s naredba1 i naredba2.. &a.24 POGLAVLJE 2. scanf ("%lf%lf%lf". U naˇem sluˇaju s s s c uvjet je a != 0. kao u naˇem primjeru.. Op´enito ima oblik c if(uvjet) naredba1. b... Ako ˇelimo uˇitati vac z c rijablu tipa float (realni broj u jednostrukoj preciznosti) trebamo koristiti znak konverzije %f. 3 Nasuprot tome. a ako nije izvrˇava se naredba2. &b.0 (da li je a razliˇito od nule?). } Naredba if je naredba uvjetnog grananja. c: "). Nakon inicijalizacije varijabli y1 i y2 nulom izvrˇava se if naredba: s if(a != 0.

} naredba4. s Ako je uvjet a != 0. dok se u protivnom c s prijelazi na sljede´u instrukciju. } else if (uvjet2){ naredba2. } else{ x1=-b/(2 * a). Pod operacijskim sustavima Unix i Linux konvencija je da kod 0 oznaˇava ispravan zavrˇetak programa dok svaka druga c s cjelobrojna vrijednost signalizira zaustavljanje usljed greˇke. 25 pri ˇemu se naredba1 izvrˇava ako je uvjet ispunjen.sqrt (d))/(2 * } else if (d == 0) { x1=. = . Ako nije izvrˇava se else dio naredbe uvjetnog grananja u kojem se s ispisuje poruka da jednadˇba nije kvadratna i poziva se funkcija exit s arz gumentom -1.b + sqrt (d))/(2 * x2=(. a). x2 y1=sqrt(-d)/(2 * a). = x1. pozitivna. Prvo se raˇuna diskriminanta jednadˇbe d. Proizvoljan broj if naredbi moˇemo ugnijezditi z tako da dobijemo viˇestruku if naredbu. } else{ naredba3. pomo´u dvije if nas c redbe dobivamo naredbu oblika if(uvjet1){ naredba1. Zada´a funkcija exit je zaustaviti izvrˇavanje programa i c s predati operacijskom sustavu svoj argument (-1 u naˇem primjeru) koji se s interpretira kao kod greske.y1.b/(2 * a).2.0 ispunjen. .b . Na primjer. jednaka nuli ili negativna. izvrsava se dio koda d=b*b-4*a*c. y2 } a). x2=x1. IF NAREDBA if(uvjet) naredba1.0 provjeravamo je li jednaˇba kvadratna ili z nije.4. c Testiranjem uvjeta a != 0. if (d > 0) { x1=(. a onda se ulazi u jednu viˇestruku c z s naredbu uvjetnog granjnja u kojoj se ispituje je li diskriminanta.

premda zbog preglednosti najˇeˇ´e piˇemo svaku c sc s naredbu u svojoj liniji. Kako takve naredbe nema. ako je program spremljen u datoteku kvadratna jed. Ako je on ispunjen izvrˇava se blok naredbi s naredba2 i nakon toga naredba4. ispisujemo rezultat. Kada se izraˇunaju x1 i x2 program izlazi iz sloˇene if z c z narebe i nastavlja s prvom sljede´om naredbom. Centralni dio programa ˇini jedna if-else naredba u koju je smjeˇtena c s druga. Kod kompilacije programa koji koristi matematiˇke funkcije c moramo prevodiocu dati -lm opciju.0 c c imamo jedan dvostruki realni korijen.26 POGLAVLJE 2. OSNOVE PROGRAMSKOG JEZIKA C U ovoj se konstrukciji prvo testira uvjet1. u zadnjoj naredbi c printf("z1=%f + i*(%f). Ako niti uvjet2 nije ispunjen izvrˇava se s else blok. Kod if-else naredbe potrebno je uvijek uvu´i tijelo if dijela i tijelo else dijela da se naglasi logiˇka struktura. z s s na primjer u situaciji kada je a != 0 i d > 0. y1.c -lm Napomena. C ima i switch naredbu koja takoder sluˇi uvjetnom granjaz nju (vidi sekciju 6. z2=%f + i*(%f)\n". Posve c c isto se postupa i sa petljama. onda ga komiliramo linijom cc kvadratna_jed. x2. Konaˇno. U sluˇaju d > 0. Napomena. Konaˇno.0 imamo dva realna korijena. Pogledajmo joˇ jednom kako se program izvrˇava. Na primjer. Posebnu paˇnju treba posvetiti ugnjeˇdenim naredbama z z uvjetnog granjanja te petljama. a to je poziv funkcije printf. ako je diskriminanta negac tivna imamo konjugirano kompleksne korijene. Uoˇimo da smo u tom dijelu c koda dvije naredbe pisali u jednoj liniji. C nam dozvoljava pisati bilo koji broj naredbi u istoj liniji. dok u sluˇaju d == 0. y2). Ako uvjet1 s nije ispunjen testira se uvjet2. sloˇena if naredba. 2.c. Ako je on ispunjen izvrsava se blok naredbi naredba1 i izvrˇavanje programa se nastavlja s prvom naredbom s koja slijedi viˇestruku naredbu uvjetnog granjnja (naredba4).x1.3). Program ´e u´i u if dio c c vanjske if-else naredbe. U dobro pisanom programu njegova logiˇka struktura mora biti vidljiva c iz njegove forme. izraˇunat ´e d i zatim ´e u´i u if dio unutarnje c c c c sloˇene if naredbe. c izvrˇavanje if bloka prve if-else naredbe je zavrˇeno i program se nastavlja s s s prvom naredbom iza nje.5 Funkcije .

i--) rezult=rezult*i. return rezult. FUNKCIJE Treba napisati funkciju koja raˇuna binomni koeficijent c n n · (n − 1) · · · (n − k + 1) = .5. 1 · 2···k k 27 (2. int i.++i) rezult=rezult/i.h> /* Funkcija binom() racuna binomni koeficijent.2. for(i=1. za sve vrijednosti k od 0 do n. c #include <stdio.i<=k. int k) { long rezult=1. } . U k glavnom programu treba zatim ispisati tzv. if(n == k) return 1.1) Funkcija treba uzeti dva cjelobrojna parametra n i k te vratiti broj n . if(k == 0) return 1. z U liniji s indeksom n ispisani su brojevi n . Pascalov trokut prikazan niˇe.i>n-k. k n n n n n n n n n n = = = = = = = = = = 0 1 2 3 4 5 6 7 8 9 1 1 1 1 1 4 1 5 3 2 3 1 1 1 6 4 1 10 10 5 1 1 6 15 20 15 6 1 1 7 21 35 35 21 7 1 1 8 28 56 70 56 28 8 1 1 9 36 84 126 126 84 36 9 1 Radi jednostavnosti ispisat ´emo Pacalov trokut poravnan na lijevoj strani. */ long binom(int n. for(i=n.

for(n=0. if(k == 0) return 1.. s U dijelu koda long binom(int n. Za svaki argument navodi se njegov tip i ime.. for(i=n. . tip 2 arg 2.n<10.k). c Definicija funkcije op´enito ima oblik c tip ime_funkcije(tip_1 arg_1.i>n-k. OSNOVE PROGRAMSKOG JEZIKA C int main(void) { long bnm. } return 0.. .i--) . tip_2 arg_2.bnm)... } Funkcija binom definirana je ispred funkcije main.k<=n. } printf("\n").n++){ for(k=0. k. a zatim izvrˇne naredbe.) { deklaracije varijabli narebe } gdje je tip tip podatka koji funkcija vra´a kao rezultat svog djelovanja. int i. Unuc tar zagrada nalaze se deklaracije argumenata funkcije: tip 1 arg 1.k++){ bnm=binom(n. printf("%ld ". int k) { long rezult=1. Tijelo funkcije je omedeno vitiˇastim zagradama i unutar njega prvo dolaze deklaracije c varijabli. Iz definicije se vidi da uzima dva argumenta tipa int i vra´a rezultat tipa long. if(n == k) return 1. int n.28 POGLAVLJE 2.

jer vrijedi n = 1 c 0 za svako n.i<=k. Kako unutar petlje imamo samo jednu naredbu. jer je n = 1 za c svako n. U naˇem c s n primjeru ako je n == k funkcija se zaustavlja i vra´a 1.2. FUNKCIJE rezult=rezult*i. . Svaka funkcija koja vra´a neku vrijednost mora sadrˇavati barem jednu c z return naredbu.i--) rezult=rezult*i. } 29 definirana je funkcija binom. . . Unutar funkcije deklarirane su dvije c varijable: rezult te i.++i) rezult=rezult/i. k. Time dolazimo do izraza koji smo trebali izraˇunati.1).. U tim smo naredbama koristili if naredbe bez pripadnog else dijela.i>n-k. Njezini se argumenti zovu n i k i tipa su int. k. Uoˇimo da c c je brojaˇ i inicijaliziran s n i da se u svakom prolasku kroz petlju smanjuje za c jedan dok ne postane jednak n-k. u funkciji main) int main(void) { long bnm. Ukoliko prethodna dva uvjeta nisu zadovoljena. return rezult. pri deklaraciji mogu se c inicijalizirati i ostali tipovi varijabli.5. c U glavnom programu (tj. izvrˇavanje funkcije ´e s c do´i do for petlje c for(i=n. nismo koristili vitiˇaste zagrade. ako je k == 0 ponovo se vra´a 1. Na taj naˇin. lokalne varijable koje postoje samo za vrijeme izvrˇavanja funkcije. U deklaraciji s long rezult=1. To su tzv. Tad se izlazi iz petlje.3. Nailaskom na return zaustavlja se izvodenje funkcije i u pozivni program se vra´a ona vrijednost koja stoji uz return. varijabla rezult je inicijalizirana s 1. int n. Ona raˇuna brojnik rezult=n(n−1) · · · (n−k+1) u formuli (2. . for(i=2. Funkcija vra´a vrijednost tipa long. U sljede´oj c c for petlji brojnik dijelimo brojevima 2. Isto tako.

} return 0. Argumenti koji su deklarirani u definiciji funkcije binom nazivaju se formalni argumenti. Nakon ˇto funkcija binom izvrˇi return naredbu. n i k (tipa int). Varijaz c c ble n i k deklarirane u funkciji main postoje za vrijeme izvrˇavanja funkcije s main dok varijable n i k deklarirane u funkciji binom postoje samo za vrijeme izvrˇavanja funkcije binom i nestaju nakon izlaska iz funkcije binom. Stoga c . Varijable n i k deklarirane u main su stvarni argumenti funkcije binom. Nula se interpretira kao uspjeˇni zavrˇetak programa.k).k). printf("%ld ".k++){ bnm=binom(n.5.30 POGLAVLJE 2. vrijednost koja s s stoji uz return se kopira u varijablu bnm koja stoji na lijevoj strani jednakosti.n<10. a svaka vrijednost s s razliˇita od nule signalizira zavrˇetak uslijed greˇke. Nju poziva operac s cijski sustav kad korisnik pokrene program. za ispis u printf naredbi koristimo znak konverzije %ld. Prilis kom poziva funkcije vrijednosti stvarnih argumenata se kopiraju u formalne argumente.n++){ for(k=0.2 Prototip funkcije Funkcija mora biti deklarirana prije mjesta na kome se poziva kako bi prevodilac znao broj i tip argumenata funkcije i tip rezultata koji vra´a.1 Funkcija main Svaki program mora sadrˇavati funkciju main budu´i da izvrˇavanje proz c s grama zapoˇinje prvom izvrˇnom naredbom u main-u. 2. ali to nije nuˇno budu´i da se radi o posve razliˇitim varijablama. } printf("\n").k<=n. c s s 2. } deklarirane su varijable bnm (tipa long). Program se zatim nastavlja ispisivanjem izraˇunate vrijednosti bnm. Njima smo dali ista imena n. k kao i stvarnim argumentima.5. Funkcija binom se poziva unutar dvostruke for petlje u naredbi bnm=binom(n. c Kako se radi o varijabli tipa long.bnm). Nakon zavrˇetka main vra´a s c cjelobrojnu vrijednost operacijskom sustavu koja ima znaˇenje izlaznog stac tusa. OSNOVE PROGRAMSKOG JEZIKA C for(n=0.

Prototip kao i sve druge naredbe zavrˇava s toˇka-zarezom.6 Polja Treba napisati program koja uzima dva trodimenzionalna vektora i ispituje njihovu okomitost. a a · b = a 1 b1 + a 2 b2 + a 3 b3 a = a2 + a2 + a2 1 2 3 a·b . Indeks polja uvijek kre´e s c od nule. onda se definicija funkcije moˇe z premjestiti iza funkcije main.. Na osnovu prototipa prevodilac moˇe provjeriti z da li je funkcija ispravno pozvana.). iza mjesta na kome se poziva) ako prije funkcije main postavimo prototip funkcije. Matematiˇki pojmovi vektora i matrica implemenc tiraju se u C-u pomo´u polja. kao ˇto smo uˇinili s c u prethodnom primjeru. Prototip funkcije dobiva se tako da se u definiciji funkcije ispusti ˇitavo c tijelo funkcije. s c Op´enito. Ako je takva c deklaracija postavljena prije funkcije main. prototip ima oblik c tip ime_funkcije(tip_1 arg_1. int k). Prototip za funkciju binom bi bio long binom(int n. C z nam stoga dozvoljava da definiciju funkcije stavimo iza funkcije main (odn. dakle. . Polje je niz indeksiranih varijabli istog tipa. On predstavlja samo deklaraciju funkcije koja kaˇe da je binom ime funkcije z koja uzima dva argumenta tipa int i vra´a argument tipa long.. Istaknimo da definicija funkcije mora biti u skladu s prototipom funkcije – tip funkcije te broj i tip argumenata moraju se podudarati.6. 2. a b . tip_2 arg_2. Ako u programu imamo puno funkcija takav naˇin c pisanja moˇe biti nepregledan jer funkcija main dolazi na kraju datoteke. c smjeˇtenih u memoriji na uzastopnim lokacijama. POLJA 31 je prirodno definiciju funkcije staviti prije funkcije main. Da bismo ispitali okomitost vektora iskoristit ´emo formulu za kosinus c kuta izmedu dva vektora: cos φ = gdje je skalarni produkt vektora.2.

\n").i<3. printf("Kosinus kuta = %f\n".&b[i]). s #include <stdio. u naˇem primjeru 10−10 . cos_phi).i<3. b[3]. uzimat c c s z ´emo da su vektori okomiti ˇim je kosinus njihovog kuta manji od nekog malog c c broja ε. } else{ printf("Vektori nisu okomiti. scanf(" %lf". } . b).i+1).&a[i]).++i){ printf("a[%d]= ". Vektori su okomiti ukoliko je kosinus kuta medu njima jednak nuli. Budu´i da se u raˇunu kosinusa pojavljuju greˇke zaokruˇivanja. int main(void) { double a[3]. OSNOVE PROGRAMSKOG JEZIKA C norma vektora. int i.\n"). for(i=0. } return 0. double kosinus_kuta(double x[]. double cos_phi.h> double epsilon=1. cos_phi).\n"). printf("Unesite vektor a. double y[]).0E-10. for(i=0.i+1).++i){ printf("b[%d]= ". if(fabs(cos_phi) < epsilon){ printf("Vektori su okomiti. scanf(" %lf".\n"). printf("Kosinus kuta = %f\n".h> #include <math. } cos_phi= kosinus_kuta(a.32 POGLAVLJE 2. } printf("Unesite vektor b.

suma=0.y). c c Ispred funkcije main naveden je prototip funkcije kosinus kuta double kosinus_kuta(double x[]. double suma. } double produkt(double x[].0. double y[]) { int i.2. . double suma.0. b[3]. cos_phi=cos_phi/(norma(x)*norma(y)). prototip nije potreban.6. for(i=0.++i) suma = suma + x[i]*y[i]. funkcija kosinus kuta poziva funkcije produkt i norma.++i) suma = suma + x[i]*x[i]. return suma.i<3. } Program se sastoji od funkcije main. } double kosinus_kuta(double x[]. u kojoj se poziva. cos_phi=produkt(x. POLJA 33 double norma(double x[]) { int i.i<3. double y[]). return sqrt(suma). Funkcija main zapoˇine deklaracijom polja a i b: c double a[3]. return cos_phi. no kako su one definirane ispred nje. To je nuˇno jer je funkcija definirana iza main funkcije. z S druge strane. funkcije norma koja raˇuna normu c vektora. for(i=0. funkcije produkt koja raˇuna skalarni produkt dva vektora i funkcije c kosinus kuta koja raˇuna kosinus kuta pozivaju´i funkcije produkt i norma. double y[]) { double cos_phi. suma=0.

} printf("Unesite vektor b. element po element. poziva se funkcija kosinus kuta. izraz &a[i] daje adresu i+1-vog elementa poja a.\n"). Tako. Njena definicija je dana iza main-a: . Izraz a[i] je i+1vi element poja (jer prvi ima indeks i=0). ime[2]. definicija polja je oblika c tip ime[max]. na primjer. . ime[max-1] U naˇem primjeru deklarirali smo polja a i b. Op´enita.\n"). OSNOVE PROGRAMSKOG JEZIKA C Uglate zagrade iza imena varijabli govore da je varijabla polje s tri elementa.. c Elemente polja indeksiramo cjelobrojnom varijablom. b[2]. a[1]. Pri ispisu c printf("a[%d]= ". for(i=0. Indeks polja stavlja se unutar uglatih zagrada. for(i=0. ime[1]. . scanf(" %lf". Stvarni indeksi u C-u uvijek poˇinju s nulom. a ime ime polja. Elementi tih polja su a[0].i<3. b[1]. oba sa po tri element tipa s double.++i){ printf("b[%d]= ".++i){ printf("a[%d]= ". gdje je tip tip varijable. U liniji cos_phi= kosinus_kuta(a..&a[i]).&b[i]). max broj elemenata polja. a[2] i b[0].i<3. On predstavlja jednu varijablu tipa double i moˇe se javiti u svim izrazima u kojima se moˇe pojaviti varijabla z z tipa double. scanf(" %lf". 2 i 3. pomaknuli smo indeks i za jedan kako bi korisnik imao dojam da komponente vektora imaju indekse 1.34 POGLAVLJE 2. Elementi polja ime su ime[0]. } uˇitavaju se dva vektora.i+1).i+1). U dijelu koda printf("Unesite vektor a.i+1). b).

double y[3]) { . Svaka varijabla definirana unutar tijela funkcije je lokalna varijabla. te su varijable posve razliˇite. kako bi prevodilac mogao rezervirati dovoljno memorijskog prostora za nju. Treba dobro uoˇiti razliku izmedu definicije varijable tipa polja i deklarac cije argumenta tipa polja u nekoj funkciji. Ipak. tako da razliˇite funkcije mogu imati argumente c istog imena. POLJA double kosinus_kuta(double x[]. U zavrˇnom dijelu funkcije main s . Funkc cija ´e vrˇiti operacije nad stvarnim argumentima.. U nekoj s z s drugoj funkciji lokalnoj varijabli moˇemo dati isto ime jer samim time ˇto su definirane u razliˇitim funkcijama. To su stvarni argumenti funkcije i sve operacije koja funkcija obavlja nad poljima obavljaju se nad stvarnim argumentima. nije pogreˇno deklarirati argumente tipa polja s njihovim s dimenzijama. Pri tome je odgovornost c s programera da osigura da stvarni argument funkcije bude odgovaraju´e dic menzije. } 35 Funkcija uzima dva argumenta tipa polja.. jer to u deklaraciji argumenata funkcije nije potrebno. return cos_phi. cos_phi=produkt(x. } U implementaciji funkcija norma i produkt koristili smo varijablu istog imena: suma. /* POGRESNO */ predstavljaju greˇku..y).. Definicije poput double x[].. Kad se definira varijabla obavezno navodimo njezinu dimenziju. Dimenzije polja c nisu navedene. Ti su argumanti deklarirani s uglatim zagradam koje oznaˇavaju da se radi o poljima.6. definiranim u funkciji main.2. double y[]) { double cos_phi. U deklaraciji argumenta tipa polja dimenziju ne treba s navoditi jer za taj argument ne´e biti rezerviran memorijski prostor. cos_phi=cos_phi/(norma(x)*norma(y)). Kod poziva funkcije formalni argumenti x i y zamijenjuju se imenima polja a i b. Ona postoji samo za vrijeme izvrˇavanja tijela funkcije. Isto vrijedi c c i za argumente funkcije. Tako smo mogli pisati double kosinus_kuta(double x[3].

5. OSNOVE PROGRAMSKOG JEZIKA C if(fabs(cos_phi) < epsilon){ printf("Vektori su okomiti. asin (arcsin). log10 (log) itd. } ispitujemo da li je apsolutna vrijednost kosinusa manja od epsilona i ispisujemo odgovaraju´u poruku. Ona daje greˇku ako je x = 0 i y ≤ 0 te x < 0 i c s y nije cijeli broj. cos_phi). y) = xy . } else{ printf("Vektori nisu okomiti. log (ln). 23 × 104 je 5. Ako koristimo matematiˇke funkcije iz standardne biblioteke. Biblioteka matematiˇkih funkcija sadrˇi brojne funkcije. Realne konstante s eksponentom piˇu se pomo´u slova E koje s c razdvaja mantisu i eksponent. tan (tg). Varijabla definirana ispred funkcije main je globalna varijabla.\n"). 2.23E4 itd. Tako varijabli epsilon moˇemo pristupiti i u main i u svim z ostalim funkcijama.h> i pri kompilaciji na kraj komandne linije c staviti -lm opciju. Uoˇimo da je varijabla epsilon definirana c c ispred funkcije main i tamo je inicijalizirana s 10−10 .36 POGLAVLJE 2. atan (arc tg).\n"). exp (e x ). Napomena. Njeno podruˇje djelovanja je od mjesta definicije do kraja datoteke u kojoj je definic rana.1 <math. printf("Kosinus kuta = %f\n".h> Funkcije poput printf i scanf nisu dio programskog jezika C ve´ pric padaju standardiziranim bibliotekama funkcija kojima je opskrbljen svaki C prevodilac. printf("Kosinus kuta = %f\n".6. Prototipovi funkcija iz standardnih biblioteka nalaze se u sistemskim datotekama zaglavlja koje je potrebno ukljuˇiti u svaki program koji ih c koristi. Treba razlikovati funkciju abs koja uzima argument tipa int i vra´a apc solutnu vrijednost broja (tipa int) od funkcije fabs koja uzima double i vra´a apsolutnu vrijednost (tipa double). cos. U C-u nema operatora potenciranja pa zato postoji funkcija pow: double pow(double. c . cos_phi). acos (arc cos).double) koja raˇuna pow(x. Takvim se varijablama moˇe pristupiti u svim funkcijama definiranim z u datoteci. onda c moramo ukljuˇiti zaglavlje <math. Izmedu ostalih c z sin. Na primjer. Sve one uzimaju jedan argument tipa double i vra´aju c rezultat tipa double.

h> int main(void) { double x=5. Zvjezdica (*) prije imena c varijable govori da se ne radi o varijabli tipa double nego o pokazivaˇu na c tip double. printf("x = %f. adresa od x= %p\n". Deklaracija double *px.h> int main(void) { double x=5.x. printf("x = %f. program c #include <stdio. Pokazivaˇ na tip double moˇe sadrˇavati samo adrese varijabli c z z tipa double.x. adresa od x= %p\n". return 0.&x).000000. } Program ´e ovisno o raˇunalu na kojem se izvodi ispisati neˇto kao c c s x=5.px). POKAZIVACI 37 2. } daje posve isti rezultat kao i prethodni program. adresa od x= 0065FDF0 Adresa varijable x ispisana je heksadecimalno. #include <stdio.7 Pokazivaˇi c Svaka varijabla deklarirana u C programu ima svoju adresu koju je mogu´e c dohvatiti putem adresnog operatora &. U naredbi . return 0. definira varijablu px kao pokazivaˇ na tip double.ˇ 2. px=&x. znak konverzije %p sluˇi za z ispis adresa.7. double *px. Na primjer. Adresu varijable nekog tipa moˇemo zapamtiti u varijabli koji je tipa z pokazivaˇ na dani tip. Na primjer.

s Vrijednost na koju pokazivaˇ pokazuje moˇemo dohvatiti putem operatora c z dereferenciranja *. ..px). double x. adresa od x= %p\n". POGLAVLJE 2. U c c c s deklaraciji varijable ona ukazuje da je varijabla tipa pokazivaˇ na dani tip.0.h> int main(void) { double x=5.. koja ´e na osnovu te vrijednosti izraˇunati sinus.38 px=&x. } Uoˇimo da * ima razliˇito znaˇenje u deklaraciji i u izvrˇnoj naredbi. return 0. <-. px=&x. OSNOVE PROGRAMSKOG JEZIKA C u varijablu px smjeˇta se adresa varijable x.. double x=5.. ˇto znaˇi kopiras c njem. c U izvrˇnoj naredbi ona predstavlja operator dereferenciranja. double *px. Na taj naˇin funkcija c c c ne moˇe (namjerno ili nenamjerno) promijeniti vrijednost varijable x koja z joj je dana kao argument. s Skalarni argumenti prenose se funkciji “po vrijednosti”. y=sin(x). . prethodni program bismo mogli zapisati u obliku #include <stdio.pokazivac na tip double <-. U primjeru. Ipak. x=2. printf("x = %f.. y=*px.y..varijable tipa double <-. px=&x.*px..px sada ima adresu varijable x <-. Na primjer. double *px.. To je vrlo korisno svojstvo koje u ovom sluˇaju c osigurava da raˇunanje vrijednosti sin(x) ne´e promijeniti vrijednost arguc c menta x.y prima vrijednost varijable x (=5) Na primjer.y.. u nekim sluˇajevima ˇelimo da funkcija promjeni vrijednost c z . prevodilac ´e kopirati vrijednost varijable x u formalni argument funkcije c sin.

) To moˇemo z z realizirati na sljede´i naˇin: c c #include <stdio. Kada treba z c c memorirati viˇe znakova koristimo se poljima tipa char. Nadeni samoglasnik ´e biti vra´en c c c kroz argument psamoglasnik. koji smo nazvali c psamoglasnik (pointer na samoglasnik). a mjesto na koje je naden vra´a se kroz return naredbu. Varijabla tipa char sluˇi za pam´enje pojedinaˇnih znakova. Ona ne´e mo´i promijeniti vrijedc c nost pokazivaˇa. cijeli broj n koji daje broj znakova u nizu linija i pokazivaˇ na varijablu tipa char. int n. U takvim sluˇajevima moramo se c posluˇiti pokazivaˇima. Funkcija treba vratiti nadeni samoglasnik i mjesto u nizu na kojem je naden. umjesto mjesta u nizu vra´a se -1. Na primjer. tako da se druga mora vraz titi kroz argument funkcije. if(c == ’a’ || c == ’e’ || c == ’i’ || c == ’o’ || c == ’u’) { *psamoglasnik=c. Ukoliko u danom nizu nema samoglasnika.ˇ 2. Znakovne konstante s formiraju se tako da se znak stavi u jednostruke navodnike. } Funkcija trazi uzima niz znakova linija. i++){ c=linija[i].h> int trazi(char linija[]. char *psamoglasnik) { int i. Treba napisati funkciju koja uzima niz znac kova i u njemu nalazi prvi samoglasnik. c U ovom primjeru funkcija mora vratiti dvije vrijednosti. . c Pogledajmo sljede´i primjer. ali ´e putem operatora dereferenciranja mo´i promijeniti c c c vrijednost na koju pokazivaˇ pokazuje. (Funkcija ne moˇe vratiti polje. Kroz return naredbu moˇe se vratiti samo jedna vrijednost. kao kod funkcije scanf. for(i=0.7. Funkciji dajemo kao argument pokazivaˇ koji sadrˇi z c c z adresu varijable koju treba promijeniti. POKAZIVACI 39 svog argumenta. ’?’ itd. i<n. } } return -1. ’a’. ’/’. char c. return i.

’i’. if(no != -1){ printf("Prvi samoglasnik = %c\n".’g’.’.’k’. char znak. Poziv funkcije trazi mogao bi izgledati ovako: int main(void) { char ime[]={’P’.19. return 0.no). Ukolikoje petlja for izvrˇena a da samoglasnik nije naden.’C’.40 POGLAVLJE 2. Uoˇimo da smo pet testova spojili c operatorom logiˇko ILI.’k’.’}.’e’.’i’. } else printf("Nema samoglasnika.’z’.’ ’. Svako se c polje prilikom deklaracije moˇe inicijalizirati tako da se nakon znaka jednaz kosti u vitiˇastim zagradama navedu svi elementi polja.’s’. Njena se adresa s predaje funkciji trazi. ’j’.&znak). printf("Nalazi se na mjestu %d\n". } Varijabla u koju smjeˇtamo nadeni samoglasnik je znak. Nakon toga se u pozivni program vra´a poloˇaj znaka koji je dan varijablom c z c i.’m’. c Dimenziju polja ne moramo nuˇno navesti jer ´e ju prevodilac sam izraˇunati z c c na osnovu inicijalizacijskog niza. .\n").’a’. a s lokacija na koju pokazuje psamoglasnik se ne mijenja. vra´a se -1. no=trazi(ime. odvojeni zarezom. u naredbi *psamoglasnik=c.znak). c Svi logiˇki operatori dani su u ovoj tabeli: c Operator && || ! Znaˇenje c logiˇko I c logiˇko ILI c logiˇka negacija (unarno) c Ako je samoglasnik naden. Za svaki znak u if nac redbi ispitujemo da li je samoglasnik.’r’.’o’.’ ’. se znak c=linija[i] kopira na lokaciju na koju pokazuje psamoglasnik. int no. OSNOVE PROGRAMSKOG JEZIKA C U for petlji prolazimo kroz ˇitav niz znakova. U deklaraciji polja ime susre´emo se s inicijalizacijom polja.’r’.

niz_znakova).h> (zbog funcije srtlen). i=0.i. c c #include <stdio.brojac). } } Program zapoˇinje ukljuˇivanjem standardnih datoteka zaglavlja <stdio. . scanf("%s". i<j. raˇuna s c c broj numeriˇkih znakova u nizu i ispisuje uˇitani niz u obrnutom poretku. for(i=0. } void inv(char s[]) { int c.niz_znakova).h> #define MAX 128 void inv(char []). POLJA ZNAKOVA 41 2.j. int i. s[j]=c.j=strlen(s)-1.j--) { c=s[i].8.h> #include <string. return 0.8 Polja znakova Napiˇimo program koji uˇitava niz znakova limitiran bjelinama. i++. while((c=niz_znakova[i]) !=’\0’) { if(c>=’0’ && c<=’9’) ++brojac.2. int main(void) { char niz_znakova[MAX].h> c c (zbog printf i scanf) te <string. inv(niz_znakova). Zatim #define MAX 128 c uvodi simboliˇku konstantu MAX koja ima vrijednost 128. i++.brojac=0. printf("%s\n". s[i]=s[j].c. } printf("Broj ucitanih numerickih znakova = %d\n".

Funkcija je tipa void ˇto znaˇi da ne vra´a nikakvu vrijednost u pozivni program. je prototip funkcije inv koja je definirana iza main funkcije. Na taj naˇin umjesto konstanti moˇemo c z koristiti simboliˇka imena. Deklaracije obiˇnih varijabli i polja moˇemo slobodno mijeˇati (ako su c z s istog tipa) te smo stoga mogli pisati char niz_znakova[MAX]. #define je prepprocesorska naredba kojom se definira simboliˇko ime. int i. #include je preprocesorska naredba kojom se u datoteku. c Linija void inv(char []). U naˇem primjeru ime je MAX. Prva izvrˇna naredba je s .brojac=0.42 POGLAVLJE 2. z s Vaˇna je samo prisutnost uglatih zagrada koje signaliziraju da je argument z polje tipa char. Preprocesor je program koji prolazi kroz izvorni kod prije prevodioca i izvrˇava naredbe koje su mu nas mijenjene. Na primjer. ukljuˇuje sadrˇaj datoteke c z navedene u #include liniji. char c.c. Ti su znakovi smjeˇteni jedan do drugog u memoriji s i indeksirani su od 0 do 127. Preprocesor nalazi u programu svako pojavljivanje simz bola MAX i zamijenjuje ga sa 128. varijablu tipa char moˇemo iniijalizirati znakom a na z ovaj naˇin: c char c=’a’. OSNOVE PROGRAMSKOG JEZIKA C Naredba #define je preprocesorska naredba. Varijabla niz znakova iz funkcije main je polje od MAX (=128) znakova tj. Vidjeli smo da pri deklaraciji polja kao argumenta funkcije dimenziju polja ne moramo navesti. varijabli tipa char. Takva inicijalizacija kod same deklaracije varijable mogu´a je za sve vrste c varijabli. na onom mjestu na kojem se nalazi. Na primjer. U ovom primjeru vidimo da u prototipu niti ime varijable nije vaˇno. a vrijednost koja mu c s se pridruˇuje je 128. Ona ´e s c c c vrˇiti inverziju niza znakova. pa je stoga ispuˇteno. U deklaraciji varijabli i i brojac varijabla brojac je inicijalizirana nulom. s Argument funkcije inv je niz znakova. umjesto char niz_znakova[MAX].

Opez rator != je logiˇki operator usporedivanja. c c Deskriptor "%s" oznaˇava da funkcija scanf mora uˇitati niz znakova u c c varijablu niz znakova i dodati \0 iza posljednjeg znaka. Kada je niz stvarni c argument funkcije ona ne dobiva kopiju ˇitavog niza ve´ adresu prvog elec c menta u nizu. while((c=niz_znakova[i]) !=’\0’) { if(c>=’0’ && c<=’9’) ++brojac.h> deklariran je niz funkcija namjenjenih radu sa stringovima. To je znakovni niz koji se sastoji od sljede´ih znakova: c D o b a r d a n . Pod bjelinama se osim bjelina unesenih razmaknicom smatraju i znakovi uneseni tipkama Tab (tabulator) i Enter (prijelaz u novi red). U while petlji z i=0. tako je strlen("Dobar dan. c c Znakovni niz (ili string) predstavlja poseban tip podatka u C-u. niz_znakova). Znak konverzije "%s" c oznaˇava da se uˇitava niz znakova. U funkciji se ˇlanovi niza mogu dohvatiti (i mijenjati) isto kao c i u pozivnom programu pomo´u indeksa. U datoteci zaglavlja <string.2. Jedna od njih je i strlen koja vra´a duljinu niza c znakova. i++. } prvo se varijabli c pridruˇuje niz znakova[i]. \n \0 Uoˇimo da je na kraj niza dodan nul-znak ’\0’ koji ima ulogu signaliziranja c kraja niza znakova. Uˇitavanje niza c znakova sa ulaza prestaje s prvom bjelinom. 43 koja uˇitava niz znakova u varijablu niz znakova. Znakovni niz se time razlikuje od niza znakova. c Uoˇimo da ispred varijable niz znakova u pozivu funkciji scanf nema adresnog operatora &.8.\n")=11. kao npr. Niz znakova je niz bilo kakvih znakova. Stoga izrazi poput z c=niz_znakova[i] mogu sudjelovati u sloˇenijim izrazima. POLJA ZNAKOVA scanf("%s". Rezultat usporedivanja c .\n”. a znakovni niz (string) je niz znakova ˇiji c je zadnji znak nul-znak. Konstantan znakovni niz (konstantan string) je svaki niz znakova smjeˇten u dvoss truke navodnike. c U C-u izraz pridruˇivanja ima vrijednost koja je jednaka vrijednosti lijeve z strane nakon pridruˇivanja. To je op´enito pravilo za nizove. ”Dobar dan. ne raˇunaju´i nul-znak. a zatim se vrijednost izraza z pridruˇivanja (to je vrijednost varijable c) usporeduje s znakom ’\0’.

provjerava se da li je znakovna vrijednost smjeˇtena u varijablu c broj. Po zavrˇetku while petlje iz funkcije main varijabla brojac sadrˇi broj s z numeriˇkih znakova u znakovnom nizu. i<j. Naredbom if(c>=’0’ && c<=’9’) ++brojac.j--) . z Gornja while petlja mogla je biti napisana i na sljede´i naˇin: c c i=0. oba ova oblika pove´avaju varijablu za jedan. z Relacijski izrazi kao ˇto je to c != ’\0’ ili i<=10 imaju logiˇku vrijeds c nost istine ili laˇi koja se u C-u reprezentira cijelim brojem: 1 (i bilo koja z vrijednost razliˇita od nule) predstavlja istinu. c c c Funkcija inv sastoji se od jedne for petlje for(i=0. a 0 laˇ. Izraz z c=niz_znakova[i] !=’\0’ ekvivalentan je izrazu c=(niz_znakova[i] !=’\0’) koji bi pridruˇio varijabli c vrijednost 0 (laˇ) ako niz znakova[i] sadrˇi z z z znak ’\0’ ili 1 (istina) ako niz znakova[i] ne sadrˇi znak ’\0’.44 c!=’\0’ POGLAVLJE 2. while(c !=’\0’) { if(c>=’0’ && c<=’9’) ++brojac. c=niz_znakova[i]. Pri tome su bitne zagrade je operatori pridruˇivanja imaju z najniˇi prioritet. Uoˇimo da smo pisali ++brojac c c umjesto brojac++. c=niz_znakova[0]. Operatore inkrementiranja i dekrementiranja moˇemo z pisati prije i poslije varijable. Pri s tome koristimo ˇinjenicu da su brojevi u skupu znakova poredani u prirodnom c poretku (isto vrijedi i za slova engleske abecede). i++.j=strlen(s)-1. } Vidimo da koriˇtenje izraza pridruˇivanja unutar sloˇenijih izraza ˇini kˆd s z z c o kompaktnijim. Iako medu tim zapisima postoji razlika koju ´emo nauˇiti kasnije. OSNOVE PROGRAMSKOG JEZIKA C je istina ako varijabla c ne sadrˇi nul-znak ’\0’. i++. To je razlog zbog c z kojeg bi u prethodnom primjeru varijabla c dobila vrijednost 0 ili 1.

Petlja se izvrˇava sve dok je izraz i<j istinit. s[j]=c. POLJA ZNAKOVA 45 u kojoj se inicijaliziraju dvije varijable: i koja se pozicionira na poˇetak c stringa i j koja se pozicionira na kraj.2. Pri tome posljednji nul-znak nismo pomicali s njegovog mjesta. Evidentno je da ´e na izlazu iz for petlje poredak znakova u c znakovnom nizu biti invertiran. s[i]=s[j]. koji su na pozicijama i i j. u dijelu u kome se mijenja brojaˇ petlje mijenjaju je c c oba brojaˇa: i se pove´ava. Viˇetruke inicijalizacije odvajaju s se zarezom. a j se smanjuje. Unutar s tijela petlje vrˇi se zamjena znakova s c=s[i]. Sliˇno. Pri toj zamjeni morali smo koristiti pomo´nu c varijablu c. .8. Ove se dvije naredbe ponovo c c odvajaju zarezom.

njene argumente i vrijednost koju vra´a. Zamjena se vrˇi prije prolas ska preprocesora. tako da se mogu komentirati i preprocesorske naredbe.Poglavlje 3 Konstante i varijable 3. Mnoge verzije jezika C dozvoljavaju da i drugi znakovi budu ukljuˇeni unutar stringova i komentara. z z z s /* Ovo je komentar. c 3. horizontalni i vertikalni tabulator te znak za prijelaz u novi red. decimalne znamenke 0-9 te specijalne znakove + ! < : ? > . */ Prevodilac zamijenjuje svaki komentar jednom bjelinom. / " ) . c c s Komentar zapoˇinje znakom /* i zavrˇava znakom */. Komentar c s o se moˇe umetnuti bilo gdje u programu i moˇe sadrˇavati viˇe linija teksta. Minimalno je potrebno komentirati svaku funkciju u s c c programu.1 Znakovi C koristi sljede´i skup znakova: velika i mala slova engleske abecede A-Z c i a-z.2 Komentari U program je potrebno umetati komentare radi lakˇeg razumijevanja njes govog funkcioniranja. objaˇnjavaju´i zada´u funkcije. 46 . = ’ [ _ % & # ~ \ | ] { } (bjelina) Pod bjelinom se podrazumijeva. * ^ ( . osim sam bjeline. Tekst izmedu tih graniˇnika ignorira se prilikom prevodenja programa u izvrˇni kˆd.

c c Standard C99 omogu´ava koriˇtenje drugog tipa komentara koji dolazi iz c s Jave i C++a.. // brojac znakova Prevodilac ignorira tekst od znaka // do kraja linije. s Jednako tako ne moˇemo komentirati dio programa koji ve´ sadrˇi komentar.3. Prevodilac ´e ignorirati text sve c do prvog znaka */ na koji naide. kˆd o /* x=72.0. Velika i mala slova se razlikuju i znak (underscore) smatra se slovom.0. . c Na primjer. y=31. najbolje z je koristiti preprocesor (vidi Poglavlje 8). z c z Na primjer.ˇ ˇ 3. Komentar zapoˇinje znakom // i prostire se do kraja linije. IDENTIFIKATORI I KLJUCNE RIJECI 47 Komentari ovog tipa mogu dovesti do gubitka kˆda u situacijama u kojim o je ispuˇten jedan graniˇnik. U standardu C99 ta je granica dignuta na 63 znaka. /* Inicijalizacija */ Prevodilac ´e javiti sintaktiˇku greˇku jer zadnji */ graniˇnik nema odgovac c s c raju´eg /* graniˇnika.0. /* Ovo je drugi komentar. Na imena vanjskih varijabli postoje ve´a ograniˇenja i ona ne bi trebala biti duˇa od 6 znakova c c z (C90). odnosno 31 (C99). U primjeru s c /* Ovo je prvi komentar.0. Duljina imena je proizvoljna premda je prema standardu C90 prevodilac duˇan prez poznati samo prvih 31 znakova. int n.0. */ je neispravan jer komentar zapoˇet prvim /* graniˇnikom zavrˇava s prvim c c s */ graniˇnikom. */ prvi komentar smo zaboravili zatvoriti. Stariji prevodioci ne prihva´aju ovaj naˇin komentiranja. x=72. ˇto znaˇi u liniji c s c x=72.3 Identifikatori i kljuˇne rijeˇi c c Identifikatori su imena koja se pridruˇuju razliˇitim elementima programa z c s kao ˇto su varijable polja i funkcije. ˇto rezultira gubitkom naredbe x=72. c c Ako treba komentirati veliki dio programa koji u sebi sadrˇi brojne komentare. Sastoje se od slova i brojeva u bilo kojem c poretku s ograniˇenjem da prvi znak mora biti slovo. /* Inicijalizacija */ 3.

tzv. KONSTANTE I VARIJABLE Primjer 3. far fortran huge near pascal treba izbjegavati jer su rezervirana za varijable i . Kljuˇne rijeˇi prema C90 c c standardu su: auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while Standard C99 uveo je i sljede´e kljuˇne rijeˇi: c c c inline restrict Bool Complex Imaginary U nekim verzijama jezika postoje i sljede´e (nestandardne) kljuˇne rijeˇi: c c c ada asm entry Imena koja poˇinju sa znakom c funkcije iz standardne biblioteke. imaju posebno znaˇenje u jeziku c c c c i stoga se ne mogu koristiti kao identifikatori.2 Primjeri nepravilno napisanih identifikatora: 3dan "x" ac-dc (prvi znak je broj) (nedozvoljeni znak ") (nedozvoljeni znak -) Jedan broj rijeˇi. kljuˇne rijeˇi.1 Primjeri pravilno napisanih identifikatora: x names y13 Pov1 sum_1 table _temp TABLE Primjer 3.48 POGLAVLJE 3.

c char: znakovni podatak. float. c float: broj s pokretnim zarezom u jednostrukoj preciznosti. int.3.4 Osnovni tipovi podataka char.4. U memoriji tipiˇno zauzima jedan oktet. U memoriji zauzuma manje memorijskog prostora od podatka tipa int pa moˇe prikazati manji raspon cijelih z brojeva. Sadrˇava jedan znak iz sustava znakova koji raˇunalo z c koristi. ako varijabla odredenog integralnog c tipa zauzima n bitova memorije i naˇin kodiranja je 2-komplement. long: “dugi” cjelobrojni podatak. tipiˇno zauzima ˇetiri okteta. Primjenom kvalifikatora unsigned dobivamo tipove podataka . Tako tip int mora imati ˇirinu najmanje 16 bitova.1 Cjelobrojni podaci Cjelobrojni tipovi podataka sluˇe pam´enju cijelih brojeva. U memoriji tipiˇno c zauzima osam okteta. Tako dobivamo nove cjelobrojne tipove: short int ili kra´e short c long int ili kra´e long c short: “kratki” cjelobrojni podatak.4. 3. Tipiˇno zauzima 4 okteta. short i long pokrivaju podruˇje pozitivnih i negac tivnih brojeva. double. Svaki tip z c pokriva odreden raspon cijelih brojeva ovisno o koliˇini memorije koju zac uzima i naˇinu kodiranja. Na primjer. c c U memoriji double: broj s pokretnim zarezom u dvostrukoj preciznosti. dok tip long mora imati minimalno 32 bita. OSNOVNI TIPOVI PODATAKA 49 3. C propisuje samo minimalnu preciznost pojedinih cjelobrojnih tipova. s Cijeli brojevi tipa int. c int: cjelobrojni podatak. onda je c n−1 n−1 pokriveni raspon od −2 do 2 − 1. Osnovni tipovi podataka su Koliˇina memorije koju pojedini tip zauzima ovisi o raˇunalu. Mi navodimo c c tipiˇne vrijednosti. Cjelobrojni tip int moˇe se modificirati pomo´u kvalifikatora short i z c long. U memoriji zauzuma viˇe memorijskog s prostora od podatka tipa int pa moˇe prikazati ve´i raspon cijelih z c brojeva.

• INT MIN. Na nekom c raˇunalu dio sadrˇaja datoteke <limits. LONG MAX: Minimalna i maksimalna vrijednost za tip long.h> u kojoj su graniˇne vrijednosti definirane. long). SHRT MAX: Minimalna i maksimalna vrijednost za tip short. KONSTANTE I VARIJABLE unsigned int ili kra´e unsigned c unsigned short int ili kra´e unsigned short c unsigned long int ili kra´e unsigned long c koji zauzimaju isti memorijski prostor kao osnovni tipovi podataka (int.h> mogao bi biti sljede´i: 2 c z c #define #define #define #define #define #define #define #define #define SHRT_MIN SHRT_MAX INT_MIN INT_MAX LONG_MIN LONG_MAX USHRT_MAX UINT_MAX ULONG_MAX (-32768) 32767 (-2147483648) 2147483647 (-9223372036854775808L) 9223372036854775807L 65535 4294967295U 18446744073709551615UL (Oznake L. Oni pokrivaju stoga pribliˇno dvostruko ve´i raspon pozitivnih cijelih z c brojeva od osnovnih tipova. ˇto je u danaˇnje vrijeme uglavnom c c c s s 32 bita. • USHRT MAX: Maksimalna vrijednost za tip unsigned short. • UINT MAX: Maksimalna vrijednost za tip unsigned. Koriˇtenje ˇirih cjelobrojnih tipova moˇe stoga imati za posljedicu s s z sporije izvrˇavanje programa. Za njegovo c pam´enje koristi se jedna raˇunalna rijeˇ. 1 2 Na Unix sustavima ta se datoteka najˇeˇ´e nalazi u /usr/include/ direktoriju. INT MAX: Minimalna i maksimalna vrijednost za tip int. Stoga postoji sistemska datoteka c 1 zaglavlja <limits. c sc Zaˇto su negativne vrijednosti u zagradama objaˇnjeno je u sekciji 8. • ULONG MAX: Maksimalna vrijednost za tip unsigned long. s Koje su minimalne i maksimalne vrijednosti cjelobrojnih varijabli pojedinih tipova? Odgovor je zavisan o raˇunalu. s s . • LONG MIN. Tip int je prirodan cjelobrojni podatak za dano raˇunalo.U i UL su objaˇnjene u sekciji 3. short.6.50 POGLAVLJE 3. a mogu reprezentirati samo pozitivne cijele brojeve (ukljuˇivˇi c s nulu).) Ovdje su s • SHRT MIN.

OSNOVNI TIPOVI PODATAKA 51 Ovdje imamo primjer raˇunala na kojem tip short zauzima 2 okteta. ..3.0.1. . Sljede´i program ispisuje s c c c gornje vrijednosti: #include <stdio. . Na vaˇem raˇunalu ovi rasponi mogu biti drugaˇiji. . a tip long 8 okteta. . • unsigned: 0. • int: −231 = −2147483648. printf("USHRT_MAX = %hu\n". printf("LONG_MAX = %ld\n".l_min)... printf("SHRT_MIN = %hd\n". .s_max). • short: −215 = −32768. .1. LONG_MAX. Stoga imamo sljede´e raspone: c • unsigned short: 0. . 263 − 1.. LONG_MIN.. INT_MAX. . 215 − 1 = 32767.h> int main() { short s_max short s_min unsigned short us_max int i_max int i_min unsigned ui_max long l_max long l_min unsigned long x_max = = = = = = = = = SHRT_MAX.i_min).x_max). • unsigned long: 0. printf("UINT_MAX = %u\n". -1.s_min).h> #include <limits. } . . 216 − 1 = 65535. . printf("INT_MAX = %d\n". . 232 − 1 = 4294967295.4. return 0. printf("INT_MIN = %d\n". -1. • long: −263 . 264 − 1 = 18446744073709551615. .us_max).1. . 231 − 1 = 2147483647. . UINT_MAX. .l_max)...1. printf("ULONG_MAX = %lu\n".0. . ..ui_max).0. printf("LONG_MIN = %ld\n".1. . . . USHRT_MAX. . SHRT_MIN. . INT_MIN. . . . ULONG_MAX.1. tip int c 4 okteat. .i_max). -1.. printf("SHRT_MAX = %hd\n".

gdje je n ˇirina tipa (u bitovima). Rezultat koji se dobiva je sljede´i: kada se najve´em broju bez predznaka c c doda jedan dobiva se nula. Zbog toga je tip char ustvari cjelobrojni tip podataka vrlo malog raspona. 3. printf("ULONG_MAX+1 = %lu\n". KONSTANTE I VARIJABLE Uoˇite kontrolne znakove s kojima se ispisuju pojedini integralni tipovi.s_min-1). Brojevi bez predznaka su dakle “poredani u krug”. kad se doda dva dobiva se jedan itd.l_max+1).52 POGLAVLJE 3. printf("INT_MAX+1 = %d\n". printf("SHRT_MIN-1 = %hd\n".4.i_max+1). Dodavanje jedinice c s najve´em pozitivnom broju daje najve´i negativni broj. a oduzimanje jedic c nice od najve´eg negativnog broja daje najve´i pozitivni broj.i_min-1). 3 ASCII je kratica od “American Standard Code for Information Interchange”.x_max+1). s c Standard C99 uvodi dodatne tipove cijelih brojeva long long i unsigned long long.2 Znakovni podaci Podatak tipa char namijenjen je pam´enju individualnih znakova. ISO Latin 2 (za srednjoeuropske jezike) itd. printf("USHRT_MAX+1 = %hu\n". Jedan od najstarijih je tzv. printf("INT_MIN-1 = %d\n". printf("UINT_MAX+1 = %u\n". Znac kovi se u raˇunalu kodiraju i sve manipulacije sa znakovima obavljaju se c c putem njihovih numeriˇkih kodova.s_max+1). ali ponaˇanje ovisi o naˇinu kodiranja negativnih s c brojeva (2-komplement u naˇem sluˇaju). .ui_max+1). c ˇ Sto se deˇava ako pokuˇamo generirati cijeli broj ve´i od najve´eg ili s s c c manji od najmanjeg dopuˇtenog? Da bismo to vidjeli dodajmo prethodnom s programu sljede´e naredbe: c printf("SHRT_MAX+1 = %hd\n". Npr.3 ASCII kˆd ima numeriˇki raspon od 0 do 127 i dovoljno je 7 o o c c o bitova za njegovo pamˇenje. I ovdje su c c brojevi “poredani u krug”.us_max+1). B ima kˆd 66 itd.l_min-1). Postoje razliˇiti naˇini kodiranja znakova. c c ASCII kˆd. Tip long long mora biti implementiran s minimalno 64 bita. odnosno implementirana je aritmetika modulo 2n . printf("LONG_MIN-1 = %ld\n". printf("LONG_MAX+1 = %ld\n". slovo A (veliko) ima ASCII kˆd 65. s Kod brojeva s predznakom imamo sliˇno ponaˇanje. Budu´i da ASCII kˆd ne sadrˇi slova iz europskih jezika razvijeni o c o z su razni drugi osam-bitni kodovi od kojih treba spomenuti ISO Latin 1 (za zapadno europske jezike).

2].3. Kljuˇna rijeˇ c z c c c Bool je izabrana da se izbjegnu konflikti s aplikacijama koje su implementirale svoj tip bool. Korisnik moˇe ukljuˇiti datoteku zaglavlja <stdbool. Svaki cjelobrojni tip moˇe c z preuzeti ulogu logiˇkog tipa tako ˇto se vrijednost razliˇita od nule interpretira kao istina. dvostruke i ˇetverostruke preciznosti. I nadalje se cjelobrojne varijable mogu koristiti za prezentaciju z logiˇkih vrijednosti. konstantama. onda tip unsigned char poprima pozitivne cjelobrojne vrijednosti izmedu 0 i 255.3 Logiˇki podaci c U ANSI C-u (standard C90) ne postoji poseban logiˇki tip. S druge strane. (Na pojedinim raˇunalima double moˇe biti isto c c z ˇto i long double. koji je namijenjen kodiranju znakova iz razliˇitih jezika. c 3.4 Realni podaci Podaci tipa float. Ako se za char koristi jedan oktet. c 3. Uobiˇajeno je brojeve s c c c tipa float.h> definiran je cjelobrojni tip wchar t. Mada su brojevi s pokretnim zarezom konaˇan podskup c skupa realnih brojeva. Na tip char s moˇemo primijeniti kvalifikatore signed i unsigned kao i na ostale cjeloz brojne tipove.4. Oni su analogni cjelobrojnim tipovima short.) Napomena.4.4. double i long double su realni brojevi koji zbog specifiˇnog naˇina pam´enja u raˇunalu imaju naziv brojevi s pokretnim zac c c c rezom (eng. U datoteci zaglavlja <stddef. dok tip signed char poprima pozitivne i negativne cjelobrojne vrijednosti izmedu -127 (ili 128) i 127.h>. radi kratko´e ´emo ih jednostavno zvati realnim broc c jevima (ili realnim varijablama. Sirina tipa char te njegova minimalna i maksimalna vrijednost dani su u datoteci <limits. ˇto se lako provjerava pomo´u sizeof operatora [vidi s s c sekciju 4. OSNOVNI TIPOVI PODATAKA 53 U C-u je char cjelobrojni tip podatka s vrlo malim rasponom. Standard C99 uvodi poseban logiˇki tip Bool koji prima samo vrijednosti z c 0 i 1 (“laˇ” i “istina”). int i long. podacima). ˇiroki znas kovni tip. ali upotreba novog tipa moˇe uˇiniti program jasnijim. tzv. c s c a nula kao laˇ.h> u kojoj se definira bool z c kao sinonim za Bool te se uvode simboliˇka imena true i false. Pravilo je da se kvalifikatori signed i unsigned ne koriste s tipom char ako on sluˇi za manipulaciju sa znakovima. Najˇeˇ´e c sc je to jedan oktet ˇto je dovoljno za sve osam-bitne kodove. floating point numbers). Jezik ne propisuje ˇirinu pojedinih tipova brojeva s pokretnim zarezom s ve´ to ovisi o raˇunalu na kojem se program izvrˇava. svojstva c c s sustava brojeva s pokretnim zarezom propisana su IEEE standardom kog . Svaki sljede´i tip pokriva ve´i raspon realnih brojeva i c c ima viˇu preciznost (ve´i broj znaˇajnih znamenaka). double i long double nazivati brojevima jednostruke. ve´ samo ako ga koristimo z c ˇ kao cjelobrojni tip malog raspona.2.

Na nekom raˇunalu dio c c sadrˇaja datoteke <float. dok se one s DBL odnose c na tip double. Kao i u sluˇaju cijelih brojeva vrijednosti ovisne o hardwareu stavljene c su u standardnu datoteku zaglavlja. najmanji (normalizirani) broj s pokretnim zarezom. najve´i (norc c malizirani) broj s pokretnim zarezom i najve´i dekadski eksponent.7976931348623157E+308 (+308) Konstante koje poˇinju s FLT odnose se na tip float. KONSTANTE I VARIJABLE se danas pridrˇava ve´ina proizvodaˇa hardwarea. najmanji dekadski eksponent.798 · 10308 . Sustav brojeva s pokretnim zarezom sadrˇi simboliˇke veliˇine +Inf i -Inf z c c koje predstavljaju plus i minus beskonaˇno. Njih dobivamo kada raˇunskim c c 4 Ukoliko je realan broj u podruˇju koje pokrivaju brojevi s pokretnim zarezom. Stoga ´e svojstva tipa z c c c float i tipa double na ve´ini strojeva biti ona propisana IEEE standardom c (standard IEEE ne propisuje strogo brojeve ˇetverostruke preciznosti). c .175494351E-38F (-37) 3.0 i prvog ve´eg broja s pokretnim zarec zom.17549 · 10−38 ≈ 2−126 ≤ |x| ≤ (1 − 2−24 ) · 2128 ≈ 3.h>. On je stoga dobra mjera preciznosti brojevnog sustava.54 POGLAVLJE 3.402823466E+38F (+38) 2. U sluˇaju brojeva s pokretnim zarezom c to je datoteka <float.40282 · 1038 . Navec dimo samo granice u kojima se ti brojevi kre´u (prema IEEE standardu): U c jednostrukoj preciznosti (float) imamo 1.h> mogao bi biti sljede´i: z c #define #define #define #define #define #define #define #define #define #define FLT_EPSILON FLT_MIN FLT_MIN_10_EXP FLT_MAX FLT_MAX_10_EXP DBL_EPSILON DBL_MIN DBL_MIN_10_EXP DBL_MAX DBL_MAX_10_EXP 1.225 · 10−308 ≈ 2−1022 ≤ |x| ≤ (1 − 2−53 ) · 21024 ≈ 1. Ona sadrˇi niz simboliˇkih konstanti koje daju z c razliˇite informacije o realnim tipovima podataka.2204460492503131E-16 2.192092896E-07F 1.2250738585072014E-308 (-307) 1. Ovdje redom imamo: strojni epsilon. On je bitan utoliko ˇto pri aproksimaciji realnog broja najbliˇim brojem s z 4 s pokretnim zarezom ˇinimo greˇku koja ne prelazi jednu polovicu strojnog c s epsilona. dok u dvostrukoj (double) vrijedi 2. c Ovdje ne´emo ulaziti u diskusiji brojeva s pokretnim zarezom. Strojni epsilon je udaljenost izmedu broja 1.

printf("DBL_MIN/0 = %e\n". printf("DBL_MAX = %e\n".4.h> #include <float. overflow).h> #include <math. double Complex i long double Complex.0)). K tome se uvode joˇ i tri imaginarna tipa: float Imaginary. printf("DBL_MAX = %e\n".5 Varijable i deklaracije Varijabla je objekt koji zauzima dio memorijskog prostora i koji se moˇe z dohvatiti putem svog imena. Ukljuˇivanje zaglavlja <complex.1).5.1 = %e\n". Operacija koja matematiˇki nije dobro definirana (u c proˇirenom skupu realnih brojeva) daje kao rezultat vrijednost NaN (eng. } Uoˇimo da smo morali ukljuˇiti datoteku zaglavlja <math.h> supc √ stituira ime complex za Complex. return 0. Malu ilustraciju tih svojstava predstavlja sljede´i program: c #include <stdio. Standard uvodi tri kompleksna tipa float Complex.h> radi funkcije c c √ sqrt(x)= x.x_max).x_max*1.2 gdje ´e biti viˇe rijeˇi od memorijskoj klasi i dosegu).h> int main(void) { double x_max=DBL_MAX. s Not a Number). printf("\n"). Memorijska klasa varijable je najˇeˇ´e odredena implicitno c sc poloˇajem varijable u programu i stoga ´emo ju za sada zanemariti (vidi z c sekciju 9.5 Kompleksni podaci Kompleksni podaci su dodani jeziku tek u standardu C99. c s c . printf("sqrt(-1) = %e\n". s double Imaginary i long double Imaginary. Kompilirati treba s opcijom -lm. imaginary za Imaginary te I za −1. memorijska klasa varijable i doseg. jedna float Complex varijabla sastoji se od dvije float varijable koje reprezentiraju realni i kompleksni dio broja.sqrt(-1. VARIJABLE I DEKLARACIJE 55 operacijama izademo iz podruˇja koje pokrivaju brojevi s pokretnim zarec zom (eng.x_min/0).3. double x_min=DBL_MIN. printf("DBL_MAX*1. Varijabla ima tri atributa o kojim ovisi kako ´e c se podatak pohranjen u memoriji interpretirati: to su tip . Na primjer. 3.x_max). 3.

.1). a ime njeno ime. Doseg varijable je dio programa u kojem je varijabla vidljiva. Kada je varijabla deklarirana unutar neke funkcije (npr. Deklaracija odreduje ime i tip varijable i ima sljede´i oblik: c tip ime. c s Mogu´e je varijablu deklarirati i izvan svih funkcija. vektor[2]. obiˇno na poˇetku datoc c c teke i tada takve varijable nazivamo vanjskim varijablama (vidi sekciju 9.b. .. KONSTANTE I VARIJABLE Prije upotrebe varijablu je potrebno deklarirati. short b.5. onda su elementi polja vektor[0]. . Iz drugih funkcija toj se varijabli ne moˇe pristupiti. Nasuprot tome. vektor polje od 10 elementa nekog tipa. c je cjelobrojna varijabla bez predznaka. Ako c je npr.1). u deklaracijama int a. a d je znakovna varijabla. odnosno.c. a i b su cjelobrojne varijable.56 POGLAVLJE 3. gdje je tip varijable. Varijable istog tipa mogu´e je deklarirati u istom retku c kao u primjeru short a. short c. Varijable se deklariraju na poˇetku funkcije.1 Polja Polje je niz varijabli istog tipa indeksiranih cjelobrojnim indeksom koji se kre´e u rasponu od 0 do n-1. dio programa u kojem joj se moˇe pristupiti putem z njenog imena. Za indeksiranje polja koristimo uglate zagrade i poˇetna vrijednost indeksa je uvijek nula.b. vektor[9]. Deklaracija varijable je tada ujedno i njena definicija. main ili neke druge) onda se na mjestu deklaracije rezervira memorijski prostor za varijablu. 3. vektor[1]. Varijable definirane unutar neke funkcije i izvan svih funkcija razlikuju se po svom dosegu. c Deklaracija polja ima oblik: . Doseg varijable definirane u nekoj funkciji je tijelo te funkcije. char d.1. gdje je n broj elemenata polja. Kod vanjskih varijabli pojam deklaracije i definicije varijable mogu se razlikovati kao ˇto ´emo vidjeti s c u sekciji 9. varijaz bli deklariranoj izvan svih funkcija (vanjskoj varijabli) moˇe se pristupiti iz z svake funkcije (vidi sekciju 9. unsigned c. ili svaku zasebno short a. Na primjer. prije prve izvrˇne naredbe.

. c Deklaracije varijabli nekog tipa i pokazivaˇa na isti tip mogu se pisati u c jednom retku kao u primjeru .5. c pv. Na primjer. int *pv. .. a * u deklaraciji oznaˇava da identifikator ime c c nije varijabla tipa tip p ve´ pokazivaˇ na varijablu tipa tip p. u pokazivaˇ pv spremljena je adresa varijable v.y. Deklaracija polja moˇe biti dana zajedno s deklaracijama drugih varijabli kao z u primjeru float x. ime pokazivaˇa. Na primjer. ime ime polja.2 Pokazivaˇi c Svakoj varijabli u programu pridruˇena je odredena memorijska lokacija z koju moˇemo dohvatiti pomo´u adresnog operatora &. polje vektor od 10 realnih brojeva jednostruke preciznosti deklarira se naredbom float vektor[10]. Ukoliko ˇelimo memorirati adresu npr. 57 gdje je tip tip podatka. onda je &v njena adresa u memoriji. Ako je v neka variz c jabla.vektor[10]. pv=&v. 3. VARIJABLE I DEKLARACIJE tip ime[dimenzija]. U liniji kˆda o pv=&v. deklarirana je cjelobrojna varijabla v i pokazivaˇ na cjelobrojnu varijablu.3. gdje je ime.. a vektor polje od 10 elemenata tipa float. c c u kˆdu o int v. moramo z deklarirati pokazivaˇ na cjelobrojnu varijablu.5. Adrese se pohranjuju i s njima se manipulira pomo´u posebnih varijabli koje se nazivaju pokazivaˇi (poinc c teri). a dimenzija broj elemenata polja.. u kome su x i y varijable tipa float. cjelobrojne varijable. Sintaksa deklaracije je c tip_p *ime.

Napomena. s . printf("Na adresi %p smjesten je znak %c\n".. sugerira da je pv nakon dereferenciranja (*pv) tipa char. Tu se krije mogu´nost (namjerne ili nenamjerne) korupcije memorije. Tako moˇemo imati z char *pv. Sintaksa deklaracije pokazivaˇa je u skladu s primjenom c c * kao operatora dereferenciranja tako ˇto deklaracija tipa s char *pv. printf("Na adresi %p smjesten je znak %c\n". .*pu. char v. od prekida programa do c pogreˇnog funkcioniranja. c Sadrˇaj memorijske lokacije moˇe se dohvatiti pomo´u pokazivaˇa.&v. Prethodni primjer ve´ ukazuje na opasnost koja se javc lja upotrebom pokazivaˇa. onda je *pv c znak spremljen na tu lokaciju.. KONSTANTE I VARIJABLE gdje je deklarirana varijabla u tipa float i pokazivaˇ pu na float. Pomo´u pokazivaˇa mogu´e je unutar programa c c c c pristupiti svakoj memorijskoj lokaciji koju je operacijski sustav dodijelio programu. u svakom sluˇaju.. recimo char. *pv=’b’.. . c U gornjem primjeru nije pokazana linija kˆda u kojoj je pokazivaˇ inicijaliziran. Radi se.. no u sluˇaju da pv sadrˇi c c z neku sluˇajnu vrijednost naˇ ´e program pokuˇati pisati na sluˇajno odabranu c sc s c adresu. Ako z z c c je pv varijabla tipa pokazivaˇ na neki tip podatka.v). Posljedice toga mogu biti najrazliˇitije. Na koju memorijsku lokaciju ´e biti upisan znak ’b’? Precizan odgovor ovisi c o memorijskoj klasi pokazivaˇa (vidi sekciju 9... a u izvrˇnim naredbama ona predstavlja c s operator dereferenciranja koji primijenjen na pokazivaˇ daje vrijednost na koju c pokazivaˇ pokazuje. POGLAVLJE 3.. v=’a’. Vidimo da * ima dvije razliˇite uloge: u deklaraciji varijable ona oznaˇava c c da je varijabla pokazivaˇkog tipa.58 float u. o c Pretpostavimo stoga da smo ga zaboravili inicijalizirati prije dereferenciranja *pv=’b’.2).pv. o tipu greˇke koju je s c s teˇko otkriti.*pv).

c Konstanta tipa long formira se tako da se na kraj cjelobrojne konstante doda slovo L (veliko ili malo). Ako konstanta sadrˇi viˇe od jedne znamenke prva ne smije biti z s jednaka nuli.6 Konstante C poznaje cjelobrojne. Prva znamenka mora uvijek biti 0 ˇto signalizira da se radi o s broju zapisanom u oktalnom sustavu. Primjeri su 0 01 -0651 077777 Decimalna toˇka nije dozvoljena.6.3. Primjeri su 0 1 234 -456 99999 Decimalna toˇka nije dozvoljena. oktalnom (baza 8) i heksadecimalnom (baza 16). Cjelobrojne konstante moraju stoga biti unutar granica odredenih tipom int. Cjelobrojne konstante mogu biti zapisane u tri brojevna sustava: decimalnom (baza 10). Primjeri su = = = = = = 10 11 12 13 14 15 . konstanta tipa unsigned formira se dodavanjem slova U (veliko ili malo). Konstante tipa unsigned. long i unsigned long mogu poprimiti vrijednosti ve´e od obiˇnih cjelobrojnih konstanti i c c stoga moraju biti posebno oznaˇene. c Oktalne cjelobrojne konstante dobivaju se kombinacijom decimalnih znamenki 0 do 7. c Heksadecimalne cjelobrojne konstante zapoˇinju s 0x ili 0X i sastoji se od c kombinacije znamenki 0 do 9 i slova a do f (mala ili velika). c Sve numeriˇke konstante moraju biti unutar granica odredenih tipom c podatka koji predstavljaju.0 nije cjelobrojna konstanta. Interpretacija slova je sljede´a: c a b c d e f Primjeri su 0x0 0x1 -0x7FFF 0xabcd Decimalna toˇka nije dozvoljena. realne i znakovne konstante te konstantne znakovne nizove. KONSTANTE 59 3. Decimalne cjelobrojne konstante sastavljene su od decimalnih znamenki 0 do 9. tako da 10. Konstanta tipa unsigned long formira se dodavanjem slova UL (veliko ili malo).

’A’ ’x’ ’5’ ’?’ ’ ’ (uoˇimo da je i bjelina znak). b=2000L. z c Jedan broj posebnih znakova moˇe se reprezentirati u C-u pomo´u dva z c znaka: prvi je obrnuta kosa crta \(eng. Znakovi konverzije za printf funkciju vide se u sljede´em primjeru: c unsigned long unsigned long long long unsigned long long a=20U. %lld\n".e). e=2000000000ULL.60 Konstanta 500000U 123456789L 123456789ul 0123456l 0X50000U -5000ULL 50000ULL POGLAVLJE 3. %lo\n". backslash). c=200000LU. ’3’ postiˇe se neovisnost o naˇinu kodiranja znakova. Koriˇtenjem konsc s tanti poput ’a’.c). Znakovne konstante imaju svoje cjelobrojne c vrijednosti koje su odredene naˇinom kodiranja znakova. printf("unsigned printf("long printf("unsigned long printf("unsigned long printf("long long printf("unsigned long long Znakovne konstante. Cesto koriˇteni posebni znakovi su s . = = = = = = %u\n". Znakovna konstanta je jedan znak u jednostrukim navodnicima.a). d=20000000LL.c). %Ld\n".d). %lu\n".b). a drugi je neko od slova ˇ ili znakova. %llu\n". Npr. KONSTANTE I VARIJABLE Tip unsigned (decimalna) long (decimalna) unsigned long (decimalna) long (oktalna) unsigned (heksadecimalna) long long (decimalno) unsigned long long (decimalno) U i L mogu se kombinirati u bilo kojem poretku.

2e3l 5000.00fe-3f 1. Na primjer ’\170’ je znak koji ima kod 170 oktalno. KONSTANTE Kod \b \f \n \r \t \v \0 \? \” \’ \\ Na primjer.345E+8F -0. Realna konstanta je broj zapisan u dekadskom sustavu s decimalnom toˇkom. -0. gdje je ooo troznamenkasti oktalni broj. kodovi mogu biti zadani i heksadecimalno u obliku \xoo.0 e+5 c nije ispravno napisana konstanta).0e+5 . gdje oo c dvoznamenkasti heksadecimalni broj. Tip svih realnih konstanti je double. Eksponent mora biti cijeli broj kome prethodi slove e (malo ili veliko). realna konstanta koja nakraju ima l ili c L je tipa long double: 0. odnosno 120 decimalno (x u ASCII znakovima). Npr. ’\n’ ’\b’ ’\\’ Znaˇenje c povratak za jedno mjesto unazad (backspace) nova stranica (form feed) novi red (new line) povratak na poˇetak linije (carriage return) c horizontalni tabulator vertikalni tabulator nul znak (null character) upitnik navodnik jednostruki navodnik obrnuta kosa crta (backslash) 61 Znakovne konstante mogu biti zadane svojim kodom koji se piˇe u obliku s \ooo.6. c 0.0L Kontrolni znakovi u printf funkciji mogu se vidjeti u sljede´em primjeru: c . Ukoliko se ˇeli konstanta tipa float z na kraj joj se dodaje f ili F. 1. 3.2 5000. Realne se konstante mogu pisati i s eksponentom i tada decimalna toˇka nije c potrebna. 3e5 3E+5 3.3.3e6 30E4 Uoˇimo da ne smije biti razmaka izmedu mantise i eksponenta (npr. Sliˇno. Na primjer. Sliˇno. broj 3 × 105 moˇe biti napisan na sljede´e naˇine: z c c 300000.

0F.62 float double long double long double POGLAVLJE 3. definirane u <complex. Prazan niz znakova ”” sadrˇi z z samo nul-znak. Obiˇno c se na taj naˇin definiraju konstante kao npr. "Zagreb" "01/07/2001" "Linija 1\nLinija 2\nLinija3" Specijalni znakovi kao \n i \t mogu biti ukljuˇeni u konstantne znakovne c nizove. w=-0. printf("float printf("double printf("long double printf("long double Kompleksne konstante se formiraju kao kombinacija realnih konstanti i imaginarne jedinice Complex I (ili I). Na primjer. Prevodilac ´e tada ignorirati kombinaciju znaka \ i znaka za prijelaz u novi red. Definiraju se na poˇetku programa a sintaksa im c je #define ime tekst gdje je ime ime simboliˇke konstante. a tekst niz znakova koji ´e biti subsc c tituiran na svakom mjestu u programu na kojem se pojavljuje ime. dakle niz od dva znaka: a i \0. c . y=2. onda ´e ih prevodilac automatski nadovezati. onda imamo s c dvije mogu´nosti. Konstantni znakovni nizovi (konstantni stringovi) su nizovi znakova navedeni unutar (dvostrukih) navodnika.y). Svakom znakovnom nizu automatski se na kraj dodaje nul-znak (\0) kao oznaka kraja znakovnog niza.2e-8L. Moˇemo ga produˇiti u sljede´u liniju ako ispred prijelaza u novi red c z z c c stavimo znak \. char s2[]="Vrlo dugacak " "niz znakova".0.h>. %Le\n". %Lf\n". %f\n". = = = = %f\n". Simboliˇke konstante su imena koja preprocesor (vidi Poglavlje 8) subc stituira nizom znakova. U sljede´em primjeru oba stringa su jednako inicijalizirana: c char s1[]="Vrlo dugacak \ niz znakova". Druga mogu´nost je iskoristiti svojstvi automatskog nadovezivanja konstantnih c c znakovnih nizova: ako se dva takva niza nalaze jedan pored drugog.w).2e8l. KONSTANTE I VARIJABLE x=-1. a drugo je znakovni niz koji z sadrˇi slovo a. To ima za posljedicu da su ’a’ i "a" dva razliˇita c c objekta. Prvo je jedan znak koji sadrˇi slovo a.z).x). Ako je konstantan znakovni niz suviˇe dugaˇak da bi stao u jednu liniju. Na taj naˇin algoritmi koji rade sa c c znakovnim nizovima ne trebaju poznavati broj znakova ve´ prepoznaju kraj niza pomo´u nul znaka \0. z=-0.

tj.2. 15 za rijeˇ Inicijalizacija c i jedan za nul-znak koji dolazi na kraju niza znakova.3. dok b nije inicijalizirana. char d=’\t’.4. 3. x[2]=-6. Varijable se mogu inicijalizirati u trenutku deklaracije. Prevodilac ne´e dozvoliti izmjenu varijable e u preostalom dijelu programa. c const double e=2.7. int a=7. x[1]=3. u tu svrhu bolje je koristiti const varijable (sekcija 3. definiran niz znakova tekst koji ima 16 znakova.1}. Alternativno smo mogli napisati char tekst[16]="Inicijalizacija".71828182845905.1 .3. Tako je deklaracijom char tekst[]="Inicijalizacija". Sve deklaracije varijabli mogu nositi kvalifikator const koji indicira da varijabla ne´e nikad biti mijenjana. Uoˇimo da nismo moc rali eksplicitno navesti dimenziju polja tekst. c Polja znakova mogu se inicijalizirati konstantnim nizovima znakova.7 Inicijalizacija varijabli tip varijabla = konstantni izraz. polja se mogu inicijalizirati navodenjem vrijednosti elemenata c polja unutar vitiˇastih zagrada: c double x[]={1. gdje su inicijalizirane varijable a. Npr. Sintaksa je Na primjer.2. INICIJALIZACIJA VARIJABLI #define PI 3.141593 63 #define TRUE 1 #define FALSE 0 Ipak.7). c c Nakon ove deklaracije imat ´emo c x[0]=1. unsigned c=2345.b.4. Op´enito.-6. Ona ´e biti izraˇunata iz c c stringa "Inicijalizacija". da se radi o konstanti. c i d. Dimenzija polja bit ´e izraˇunata na osnovu broja konstanti unutar zagrada.

. TRUE}.64 POGLAVLJE 3. KONSTANTE I VARIJABLE 3. Tako smo doobili FALSE=0. . a clan 1...8 Enumeracije Enumeracije predstavljaju alternativu uvodenju simboliˇkih konstanti puc tem preprocesorske direktive #define.y. clan_2. Na primjer. nakon deklaracije c c enum {FALSE... To ekvivalentno preprocesorskim naredbama #define #define FALSE 0 TRUE 1 Princip je da se svakom imenu deklariranom u enumeraciji pridruˇi jedan z cjeli broj: prvom se pridruˇi nula. gdje je ime ime koje dajemo enumeraciji. z Identifikatori moraju biti medusobno razliˇiti i razliˇiti od drugih identic c fikatora u dosegu enumeracije.clan n predstavljaju identifikatore koji se mogu pridruˇiti varijabli tipa enum ime..... Pomo´u enumeracije deklariramo c simboliˇka imena koja primaju cjelobrojne vrijednosti i ˇija je osnovna svrha c c pove´anje ˇitljivosti programa.clan_n}. Prethodnu enumeraciju smo mogli deklarirati kao enum boolean {FALSE. clan 2. Koristimo ih za one varijable koje primaju konaˇno mnogo vrijednosti i za koje je poˇeljno c z uvesti simboliˇka imena. Varijable x i y tipa boolean definiraju se naredbom enum boolean x.. Varijable tipa enum pridonose razumljivosti programa.... Enumeraciji moˇemo dati ime i tada moˇemo deklarirati varijable tipa z z enumeracije. itd. To su varijable koje primaju samo konaˇno mnogo vrijednosti c navedenih u enumeraciji.. imena FALSE i TRUE moˇemo koristiti u programu kao cjelobrojne konstante. Identifikatorima se automatski pridruˇuju z cjelobrojne vrijednosti . Moˇemo ih koristiti na sljede´i naˇin: z c c x=FALSE... z TRUE=1. c Op´enito se enumeracija deklarira naredbom c enum ime {clan_1. if(x==TRUE) y=FALSE. TRUE}.. a TRUE 1. drugom 1 itd. z FALSE ima vrijednost 0.

ljubicasto... clan_2. ENUMERACIJE clan_1=0 clan_2=1 clan_3=2 .... Time dobivamo plavo=-1 zuto=0 crveno=1 zeleno=0 ljubicasto=1 bijelo=2 Vidimo da ´e se u ovom sluˇaju neke vrijednosti ponoviti.. c c ...var_m.bijelo}.8...zeleno=0. var_2..var_m...zuto. 65 Vrijednosti koje se dodijeljuju identifikatorima mogu se modificirati kao u sljede´em primjeru c enum ESC {novi_red=’\n’. tabulator=’\t’}.. Deklaracije enumeracije i varijabli tog tipa mogu se spojiti: enum ime {clan_1. ili enum boje {plavo=-1. clan_n=n-1 Varijabla tipa enumeracije deklarira se naredbom enum ime var_1......clan_n} var_1.crveno... var_2.3.

Paˇnju ´emo posvetiti naˇinu izraˇunavanja izraza. Najˇeˇ´e je to odsjecanje. onda je rezultat dijeljenja realan broj. cjelobrojc c nim. Ukoliko je rezultat dijeljenja negativan. zaokruˇivanje prema nuli. realnim i znakovnim operandima (znakovna konstanta ima cjelobrojni tip). zaokruˇivanje c sc z prema nuli.Poglavlje 4 Operatori i izrazi U ovom se poglavlju bavimo operatorima i izrazima koji se formiraju pomo´u c operatora i operanada. pokazivaˇe (Poglavlje 11) te operatore koji djeluju na bitovima c (Poglavlje 14). Rezultatu dijeljenja odsjecaju se decimalne znamenke kako bi se dobio cjelobrojni rezultat. • Operacije dijeljenja u sluˇaju cjelobrojnih operanada ima znaˇenje cjec c lobrojnog dijeljenje. z c c c 4. Izostavit ´emo operatore vezane uz strukture (Poc glavlje 12). Ako bar jedan s z od operanada nije cjelobrojan. c 66 .1 Aritmetiˇki operatori c Operator + * / % Znaˇenje c zbrajanje oduzimanje mnoˇenje z dijeljenje modulo Programski jezik C poznaje pet aritmetiˇkih operatora: c • Aritmetiˇki operatori djeluju na numeriˇkim operandima tj. Nazivnik pri dijeljenju mora biti razliˇit od nule. tj. Standard C99 je propisao da se i u sluˇaju negativnih c brojeva vrˇi odsjecanje. onda prema standardu C90 naˇin zaokruˇivanja rezulc z tata ovisi o implementaciji. tj.

Na primjer. Uˇi tipovi s z se ne pojavljuju jer se oni automatski konvertiraju u int ili unsigned int prema prvom pravili. ako je x = 10 i y = 3. c c s onda ´e unsigned short biti ˇiri od int. pa ´e operandi biti konvertic s c rani u unsigned int. ako je x varijabla tipa double. float. tako da ponaˇanje operatora % na negativnim operandima ovisi o ponaˇanju s s operatora /. c Primjer. double. long. U sluˇaju da je short isto ˇto i int. Umjesto njega koristi se funkcija pow iz matematiˇke biblioteke. • U svakoj operaciji koja ukljuˇuje operanda razliˇitih tipova prije izvrc c ˇenja operacije vrˇi se konverzija operanada u najˇiri tip. unsigned long long. Op´enito operandi ´e biti konvertirani u najprecizniji tip prisutan medu operandima i rezultat aritmetiˇkog izraza bit ´e tog tipa. Tada je prema standardu C99 x/y = −4 (odsjecanje) te x%y = 1 (predznak prvog operanda). Uzmimo da je na primjer x = 13 i y = −3. • Operator potenciranja u C-u ne postoji. Jedina iznimka je kad su long i int isti. Stoga je (x/y) ∗ y + (x%y) = 13 = x. long z long.1. onda je x/y = 3 (cjelobrojno dijeljenje) te x%y = 1. Tada je unsigned int ˇiri od long. Uvijek mora vrijediti da je (x/y) ∗ y + (x%y) jednako x.ˇ 4. ARITMETICKI OPERATORI 67 • Operacija modulo (%) djeluje na cjelobrojnim operandima i kao rezultat daje ostatak pri cjelobrojnom dijeljenju operanada. a n varijabla tipa int u izrazu x+n. 4. c c Na primjer. . s s s • Tipovi su prema ˇirini poredani na sljede´i naˇin (od najˇireg prema s c c s najuˇem): long double.1 Konverzije Kada u aritmetiˇkom izrazu sudjeluju operandi razliˇitih tipova onda c c prije izraˇunavanja izraza dolazi konverzije operanada u zajedniˇki tip prema c c c c odredenim pravilima.1. prije zbrajanja do´i ´e do konverzije varijable n u tip double i vrijednost c c izraza bit ´e tipa double. unsigned int i int. Prilikom ovih konverzija ne dolazi do gubitka informacije osim eventualno c u sluˇaju kada se cjelobrojni tip konvertira u realni. unsigned long. c Vrijede sljede´a pravila: c • short i char (s predznakom ili bez) automatski se konvertiraju u int prije svake aritmetiˇke operacije.

68

POGLAVLJE 4. OPERATORI I IZRAZI

Konverzija se deˇava i kod operacije pridruˇivanja (vidi sekciju 4.4) ako s z su operandi s lijeve i desne strane razliˇitog tipa. Operand na desnoj strani c konvertira se u tip operanda na lijevoj strani. Pri tome moˇe do´i do gubitka z c informacije ako se konvertira ˇiri tip u uˇi. Na primjer, ako je x varijabla tipa s z float i n varijabla tipa int prilikom pridruˇivanja i=x do´i ´e do odsjecanja z c c decimala u broju x. Konverzija se deˇava prilikom prenoˇenja argumenata funkciji (vidi seks s ciju 7.3). Naime, argumenti su izrazi i prije njihovog izraˇunavanja dolazi do c konverzije ukoliko je ona potrebna. Pri tome vrijede razliˇita pravila ovisno c o tome da li funkcija ima prototip ili ne. • Ako funkcija nema prototipa, onda se svaki argument tipa char i short transformira u int, a float u double. • Ukoliko funkcija ima prototip, onda se svi argumenti pri pozivu konvertiraju (ako je to potrebno) u tipove deklarirane u prototipu (vidi sekciju 7.2). Vrijednost nekog izraza moˇe se eksplicitno konvertirati u zadani tip z pomo´u operatora eksplicitne konverzije (tip) (eng. cast operator). Sinc taksa konverzije je (tip) izraz; izraz ´e biti izraˇunat i njegova vrijednost konvertirana u tip unutar zac c grada. U primjeru double x; float y; ........ x= (double) y; y je eksplicitno konvertiran u tip double prije pridruˇivanja. Eksplicitne z konverzije mogu biti nuˇne u sluˇajevima kao ˇto je sljede´i: z c s c int i; double x; ((int) (i+x))%2;

4.1.2

Prioriteti i asocijativnost

Svi su operatori hijerhijski grupirani prema svom prioritetu. Operacija iz grupe s viˇim prioritetom izvrˇit ´e se prije operacija s niˇim prioritetom. s s c z Upotrebom zagrada mogu´e je promijeniti redoslijed vrˇenja operacija. c s

4.2. UNARNI OPERATORI

69

Operacije *, / i % spadaju u jednu prioritetnu grupu, dok operacije + i spadaju u niˇu prioritetnu grupu (vidi sekciju 4.6). z Tako na primjer u izraz 2+4/2 dijeljenje ´e biti izvrˇeno prije zbrajanja, pa je vrijednost izraza 4, a ne 3 c s ˇto bi bilo da se prvo izvrˇi zbrajanje. Ukoliko ˇelimo zbrajane izvrˇiti prije s s z s dijeljenja treba pisati (2+4)/2 ˇto daje 3. s Zagrade koristimo kako bismo grupirali operande oko operatora. Ukoliko ih nema, onda se grupiranje vrˇi oko operatora najviˇeg prioriteta. Kada viˇe s s s operatora ima isti, najviˇi, prioritet, onda naˇin izraˇunavanja izraza ovisi o s c c asocijativnosti. Asocijativnost moˇe biti slijeva na desno ili zdesna na lijevo. z Ako imamo, na primjer, dva operatora istog prioriteta, ˇija je asocijativnost c slijeva na desno, onda ´e se operandi prvo grupirati oko lijevog operanda. Na c primjer, a - b + c je ekvivalntno s (a - b) + c

4.2

Unarni operatori

C ima jedan broj operatora koji djeluju na jednom operandu koje stoga nazivamo unarni operatori. Konstante i varijable mogu imati ispred sebe minus kao u primjerima -345, -(x+y), gdje minus daje negativan predznak konstanti i mijenja predznak varijable. Takav minus nazivamo unarni minus za razlike od binarnog minusa koji predstavlja operaciju oduzimanja. Operator konverzije tipa (tip) izraz

kojeg smo sreli u sekciji 4.1 takoder je unarni operator. Svi unarni operatori spadaju u istu prioritetnu grupu i imaju viˇi prioritet s od aritmetiˇkih operatora. U izrazu c -x+y ´e se stoga prvo izvrˇiti unarni minus, a zatim zbrajanje. c s Asocijativnost unarnih operatora je zdesna na lijevo ˇto ´e biti vaˇno kad s c z uvedemo preostale unarne oparatore !,˜ * i &. ,

70

POGLAVLJE 4. OPERATORI I IZRAZI

4.2.1

Inkrementiranje, dekrementiranje

Operator inkrementiranja ++ pove´ava varijablu za jedan. Tako je izraz c x++; ekvivalentan izrazu x=x+1; Operator dekrementiranja -- smanjuje vrijednost varijable za 1, odnosno x--; je ekvivalentno izrazu x=x-1; Izraz x++ i x-- mogu se pojaviti kao elementi sloˇenijih izraza i zbog toga z imaju dvije forme. Moˇemo pisati x++ ili ++x i isto tako x-- ili --x. U oba z sluˇaja varijabla x se pove´ava (umanjuje) za 1. Razlika izmedu prefiks i c c postfiks notacije pojavljuje se u sloˇenim izrazima. z • U prefiks notaciji (++x, --x) varijabla ´e biti promijenjena prije no ˇto c s ´e njena vrijednost biti iskoriˇten u sloˇenom izrazu; c s z • U postfiks notaciji (x++, x--) varijabla ´e biti promijenjena nakon ˇto c s ´e njena vrijednost biti iskoriˇten u sloˇenom izrazu. c s z Na primjer, ako je x=3, onda izraz y=++x; daje y=4, x=4, a izraz y=x++; daje y=3, x=4. Argumenti funkcije su izrazi koji se izraˇunavaju prije no ˇto se njihova c s vrijednost prenese u funkciju. Prefiks i postfiks notacija operatora inkrementiranja/dekrementiranja stoga i ovdje ima isto znaˇenje. U primjeru c i=7; printf("i= %d\n", --i); printf("i= %d\n", i--); printf("i= %d\n", i); prva printf naredba ispisat ´e 6, druga ´e takoder ispisati 6, a tre´a ´e c c c c ispisati 5. Razlog je to ˇto se u drugoj printf naredbi vrijednost varijable i s prvo prenese funkciji, a zatim smanji za 1.

4.2. UNARNI OPERATORI

71

4.2.2

sizeof operator

sizeof operator vra´a veliˇinu svog operanda u bajtovima. U C-u se c c jedan bajt definira kao broj bitova potreban za pam´enje podatka tipa char. c To je na veˇini raˇunala jedan oktet (osam bitova). c c Operand moˇe biti izraz ili tip podatka. Na primjer, kˆd z o int i; float x; printf("Velicina tipa int = %d\n", sizeof(i)); printf("Velicina tipa float = %d\n", sizeof(x)); bi na nekim sustavima ispisao Velicina tipa int = 4 Velicina tipa float = 4 Isti efekt postiˇemo ako sizeof primijenimo na tip podatka: z printf("Velicina tipa int = %d\n", sizeof(int)); printf("Velicina tipa float = %d\n", sizeof(float)); Kod sloˇenijih podataka dobivamo ukupan broj okteta koji podatak zaz uzima. Ako imamo char tekst[]="Dalmacija"; naredba printf("Broj znakova u varijabli tekst =%d\n", sizeof(tekst)); daje Broj znakova u varijabli tekst =10 Operator sizeof vra´a cjelobrojnu vrijednost bez predznaka koja ovisi o c implementaciji. Taj je tip definiran u datoteci zaglavlja <stddef.h> i zove se size t.

72

POGLAVLJE 4. OPERATORI I IZRAZI

4.3

Relacijski i logiˇki operatori c
Operator < <= > >= Znaˇenje c strogo manje manje ili jednako strogo ve´e c ve´e ili jednako c

ˇ Cetiri relacijska operatora su

Oni imaju isti prioritet, niˇi od prioriteta aritmetiˇkih i unarnih operatora, z c a asocijativnost im je slijeva na desno. Operatori jednakosti su c Operator Znaˇenje == jednako != razliˇito c Oni spadaju u zasebnu prioritetnu grupu s manjim prioritetom od relacijskih operatora i asocijativnost im je slijeva na desno. Pomo´u ovih ˇest operatora formiraju se logiˇki izrazi. Njihova vrijednost c s c je istina ili laˇ. Kako u ANSI C-u (C90) ne postoji poseban logiˇki tip poz c datka, logiˇki izrazi su tipa int. (Standard C99 je uveo tip Bool). Logiˇki c c izraz koji je istinit dobiva vrijednost 1, a logiˇki izraz koji je laˇan prima c z vrijednost 0. Na primjer: Izraz i<j (i+j)>=k i==2 k!=i Istinitost istinito neistinito neistinito istinito Vrijednost 1 0 0 1

Budu´i da je prioritet logiˇkih operatora niˇi od prioriteta aritmetiˇkih opec c z c ratora izraz poput i >= ’A’-’a’+1 ekvivalentan je s i >= (’A’-’a’+1) Sloˇeniji logiˇki izrazi formiraju se pomo´u logiˇkih operatora: z c c c Operator && || ! Znaˇenje c logiˇko I c logiˇko ILI c logiˇka negacija (unarno) c

ˇ 4.3. RELACIJSKI I LOGICKI OPERATORI

73

Operandi logiˇkih operatora su logiˇke vrijednosti (najˇeˇ´e logiˇki izrazi) s c c c sc c tim da se svaka cjelobrojna vrijednost razliˇita od nule interpretira kao istina, a c nula se interpretira kao laˇ. Vrijednost sloˇenog logiˇkog izraza bit ´e 0 (laˇ) z z c c z ili 1 (istina). Svaki od ova dva operatora ima svoju prioritetnu grupu i prioritet im je niˇi od prioriteta relacijskih operatora i operatora jednakosti. Asocijativnost z im je slijeva na desno. Primjer. Ako je izraz i>1 istinit, izraz c==’t’ istinit, a izraz j<6 laˇan, z onda imamo: Izraz i>1 || j<6 i>1 && j<6 !(i>1) i>1 || (j<6 && c!=’t’) Istinitost istinito neistinito neistinito istinito Vrijednost 1 0 0 1

Logiˇki izrazi koji se sastoje od individualnih logiˇkih izraza povezanih c c logiˇkim operatorima && i || izraˇunavaju se slijeva na desno. Izraz se presc c taje izraˇunavati kad je njegova vrijednost poznata. To je vaˇnu u sljede´im c z c situacijama: pretpostavimo da je x polje tipa char dimenzije n. Tada u izrazu for(i=0; i<n && x[i] !=’a’; ++i) { .... } ne´e do´i do ispitivanja x[i] !=’a’ ako je i=n (granica polja x ne´e biti c c c prijedena). S druge strane, kˆd o for(i=0; x[i] !=’a’ && i<n; ++i) { .... } bi ispitivao da li je x[n] razliˇito od ’a’. To moˇe dovesti do nepredvidivog c z ponaˇanja programa. s Logiˇka negacija koristi se ˇesto u if naredbama. Konstrukcija c c if(!istina) ekvivalentan je s if(istina==0) i budu´i da je kra´a (za dva znaka) ˇesto se koristi. c c c

74

POGLAVLJE 4. OPERATORI I IZRAZI

4.4

Operatori pridruˇivanja z

Osnovni operator pridruˇivanja je =. Naredba pridruˇivanja je oblika z z varijabla = izraz; Izraz na desnoj strani ´e biti izraˇunat i njegova vrijednost se zatim pric c druˇuje varijabli na lijevoj strani. Na primjer, z i=2; a=3.17; c=’m’; Pridruˇivanje varijabla = izraz je ustvari izraz i stoga moˇe biti dio komz z pleksnijeg izraza. Preciznije, izraz varijabla = izraz ima vrijednost varijable na lijevoj strani nakon ˇto se izvrˇi pridruˇivanje. Stoga je mogu´e s s z c koristiti pridruˇivanje unutar drugih naredbi kao u primjeru z while((a=x[i])!=0){ .... ++i; } gdje se u testu while narebe prvo x[i] pridruˇi varijabli a, a zatim se testira z da li je dobiveni izraz, a to je vrijednost varijable a, razliˇit od nule. c Ovakva mogu´nost moˇe voditi na teˇko uoˇljive greˇke kao u sluˇaju ako c z s c s c napiˇemo s if(varijabla = izraz) .....; umjesto if(varijabla == izraz) .....; U prvoj if naredbi prvo ´e se vrijednost izraza pridruˇiti varijabli, a zatim ´e c z c se tijelo if naredbe izvrˇiti ako je varijabla razliˇita od nule. U drugoj if nas c redbi vrijednost varijable se ne mijenja ve´ se samo usporeduje s vrijednoˇ´u c sc koju daje izraz. Tijelo if naredbe se izvrˇava ako su te dvije vrijednosti s jednake. Operatore pridruˇivanja mogu´e je ulanˇati i pisati z c c varijabla1 = varijabla2 = varijabla3 = ... = izraz; Niz pridruˇivanja ide zdesna na lijevo. To znaˇi da se izraz jednom izraˇuna z c c i zatim se redom pridruˇuje varijablama .. varijabla3, varijabla2, z varijabla1. Na primjer,

je ekvivalentno s x = (y = cos(3. Prioritet im je manji od prioriteta aritmetiˇkih i logiˇkih c c operatora.22)). x=y.22). Na primjer.22). c . *. Primjeri: Izraz i +=5 i -=j i *=j+1 i /=4 i %=2 Ekvivalentan izraz i=i+5 i=i-j i=i*(j+1) i=i/4 i=i%2 4. Ovi operatori spadaju u istu prioritetnu grupu s operatorom = i izraˇunavaju c se zdesna na lijevo.5 Uvjetni operator : ? Uvjetni izraz je izraz oblika izraz1 ? izraz2 : izraz3. ekvivalentan je s izraz1 = izraz1 op izraz2. /. gdje je op jedna od operacija +. UVJETNI OPERATOR : ? x = y = cos(3. Ako je on istinit (razliˇit od nule) onda c c se izraˇunava izraz2 i on postaje vrijednost ˇitavog uvjetnog izraza. odnosno y = cos(3.4. %. U njemu se prvo izraˇunava izraz1. -. Sloˇeni operatori pridruˇivanja su z z += -= *= /= %= 75 Op´enito izraz oblika c izraz1 op= izraz2.5. ako je c c izraz1 laˇan (jednak nuli) onda se izraˇunava izraz3 i on postaje vrijednost z c ˇitavog uvjetnog izraza.

ILI bit-po-bit ILI logiˇko I c logiˇko ILI c uvjetni ? : op... ? : (i operatora c ”. ||. c Neke od tih operatora joˇ nismo uveli. OPERATORI I IZRAZI je izraz koji daje manju vrijednost od brojeva a i b... z z double a.b.b.6 Prioriteti i redoslijed izraˇunavanja c U sljede´oj tabeli sumirani su prioriteti i asocijativnost svih operatora. s Kategorija unarni op.. Stoga u izrazu s . Uvjetni operator ima nizak prioritet tako da zagrade oko prvog. POGLAVLJE 4. s s z Primijetimo joˇ da C ne propisuje redoslijed kojim se izraˇunavaju opes c randi nekog operatora osim u sluˇaju operatora &&. drugog i tre´eg izraza najˇeˇ´e nisu potrebne.... jednakosti bit-po-bit I bit-po-bit ex. op. aritmetiˇki op. Operatori () [] -> . . c aritmetiˇki op.” kojeg joˇ nismo uveli)... pridruˇivanja z zarez op.. npr. min=(a<b) ? a : b. Operatori u viˇoj liniji imaju viˇi prioritet od operatora u niˇim linijama. c c sc 4. op. c op. pomaka relacijski op..76 double a.* & (type) sizeof * / % + << >> < <= > >= == != & ^ | && || ? : = += -= *= /= %= . ... ! ∼ ++ -. asocijativnost L→D D→L L→D L→D L→D L→D L→D L→D L→D L→D L→D L→D D→L D→L L→D Operatori istog prioriteta i asocijativnosti dani su u istoj liniji. (a<b) ? a : b.min.. Vrijednost uvjetnog izraza moˇe se pridruˇiti nekoj varijabli.

/* greska */ mogu dati razliˇite rezultate ovisno o stroju na kojem se izvrˇavaju. n*n). PRIORITETI I REDOSLIJED IZRACUNAVANJA x=f()+g(). c s 77 . Naredbe poput c s printf("%d %d\n".ˇ 4. C jezikom nije definirano koja ´e se funkcija prva izvrˇiti. ++n.6.

pri ˇemu se vra´ena vrijednost ignorira. Stoga funkcija getchar ne vra´a vrijednost tipa char ve´ c c c vrijednost tipa int ˇto daje dovoljno prostora za kodiranje konstante EOF. c 5. s 78 . int putchar(int c). Ona uzima jedan argument (znak koji treba ispisati) i vra´a cjelobrojnu c vrijednost. gets.h>. End of File).h> koja signalizira kraj datoteke i kraj ulaznih podataka (ulaz je tretiran kao datoteka). s c Funkcija putchar ˇalje jedan znak na standardni izlaz (tipiˇno ekran). Funkcija getchar ˇita jedan znak sa standardnog ulaza (tipiˇno tipc c kovnice). Najˇeˇ´e poziv funkcije ima oblik c sc putchar(c_var). Funkcija nema argumenata pa je sintaksa poziva: c_var=getchar(). putchar.Poglavlje 5 Ulaz i izlaz podataka U ovom poglavlju uvodimo ˇest funkcija iz standardne ulazno-izlazne bis blioteke: getchar. Program koji koristi neku od tih funkcija mora ukljuˇiti datoteku zaglavlja <stdio. puts scanf i printf. c c Kada funkcija getchar naide na kraj ulaznih podataka vra´a vrijednost c EOF (skra´eno od eng. EOF je simboliˇka konstanta definirana c c u <stdio.1 Funkcije getchar i putchar int getchar(void). Konstanta EOF mora se razlikovati od znakova iz sustava znakova koje raˇunalo koristi.

c Isti je program mogao biti napisan jednostavnije ubacivanjem poziva funkciji getchar u test while petlje: #include <stdio. FUNKCIJE GETCHAR I PUTCHAR 79 Isto tako putchar uzima vrijednost tipa int i vra´a vrijednost tipa int.h> /* kopiranje ulaza na izlaz */ int main(void) { int c.h> /* kopiranje ulaza na izlaz */ int main(void) { int c. c Kao ilustraciju upotrebe funkcija getchar i putchar napiˇimo program s koji kopira znak po znak ulaz na izlaz i pri tome sva slova pretvara u velika: #include <stdio. Uoˇimo da je za pam´enje znakovne varijable koriˇten tip int umjesto c c s tipa char. while(c!=EOF) { putchar(toupper(c)). Funkciju putchar c smo mogli pozvati i naredbom i=putchar(c) (i je cjelobrojna varijabla) koja bi vra´enu vrijednost smjestila u varijablu i. Ona uzima varijablu tipa char i ako se radi o slovu pretvara malo slovo u veliko. c Vra´ena vrijednost je znak koji je ispisan ili EOF ako ispis znaka nije uspio.1. Sve druge znakove ostavlja na miru. U while petlji znakovi se ˇitaju sve dok se ne dode do kraja c ulaznih podataka (EOF).h> #include <ctype.h>. c=getchar(). while((c=getchar())!=EOF) . c=getchar(). Proˇitani znak se odmah ispisuje putem funkcije c putchar. } return 0. } Funkcija toupper je deklarirana u datoteci zaglavlja <ctype. a vrijednost se koju putchar vra´a ignorira.h> #include <ctype.5.

Citanje znaka odmah nac kon ˇto je otipkan ili onemogu´avanje ispisa utipkanog znaka moˇe se posti´i s c z c specifiˇnim tehnikama programiranja vezanim uz operacijski sustav koji se c koristi (http://www.com/ scs/C-faq/top. c Kada na ulazu nema podataka funkcija getchar ˇeka da se podaci utipkaju. Funkciju getchar ˇesto koristimo za ˇitanje jednog znaka (npr. } Zagrade u testu (c=getchar())!=EOF su nuˇne jer bi c=getchar()!=EOF. Posljedica je toga da prvi poziv funkciji getchar blokira program sve dok se ne ukuca ˇitava ulazna linija i pritisne tipka RETURN (ENTER). while(getchar() != ’\n’) . ch=getchar(). Ponaˇanje funkcije getchar ovisi o operacijskom sustavu na kojem se pros gram izvrˇava. c c ukljuˇivˇi znak za prijelaz u novi red. ULAZ I IZLAZ PODATAKA putchar(toupper(c)).eskimo. Da bismo zaustavili ˇitanje moramo utipkati znak za “kraj datoteke”.h> Datoteka zaglavlja <ctype. } 5. a pod DOSom Ctr-Z. // prazna naredba return ch. c Pod unix-om to je znak Ctr-D. Na primjer.h> sadrˇi deklaracija niza funkcija koje sluˇe z z testiranju znakova.html). bilo interpretirano kao test z c=(getchar()!=EOF). z zbog niskog prioriteta operatora pridruˇivanja. odgovora c c y ili n). ovdje dajemo funkciju koja ˇita samo prvo c slovo s ulaza: int get_first(void) { int ch. Svaka od tih funkcija uzima jedan argument tipa int koji treba biti znak ili EOF i vra´a vrijednost tipa int koja je razliˇita od nule c c (istina) ako je uvjet ispunjen ili nula ako nije. U tim sluˇajevima moramo paziti da proˇitamo sve znakove u liniji.80 POGLAVLJE 5. Tada su svi c ˇ uˇitani znakovi na raspolaganju funkciji getchar. kako ne bi ostali za slijede´i poziv c s c funkcije getchar. Neke od funkcija su sljede´e: c . Varijabla c bi tada dobila vrijednost 0 ili 1. Operacijski sustav prikuplja podatke s tastature i ˇalje ih aplis s kacijskom programu najˇeˇ´e liniju po liniju. Takav naˇin rada omogu´ava c sc c c operacijskom sustavu standardno editiranje ulazne linije (koriˇtenje tipki bacs kspace i delete) koje stoga ne mora biti ugradeno u svaki program.2 Funkcije iz datoteke <ctype.

Funkcija vra´a pokazivaˇ c c na char koji pokazuje na uˇitani znakovni niz ili NULL ako se doˇlo do kraja c s ulaznih podataka ili se javila greˇka. z z c Funkcija puts uzima kao argument znakovni niz koji ´e biti ispisan na c standardnom izlazu. znak carriage return. int tolower(int c) int toupper(int c) Veliko slovo u malo Malo slovo u veliko 5. znak za novi red. int puts(const char *s). tabulator i vertikalni tabulator (’ ’. ’\t’.3). Funkcija gets uzima kao argument znakovni niz u koji ´e biti uˇitan niz c c znakova s ulaza. To je jedina cjelobrojna vrijednost koja se moˇe pridruˇiti pokazivaˇu (vidi sekciju 11. Simboliˇka konstanta NULL definirana s c je <stdio.5. Pri tome se znakovi s ulaza uˇitavaju sve dok se ne naide na c kraj linije (’\n’) koji se zamijenjuje znakom ’\0’.h> i njen iznos je 0. c Ostale znakove ostavljaju na miru. ’\r’.3 Funkcije gets i puts char *gets(char *s). Funkcija gets ˇita znakovni niz sa standardnog ulaza (tastature). ’\n’. Dvije funkcije omogu´avaju konverziju velikih slova u mala i obratno. Prije ispisa puts dodaje znak ’\n’ na kraju znakovnog niza.3. ’\v’). FUNKCIJE GETS I PUTS isalnum() isalpha() isctrln() isdigit() isgraph() islower() isprint() ispunct() isspace() isupper() Alfanumeriˇki znak c Alfabetski znak Kontrolni znak Znamenka Printabilni znak osim razmaka Malo slovo Printabilni znak Printabilni znak osim razmaka. Program koji kopira ulaz na izlaz liniju po liniju mogao bi biti napisan na ovaj naˇin: c . Funkcije gets i puts sluˇe ˇitanju i pisanju znakovnih nizova (strinz c gova). ’\f’. a c funkcija puts ispisuje znakovni niz na standardni izlaz (ekran). slova i brojeva Razmak Veliko slovo 81 Pod razmakom smatramo: bjelinu. Funkcija vra´a broj ispisanih znakova ako je ispis uspio c te EOF ako nije. znak formfeed.

arg_1. Npr. .4 Funkcija scanf int scanf(const char *format. Svaka grupa znakova konverzije zapoˇinje znakom z c postotka (%) kojeg slijedi znak konverzije koji upu´uje na tip podatka koji se c uˇitava. while(gets(red)!=NULL) puts(red).. %c ili %d itd. U tom sluˇaju c s s c izvrˇava se komanda puts(red) koja ispisuje uˇitanu liniju.. . Funkcija scanf sluˇi uˇitavanju podataka sa standardnog ulaza. c Najˇeˇ´e koriˇteni znakovi konverzije navedeni su u sljede´oj tablici: c sc s c . Op´a z c c forma funkcije je scanf(kontrolni_string.. Stoga je op´enito bolje umjesto gets koristiti funkciju fgets (vidi s c sekciju 13. arg_2. ULAZ I IZLAZ PODATAKA /* kopiranje ulaza na izlaz */ int main(void) { char red[128].h> POGLAVLJE 5.arg n. } Unutar testa while petlje gets ´e proˇitati ulaznu liniju i vratiti pokazivaˇ c c c razliˇit od NULL ako ulaz nije prazan i ako nije doˇlo do greˇke.arg_n) gdje je kontrolni string konstantni znakovni niz koji sadrˇi informacije o z vrijednostima koje se uˇitavaju u argumente arg 1. 5. c Kontrolni znakovni niz (string) je konstantan znakovni niz koji se sastoji od individualnih grupa znakova konverzije pri ˇemu je svakom argumentu c pridruˇena jedna grupa..3). a povratna vris c jednost funkcije puts se zanemaruje. Osnovni nedostatak funkcije gets je u tome ˇto nije mogu´e odrediti s c maksimalni broj znakova koji ´e biti uˇitan.82 #include <stdio.. Ukoliko je broj znakova na c c ulazu ve´i od dimenzije polja koje je argument funkcije gets do´i ´e do c c c greˇke. . . .). .

. a ne samu varijablu. Ako se unosi viˇe podataka oni moraju biti separirani bjelinama ˇto u s s sebi ukljuˇuje i prijelaz u novi red (koji se raˇuna kao bjelina).4.4.5. /* pogresno */ s predstavlja greˇku.1 Uˇitavanje cijelih brojeva c Cijeli brojevi mogu biti uneseni kao decimalni (%d) ili kao oktalni i heksadecimalni (%i)..&x)... Na primjer. To znaˇi da pri pozivu funkcije c scanf ispred imena varijable u koju scanf treba uˇitati vrijednost moramo c staviti adresni operator &. uzmimo da kˆd o . Tako ´e naredba c int x. 5. uˇitati cijeli broj s ulaza u varijablu x... Podaci koje scanf ˇita dolaze sa standardnog ulaza ˇto je tipiˇno tastac s c tura. scanf("%d".. onda scanf uzima kao argument c adresu te varijable. Ukoc liko podatak treba uˇitati u neku varijablu.x). heksadecimalni ili oktalni cijeli broj (int) oktalni cijeli broj (int) cijeli broj bez predznaka (unsigned int) heksadecimalni cijeli broj (int) string (char *) pokazivaˇ (void *) c 83 Unutar kontrolnog niza znakova grupe kontrolnih znakova mogu se nastavljati jedna na drugu bez razmaka ili mogu biti odvojene bjelinama Bjeline ´e u c ulaznim podacima biti uˇitane i ignorirane.. dok naredba c int x. Numeriˇki c c c c podaci na ulazu moraju imati isti oblik kao i numeriˇke konstante. c Argumenti funkcije scanf mogu biti samo pokazivaˇi na varijable..%f. FUNKCIJA SCANF znak konverzije %c %d %e... Znak konverzije (%i) interpretira ulazni podatak kao oktalan broj ako mu prethodi nula ili kao heksadecimalan broj ako mu prethodi 0x ili 0X. scanf("%d".%g %h %i %o %u %x %s %p tip podatka koji se uˇitava c jedan znak (char) decimalni cijeli broj (int) broj s pokretnim zarezom (float) kratak cijeli broj (short) decimalni. .

Kˆd c o int x..&z).&x. ispravno ´e proˇitati ulazne podatke c c 13 15 d i svim varijablama pridruˇiti vrijednost 13 (decimalno). a heksadecimalna s 0x ili 0X..&x. Na c c primjer. Standard C99 uvodi prefiks ll za uˇitavanje varijabli tipa long long i unsigned long c long. i..&z).&y. u. kˆd o int x.&x. uˇitava sljede´i ulazni podatak: c c 13 015 0Xd onda ´e scanf u sve tri varijable (x..... short y. o. c c Cijeli brojevi u oktalnom i heksadecimalnom zapisu mogu se upisivati i pomo´u znakova konverzije %o i %x.z. scanf("%d %o %x". scanf("%i %i %i"... x).&z). u. .. .&y.. ULAZ I IZLAZ PODATAKA int x. z Podatak uˇitavamo u varijablu tipa unsigned sa znakom konverzije %u. y i z) uˇitati vrijednost 13 (decimalno). scanf("%d %hd %ld". i.. Ti znakovi konverzije interpretiraju c ulazne podatke kao oktalne odnosno heksadecimalne i stoga ne zahtijevaju da oktalna konstanta zapoˇinje nulom. short i c long.y. long z.z. uˇitava tri decimalna cijela broja i konvertira ih u varijable tipa int. Uoˇite da h i l ne mogu stajati sami i da u kombinaciji uvijek prethode c znaku konverzije (d. x mogu dobiti prefiks h ako je argument pokazivaˇ na short te prefiks l ako je argument pokazivaˇ na long. .. o. .y..84 POGLAVLJE 5. c Znakovi konverzije d..&y.

&x. Njima moraju odgovarati posve isti znakovi na ulazu.&y)..%g %le.4. . Svaka bjelina u kontrolnom znakovnom nizu ima za posljedicu preskakanje svih bjelina na ulazu do poˇetka novog ulaznog polja. Znakovi konverzije mogu biti odijeljeni bjelinama: scanf("%f %d".%f.%lf. Na primjer.%Lg tip podatka koji se uˇitava c broj tipa float broj tipa double broj tipa long double 5.4. Ukoliko z c se uˇitava vrijednost u varijablu tipa double treba koristiti prefiks l (le. Drugi znak konverzije %d preskaˇe sve bjeline koje c c odjeljuju prvo polje znakova od drugog polja znakova i uˇitava (i konvertira) c drugo polje znakova.&i). znak konverzije %e. Svako polje znakova intrepretira se prema odgovaraju´em znaku c konverzije i upisuje u varijablu na koju pokazuje odgovaraju´i argument funkc cije. Prefiks L koristi se ako je argument pointer na long double.%lg %Le. U kontrolnom znakovnom nizu mogu se pojaviti i drugi znakovi osim bjelina i znakova konverzije.. ako realan i cijeli broj uˇitavamo naredbom c . Na primjer. Pri tome se c eventualne bjeline na poˇetku preskaˇu. f. double y. Stoga je pisanje znac kova konverzije u kontrolnom znakovnom nizu razdvojeno bjelinam (kao u primjeru "%f %d") ili nerazdvojeno (kao "%f%d") posve ekvivalentno (to ne vrijedi za znakove konverzije %c i [).&x.4.2 Uˇitavanje realnih brojeva c Znakovi konverzije e.3 Drugi znakovi u kontrolnom nizu Funkcija scanf dijeli niz znakova na ulazu u polja znakova odvojena bjelinama. Prvo polje znakova zavrˇava bjelic c s nom koju %f ne uˇitava.&i).. g sluˇe za uˇitavnje varijable tipa float.. znak konverzije %f uˇitava (i konvertira) prvo polje znakova. scanf("%f %lg".%Lf. Svaki znak konverzije uˇitava jedno ulazno polje. U primjeru c scanf("%f%d"..&x. FUNKCIJA SCANF 85 5. float x.5. lf c ili lg).

86 POGLAVLJE 5. . u kojoj bjelina nakon %f preskaˇe sve eventualne bjeline na ulazu ispred c zareza. Funkcija scanf ´e uˇitati u pripadni argument najve´i niz znakova sa ulazu koji se c c c sastoji od znakova navedenih unutar uglatih zagrada..%d".&x. Znakovi u kontrolnom znakovnom nizu mogu npr.. Budu´i da se c c svako polje kao argument funkcije interpretira kao pokazivaˇ na prvi elemet c polja. To je pravilo koje vrijedi za polje bilo kojeg tipa.. Vode´e bjelina se ne preskaˇu.&i).%d".&godina). U primjeru char string[128]. c c Na primjer. biti korisni kod uˇitavanja c datuma u obliku dd/mm/gg. Iza posljednjeg uˇitanog znaka automatski se dodaje c nul-znak (\0). Znakom konverzije %s nije mogu´e uˇitati niz znakova koji sadrˇi u sebi c c z bjeline jer bjeline sluˇe za ograniˇavanje ulaznog polja. int x..4 Uˇitavanje znakovnih nizova c c s Znak konverzije %s uˇitava niz znakova.456.. scanf("%s%d".4.. ULAZ I IZLAZ PODATAKA scanf("%f.&x). Uˇitavanje zavrˇava c s prvi znak na ulazu koji nije naveden u uglatim zagradama i na kraj uˇitanog c niza dodaje se nul-znak (\0). Ako se ˇeli dozvoliti bjelina prije z zareza potrebno je koristiti naredbu scanf("%f . 1. onda ulazni podaci moraju biti oblika npr. Unutar uglatih zagrada upisuje se niz znakova. niz zavrˇava prvom bjelinom u ulaznom nizu znakova.string.]. naredba .&mjesec. Za uˇitavanje nizova z c c znakova koji ukljuˇuju i bjeline moˇemo koristiti uglate zagrade kao znak c z konverzije %[.&x.&i).. 8 bez bjeline izmedu prvog broja i zareza.&dan. 5. funkcija scanf uˇitava jedan niz znakova i jedan cijeli broj. Tada moˇemo iskoristiti naredbu z scanf("%d/%d/%d". ispred varijable string ne stavlja se adresni operator.

Sada se u odgovaraju´i argument uˇitava najve´i mogu´i niz znakova sasc c c c tavljen od svih znakova osim onih koji se nalaze u uglatim zagradama. c Znak konverzije c uˇitava jedan znak u varijablu bez obzira je li on bjelina c ili ne.. a ispred %[ mora biti ostavc c ljeno prazno mjesto kako bi bile preskoˇene sve prethodne bjeline.. Na primjer..5 Prefiks * Mogu´e je preskoˇiti neki podatak u listi i ne pridruˇiti ga odgovaraju´oj c c z c varijabli.5. linija). scanf(" %[ ABCDEFGHIJKLMNOPRSTUVWXYZ]"..4. Na primjer . Uoˇimo da smo prije %[ ostavili jedan razmak koji s c govori funkciji scanf da preskoˇi sve bjeline koje prethode znakovnom nizu. 87 uˇitava najve´i niz znakova sastavljen od velikih slova i razmaka. Ako se ˇeli ˇitati samo znakove bez z c bjelina treba koristiti " %c %c %c" ili %c zamijeniti s %1s. Stoga.. Na kraj uˇitanog niza znakova bit ´e dodan \0. linija). Nadalje. 5.. Poˇet ´e s prvim znac c c kom koji nije bjelina (zbog bjeline ispred prvog %c znaka) i proˇitat ´e tri c c uzastopna znaka bili oni bjeline ili ne. ako je prvi znak konverzije c potrebno je ispred njega staviti jednu bjelinu kako ne bi proˇitao znak za prijelaz u novi red koji je ostao c nakon prethodnog poziva funkcije scanf. uˇitati cijelu liniju bez znaka za prijelaz u novi red moˇemo pomo´u c z c naredbe scanf(" %[^\n]". To se radi tako da se znaku konverzije doda prefiks *.4. kontrolni niz " %c%c%c" ˇita tri znaka. Arguc c ment linija mora naravno imati dovoljnu dimenziju da primi sve znakove i zavrˇni nul-znak \0. linija). c To je nuˇno ukoliko smo imali prethodni poziv scanf funkcije. Naime scanf z uvijek ostavlja zavrˇni znak prijelaza u novi red u ulaznom nizu. . proˇitala prethodni znak prijelaza u novi red i budu´i da on nije u unuc c tar uglatih zagrada zavrˇila bi ˇitanje ulaznih podataka i linija ne bi bila s c uˇitana. linija). c Druga mogu´nost s uglatim zagradama je koristiti sintaksu c scanf(" %[^niz znakova]". tako da bi s naredba scanf("%[ ABCDEFGHIJKLMNOPRSTUVWXYZ]". FUNKCIJA SCANF char linija[128]..

j=4 i k=56.88 POGLAVLJE 5. c c c z 5. j=456. &k). k=3. Umjesto toga on ´e biti c s z c preskoˇen a tre´i podatak ´e normalno biti pridruˇen varijabli x. ULAZ I IZLAZ PODATAKA scanf(" %s %*d %f". Tre´e ulazno polje posve je separirano bjelinama pa dobivamo k=56. Konaˇno pogledajmo ulaz oblika c s c 1234 56 789 ˇ dobit ´emo i=123. j=456. linija. &n. Ukoliko podatak sadrˇi manje znakova od zadane c z maksimalne ˇirine polja on se uˇita samo do prve bjeline. ne´e izvrˇiti priduˇivanje drugog podatka varijabli n. &i.4. Sirina prvog ulaznog polja je tri znaka c (i=123). c c c Preostali znakovi ostat ´e na ulazu i mogu biti proˇitani novim pozivom scanf funkcije (ili neke druge ulazne funkcije). s c s a %11c uˇitava 11 znakova. viˇak znamenaka bit ´e s s s c uˇitan sljede´im konverzijskim znakom ili sljede´om scanf funkcijom. uzmimo naredbu scanf(" %3d %3d %3d". Ako na ulazu imamo c c 123 456 789 bit ´e uˇitano i=123. . Uzmimo sada ulaz oblika c c 123456789 ponovo ´emo dobiti i=123. k=789.6 Maksimalna ˇirina polja s Uz svaki kontrolni znak mogu´e je zadati maksimalnu ˇirinu ulaznog polja c s koje ´e se uˇitati tako da se ispred kontrolnog znaka stavi broj koji odreduje c c ˇirinu polja. Tako na primjer %3d uˇitava cijeli broj od najviˇe tri znamenke. &x). k=789 budu´i da sljede´e ulazno polje c c c zapoˇinje tamo gdje prethodno zavrˇava. Ukoliko na ulazu imamo 1 2 3 bit ´e uˇitano i=1. j=2. drugo ulazno polje poˇine sa znakom 4 i zavrˇava prvom bjelinom c s (j=4). Na c c c primjer. Ako pak podatak s c ima viˇe znamenaka od maksimalne ˇirine polja. &j.

ˇtoviˇe.&n). FUNKCIJA SCANF 89 5. while(scanf("%d".4. char ch.4. To moˇemo uˇiniti pomo´u funkcije z c c getchar.5. No program bi trebao kod neuspjeˇnog upisa zatraˇiti od korisnika da s z ponovi upis.&n) == s c z c 1. Stoga je potrebno prije novog poziva funkciji scanf isprazniti spremnik. c Kˆd bi mogao izgledati ovako: o int n. while(n >= 0) { // radi nesto s brojem scanf("%d". Mi ´emo. scanf("%d". Nova verzija programa bi glasila int n. n >= 0 ne´e c c c biti ispitivano. Budu´i da kˆd postaje kompleksniji moˇemo s c o z postupak ˇitanja broja zatvoriti u jednu funkciju koju ´emo nazvati get int. Uzmimo jez z c dan primjer u kojem uˇitavamo i procesiramo samo pozitivne cijele brojeve. Da bismo to realizirali uoˇimo da funkcija scanf nakon neusc pjeˇnog pokuˇaja ˇitanja nekog podatka prekida ˇitanje i ostavlja taj podatak s s c c i sve iza njega u ulaznom spremniku. Ako nije.&n) == 1 && n >= 0) { // radi nesto s brojem } Uoˇimo da prvo ispitujemo je li broj dobro uˇitan. s s Ponaˇanje programa moˇemo popraviti ako ispitujemo da li je funkcija s z scanf uspjeˇno uˇitala broj.&n). Tu ˇinjenicu c s c c moˇemo iskoristiti za provjeru jesu li svi traˇeni podaci uˇitani. iskoristiti sadrˇaj spremnika da obavijestimo koc s s z risnika o greˇci koju je napravio. To moˇemo uˇiniti testom scanf("%d".&input) != 1) . c c int get_int(void) { int input. upiˇe slovo). while(scanf("%d". // ucitaj novi broj } Ovakav kˆd ne moˇe uspjeˇno tretirati sluˇajeve u kojima korisnik uˇini o z s c c greˇku priklikom upisa (npr.7 Povratna vrijednost Funkcija scanf vra´a broj uspjeˇno uˇitanih podataka ili EOF.

. printf(" nije cijeli broj. Pojedine grupe znakova unutar kontrolnog znakovnog niza mogu se nastavljati jedna na drugu ili biti medusobno razmaknute bjelinama ili nekim drugim znakovima. } return input. Funkcija printf sluˇi za ispis podataka na standardnom izlazu..)..%f.. ..\nMolim unesite ").5 Funkcija printf int printf(const char *format. printf("cijeli broj: "). .%i %u %o %x %e.. . ULAZ I IZLAZ PODATAKA while((ch = getchar())!=’\n’) putchar(ch). .%g %c %s %p tip podatka koji se ispisuje decimalni cijeli broj (int) cijeli broj bez predznaka (unsigned int) oktalni cijeli broj bez predznaka (unsigned int) heksadecimalni cijeli broj bez predznaka (unsigned int) broj s pokretnim zarezom (double) jedan znak (char) string (char *) pokazivaˇ (void *) c . neproˇitani ulazni podatak ´e biti proˇitan c c c pomo´u druge while petlje. Sadrˇaj ´e biti ispisan funkcijom putchar i c z c korisnik ´e biti zamoljen da ponovi upis. Svi znakovi osim kontrolnih bit ´e ispisani onako kako c su uneseni. Kontrolni znakovni niz ima posve istu formu i funkciju kao kod funkcije scanf.arg n. . } Kada korisnik ne unese cijeli broj.arg_n) z gdje je kontrolni string konstantan znakovni niz koji sadrˇi informaciju o formatiranju ispisa argumenata arg 1. Op´a z c forma funkcije je printf(kontrolni_string. Najˇeˇ´e koriˇteni znakovi konverzije dani su u sljede´oj tabeli: c sc s c znak konverzije %d. c 5. arg_2.90 { POGLAVLJE 5. arg_1.

i. c s s Argumenti funkcije printf mogu biti konstante. Funkcija printf ima varijabilan broj argumenata i stoga se pri pozivu funkcije vrˇi promocija argumenata: argumenti tipa char i short konvertis raju se u tip int.. Ako treba ispisati znak %. ..i). varijable.5.. c(char)=%c\n".. ispisat ´e c c(int)=119. y=1. char c=’w’. izrazi ili polja.1 Ispis cijelih brojeva Pomo´u znakova konverzije %o i %x cijeli brojevi se ispisuju u oktalnom i c heksadecimalnom obliku bez vode´e nule odn. naredba double x=2. printf("i(okt)=%o: i(hex)=%x: i(dec)=%d\n".. printf("c(int)=%d..0. y=%f\n".i. ´e ispisati c x=2. y=%f\n". printf("x=%d. 0X. c short i=64. FUNKCIJA PRINTF 91 Funkcija vra´a broj ispisanih znakova ili EOF ako je doˇlo do greˇke. Na primjer.x.sqrt(x)). jer ´e u oba sluˇaja z c c printf vidjeti samo varijablu tipa double.. a argumenti tipa float u double. c 5. ..000000. Na primjer. Na primjer. Sliˇno pomo´u %d moˇemo ispic c z sati i varijablu tipa char i tipa short.c. ispisuje i(okt)=100: i(hex)=40: i(dec)=64 ... onda unutar kontrolnog znakovnog niza na tom mjestu treba staviti %%.. . c(char)=w ukoliko raˇunalo koristi ASCII skup znakova (119 je ASCII kod znaka “w“). Ove konverzije deˇavaju s se prije predaje argumenata funkciji pa stoga pomo´u znaka konverzije %f c moˇemo ispisati i varijablu tipa float i tipa double..5...c).414214 Svi znakovi koji nisu znakovi konverzije ispisani su onako kako su uneseni u kontrolnom znakovnom nizu "x=%d.5..

%g i %e.i. a u konverziji tipa e s eksponentom. program c #include <stdio. U konverziji tipa g naˇin ispisa (s ekspoc nentom ili bez) ovisi o vrijednosti koja se ispisuje.%Lf.%Lg tip podatka koji se ispisuje broj tipa float broj tipa double broj tipa long double .2 Ispis realnih brojeva Brojeve tipa float i double moˇemo ispisivati pomo´u znakova konverz c zije %f. printf("x(f)=%f: x(e)=%e: x(g)=%g\n".%f. } (ovisno o raˇunalu na kojem se izvrˇava) moˇe ispisati c s z i(okt)=17777777777: i(hex)=7fffffff: i(dec)=2147483647 Simboliˇka konstanta LONG MAX definirana je u datoteci zaglavlje <limits. ´e ispisati c x(f)=12345.. U konverziji tipa f broj se ispisuje bez eksponenta. double x=12345. f.92 POGLAVLJE 5...234568e+004: x(g)=12345. znak konverzije %e. g dobivaju prefiks L ako se ispisuje varijabla tipa long double.i)..678000: x(e)=1.%g %e.h> c i predstavlja najve´i broj tipa long.. Na primjer.7 Znakovi konverzije e. . Naredba.. ULAZ I IZLAZ PODATAKA Izrazi tipa long ispisuju se pomo´u prefiksa l. c 5.%g %Le.678.5. int main(void){ printf("i(okt)=%lo: i(hex)=%lx: i(dec)=%ld\n".h> long i=LONG_MAX.h> #include <limits.x).x.i...%f.x.

bf ili %a.141593 3. Drugi i tre´i ispis su zaokruˇeni na pet i deset decimala s c z budu´i da je preciznost dana eksplicitno u printf funkciji.pi. } Rezultat ispisa ´e biti c 3..bg ili %a.3e znaˇi ispis u e formatu s najmanje 7 znamenaka.2 Prva dva ispisa koriste 3 znaka dok tre´i koristi pet znakova tj..5f\n %5. pri ˇemu ´e c c c biti dano najviˇe 3 znamenke iza decimalne toˇke..3 ˇ Sirina i preciznost Uz svaki kontrolni znak mogu´e je zadati minimalnu ˇirinu ispisa tako da c s se ispred kontrolnog znaka stavi broj koji odreduje ˇirinu ispisa.. Podatak koji ima viˇe znamenaka od minic c s malne ˇirine ispisa bit ´e ispisan sa svim potrebnim znamenkama.2 1. pa je prvi ispis zaokruˇen s z na ˇest decimala.pi.pi).be gdje je a minimalna ˇirina ispisa.h> #include <math. printf("%1g\n%3g\n%5g\n".h> int main(void){ double pi=4. Sintaksa je sljede´a: c c %a.x).5. printf("%5f\n %5.14159 3.2 1. c ..5. dvije vode´e c c bjeline.10f\n".1415926536 Ispis bez specificirane preciznosti daje ˇet decimala. Pored minimalne ˇirine ispisa kod realnih brojeva mogu´e je odrediti i s c preciznost ispisa tj.0). a b preciznost. Ukoliko podatak sadrˇi manje znakova od zadane minimalne ˇirine polja..x. Na s primjer %7. .0*atan(1. ispisuje 1.. do pune ˇirine bit z s s ´e dopunjen vode´im bjelinama. Tako na s primjer %3d ispisuje cijeli broj sa najmanje tri znamenke. Tako. sljede´a naredba c double x=1. FUNKCIJA PRINTF 93 5. na s c primjer.x.5. Sljede´i program ispisuje s c c broj π: #include <stdio..2. broj decimala koje ´e biti ispisane.

5. a maksimalno 12 znakova. . z char naslov[]="Programski jezik C".*f\n %5.pi).14. . druga je ispisana s 16 znamenaka i 14 decimala. printf("%*f\n %*.141593 3. Stoga se mogu ispisati i nizovi znakova koji sadrˇe bjeline. U primjeru #include <stdio.. prec s ciznost. viˇak znakova ne´e biti z s c prikazan.11. Ako je niz znakova duˇi od 12. Tada znaˇi maksimalni z c broj znakova koji ´e biti prikazan. int i=10. printf("%s\n".4 Ispis znakovnih nizova Konverzija tipa %s primjenjuje se na znakovne nizove (nizove znakova ˇiji c je kraj signaliziran nul-znakom \0).h> int main(void){ double pi=4.14159265358979 3. Cjelobrojna varijabla (ili s izraz) na odgovaraju´em mjestu u listi argumenata odreduje ˇirinu odn.pi.. Na primjer.1415926536 Prva varijabla ispisana je s 11 znamenaka i 6 decimala (budu´i da preciznost c nije specificirana).i. ULAZ I IZLAZ PODATAKA ˇ Sirinu i preciznost ispisa mogu´e je odrediti dinamiˇki tako da se na c c mjesto ˇirine ili preciznosti umjesto broja stavi *. Na primjer %5.16.. } dobivamo ispis 3.0*atan(1.naslov). c Preciznost se moˇe koristiti i kod %s konverzije.0).h> #include <math.94 POGLAVLJE 5.pi. ispisat ´e c Programski jezik C i prije´i u novi red.5.12s specificira da ´e biti c c prikazano minimalno 5 znakova (dopunjenih bjelinama kao treba).*f\n". a tre´a c 10 decimala i 12 znamenaka. Na primjer.

printf("%.i.podatak ´e biti lijevo pozicioniran ako je manji od minimalne ˇirine c s polja. c c z c 0 Vode´e bjeline (ako su nuˇne) bit ´e zamijenjene nulama. Odnosi se samo na numeriˇke podatke koji su desno pozicionirani i koji su uˇi od c z minimalne ˇirine ispisa. c Tako ´e.5. na primjer. f ili g konverziju) osigurava da ´e decimalna toˇka biti ispisana c c i da ´e nule na krajnjoj desnoj poziciji broja biti ispisane...i.i.i). .. s ’ ’ (bjelina) jedna bjelina ´e prethoditi svakom pozitivnom broju.. + Znak + ´e biti napisan ispred pozitivnog broja. c # (uz e. . naredba c int i=66..5 Zastavice Svaka grupa znakova za konverziju moˇe sadrˇavati i zastavicu. FUNKCIJA PRINTF char naslov[]="Programski jezik C". ’ ’ c (bjelina) i #. Zastavica z z je znak koji dolazi odmah nakon znaka %.. ispisati : 66 :66 :000066 :0x42 .5.5. c # (uz o ili x konverziju) osigurava da ´e oktalni i heksadecimalni brojevi c biti ispisani s vode´om 0 ili 0x. a znaˇenja su sljede´a: c c .16s\n". +. 0. printf(":%6d\n:%-6d\n:%06d\n%#x\n".naslov). mogu´e zastavice su: -. ispisat ´e c Programski jezik 95 5.

Operatori mogu biti konstante i varijable ili njihove kombinacije.Poglavlje 6 Kontrola toka programa 6. c/d je podizraz u ˇetvrtom primjeru. u skladu s njihovim s prioritetima.2 2+13-4 x=3*2 a*(b+c/d)%2 n++ x= ++n % 2 q>2 itd. Sloˇeni izrazi mogu biti kombinacija viˇe manjih izraza koje nazivamo z s podizraima.1 6. Na primjer.1 Izrazi i naredbe Izrazi Izrazom nazivamo svaku kombinaciju operatora i operanada koju jezik dozvoljava. Ovdje su primjeri nekih izraza i njihovih vrijednosti: Izraz -3+5 x=2+17 5>3 y=7+(x=2+17) 96 vrijednost 2 19 1 26 . c Svakom izrazu u programskom jeziku C pridruˇena je vrijednost koja se z dobiva izvrˇavanjem svih operacija prisutnih u izrazu. Primjeri izraza su 7.1.

2 Naredbe Program se sastoji od niza naredbi. a zatim c se izraˇunava y=7+17. ˇto je ujedno i vrijednost ˇitavog izraza. To nam svojstvo izraza omogu´ava i pisanje viˇestrukog pridruˇivanja u c s z jednoj liniji kao npr. IZRAZI I NAREDBE 97 Ovdje treba uoˇiti da svaki logiˇki izraz (npr. 5>3) dobiva vrijednost 0 c c ukoliko nije istinit ili 1 ako je istinit.0 jedan izraz. To s c nam omogu´ava formiranje izraza poput y=7+(x=2+17): u njemu se prvo c izraˇunava podizraz x=2+17 koji u varijablu x sprema vrijednost 17. 4+4.suma. return 0. U C-u kraj naredbe oznaˇavamo znakom toˇka-zarez.1. Svaka naredba je potpuna instrukcija raˇunalu. jer je vrijednost ˇitavog podizraza na desnoj strani c c 17.6.1. naredba. legalne naredbe premda ne ˇine niˇta. Tako je c c c x=3. printf("suma = %d\n". /* deklaracijska naredba */ brojac=1.0 6. izrazi pridruˇivanja primaju z vrijednost koja se pridruˇuje lijevoj strani. Svaki izraz kojeg slijedi toˇka-zarez preds c stavlja narednu. u x=y=z=0.0. Drugo. while(brojac++ < 21) suma += brojac. dok je x=3. Sljede´i program ilustrira razliˇite c s c c tipove naredbi: #include <stdio. Tako su i 8. Tako u primjeru x=2+17 variz jabla x prima vrijednost 17.suma). C poznaje viˇe vrsti naredbi. } /* /* /* /* /* naredba pridruzivanja isto while naredba funkcijska naredba */ */ */ */ */ . suma =0.h> int main(void) /* suma prvih 20 prirodnih brojeva */ { int brojac.

/* fragment 1 */ while(brojac++ < 21) suma += brojac. side efz s fect) je svaka promjena nekog objekta koja se desi kao posljedica izraˇunavanja c .suma). s c Naredba while je primjer strukturirane naredbe. testa brojac++ < 21 i naredbe suma += brojac. Popratna pojava (eng. Prema standardu C90 sve deklaracije varijabli moraju prethoditi prvoj izvrˇnoj naredbi.suma).. do-while. Funkcijska naredba predstavz s c lja poziv funkcije. c c Ostale strukturirane naredbe su petlje for. printf("suma = %d\n".98 POGLAVLJE 6. U naˇem sluˇaju poziva se funkcija printf za ispis sume. Standard C99 dozvoljava slobodno preplitanje deklas racija i izvrˇnih naredbi. if-nareba itd. s 6. Uspoz redimo dvije while petlje. /* fragment 2 */ while(brojac++ < 21) { suma += brojac. c z Takva se cjelina naziva blok naredbi ili sloˇena naredba. Naredbe pridruˇivanja su z izrazi pridruˇivanja zavrˇeni toˇka-zarezom. Njenim izvrˇavanjem izvrˇavaju se sve naredbe iz tijela s s funkcije. U strukturiranim z naredbama jedna naredba se moˇe uvijek zamijeniti blokom naredbi. c U drugom dijeli vitiˇaste zagrade osiguravaju da su obje naredbe dio c while naredbe te se printf izvrˇava u svako prolazu kroz petlju. } U prvom dijelu primjera while naredba se proteˇe od kljuˇne rijeˇi while z c c do toˇke-zarez.1. Sa stas noviˇta while petlje sloˇena naredba tretira se isto kao i jednostavna naredba. Funkcija printf ostaje izvan petlje.3 Sloˇene naredbe z Vitiˇaste zagrade sluˇe grupiranju deklaracija i naredbi u jednu cjelinu. printf("suma = %d\n".1. KONTROLA TOKA PROGRAMA Deklaracijskim naredbama deklariramo varijable. Ona se sastoji od tri dijela: kljuˇne rijeˇi while. 6. s z Svaka blok naredba moˇe sadrˇavati deklaracije varijabli i izvrˇne naz z s redbe.4 Popratne pojave i sekvencijske toˇke c Ovdje ˇelimo uvesti neˇto C terminologije.

sequence s c point).0. popratna pojava naredbe x=50.0. c c .1. c Pri tome je potpuni izraz svaki izraz koji nije podizraz nekog ve´eg izraza.0. Prije prijelaza na novu naredbu c c c sve popratne pojave prethodne naredbe moraju biti izvrˇene. Ovdje je termin popratna pojava posve c primjeren. test petlje ˇini potpuni izraz i stoga pove´anje varijable brojac mora biti c c izvrˇeno odmah nakon izraˇunavanja testa. Sada se name´e pitanje u kojem se trenutku izvrˇavaju popratne poc s jave. s Isto tako. c c c s c no sa stanoviˇta jezika osnovna intencija je izraˇunati izraz.6.brojac). Citav izraz pridruˇivanja je potpuni c z izraz. Na primjer.0. Jezik ne c s specificira ho´e li x biti pove´an nakon izraˇunavanja svakog podizraza ili tek c c c nakon izraˇunavanja ˇitavog izraza te stoga ovakve izraze treba izbjegavati. To c je znaˇajno u strukturnim naredbama koje testiraju neki izraz. a sekvencijska toˇka je toˇka-zarez te stoga C garantira da ´e x biti c c c dva puta pove´an za 1 prije prijelaza na izvrˇavanje nove naredbe. Mjesto u programu na kojem se popratne pojave moraju izvrˇiti prije s nastavka izvrˇavanja programa naziva se sekvencijska toˇka (eng. 99 je postavljnje varijable x na vrijednost 50. Toˇka-zarez oznaˇava sekvencijsku toˇku. Kao posljedica izraˇunavanja izraza varijabla x je postavljena na 50. 4+x++ nije potpun izraz i stoga C ne garantira da ´e x biti pove´an odmah c c ˇ nako izraˇunavanja podizraza 4+x++. s U primjeru y=(4+x++)+(6-x++). c u petlji while(brojac++ < 21) printf("%d \n". IZRAZI I NAREDBE nekog izraza. c Drugi primjer popratne pojave nalazimo u while naredbi iz prethodnog programa while(brojac++ < 21) gdje kao popratnu pojavu izraˇunavanja izraza brojac++ < 21 dobivamo c pove´anje varijable brojac za 1. Takva se terminologija moˇe z ˇiniti ˇudnom jer je oˇita intencija ove naredbe postaviti varijablu x na 50. Na primjer. kao ˇto bi netko mogao pomisliti. na kraju svakog potpunog izraza nalazi se sekvencijska toˇka. a ne nakon izvrˇavanja ˇitave s c s c while naredbe.

Naredba goto pruˇa mogu´nost prekida sekvencijalnog izvrˇavanja programa i z c s skoka na neku drugu lokaciju unutar programa. c c Umjesto jedne naredbe if moˇe sadrˇavati blok naredbi omeden vitiˇastim z z c zagradama (sloˇena naredba).. 6.. u dijelu programa int x.x) te zatim. ispunjenost uvjeta) izvrˇava se naredba. . ako x nije strogo pozitivno.. s Na primjer.. onda se s z naredba ne izvrˇava i program se nastavlja prvom naredbom iza if naredbe. odn. pretvaranje malog u veliko slovo moglo bi biti izvedeno na sljede´i naˇin: c c . Naredba prvo izraˇunava c c c izraz uvjet i ako je njegova vrijednost razliˇita od nule (ˇto se interpretira c s kao istina. .1 if naredba if(uvjet) naredba. KONTROLA TOKA PROGRAMA 6. c u sljede´oj naredbi. neispunjenost uvjeta). Naredba if ima oblik gdje je uvjet aritmetiˇki (ili pokazivaˇki) izraz. z if(uvjet) { naredba...2. if(x>0) printf("\n x= %d \n". naredba.. z Uvjet u if naredbi je najˇeˇ´e izraz sastavljen od relacijskih i logiˇkih c sc c operatora. if-else i switch omogu´avaju izvrˇavanje c s pojedinih grupa naredbi u ovisnosti o tome da li su ispunjeni neki uvjeti.100 POGLAVLJE 6.2 Naredbe uvjetnog grananja Naredbe uvjetnog grananja if. } U ovom se sluˇaju ˇitav blok naredbi izvrˇava ili ne izvrˇava ovisno o tome c c s s da li uvjet ima vrijednost istine ili laˇi. Na primjer. vrijednost c c izraza x>0 bit ´e 0 i program ´e inkrementirati x bez poziva funkcije printf.x). izraz x>0 imat ´e vrijednost 1 ako je vrijednost varijable x strogo pozitivna c i varijabla x ´e biti ispisana naredbom printf("\n x= %d \n".. odn. naredba. pove´ana za 1. Ako izraz uvjet s daje nulu (ˇto se interpretira kao laˇ. x++.

\n"). izraˇunavanje izraza x != 0 && (20/x) < 5 u sluˇaju x=0 prestat ´e nakon c c c x != 0.. jer je vrijednost cijelog izraza u tom trenutku poznata (=laˇ).2... Uoˇimo op´enito da uvjet if(x!=0) moˇemo pisati kao if(x).. Oba c uvjeta zadovoljavaju samo mala slova (engleske) abecede koja u naredbi c=’A’+c-’a’.. .6. Na primjer.. .2 if-else naredba if-else naredba ima oblik ..h> char *string1. c c c c 6.. Funkcija strcmp ´e vratiti nulu ako su stringovi string1 i string2 jednaki. pretvaramo u odgovaraju´a velika slova... te isto c c z tako if(x==0) moˇemo zamijeniti s if(!x). u dijelu kˆda o int x. c Relacijski operatori ne mogu se primijeniti na znakovne nizove (stringove) pa stoga moramo koristiti strcmp funkciju kao u sljede´em primjeru: c #include <string... 101 Ovdje koristimo logiˇko I kako bismo povezali uvjete c>=’a’ i c<=’z’. if(strcmp(string1..string2)==0) printf("Stringovi su jednaki.2.\n").string2)) printf("Stringovi su jednaki. NAREDBE UVJETNOG GRANANJA char c... if(c>=’a’ && c<=’z’) c=’A’+c-’a’. Stoga z podizraz (20/x) < 5 ne´e biti izraˇunat i do dijeljenja s nulom ne´e do´i.. if(x != 0 && (20/x) < 5) . *string2.. z Kod logiˇkih izraza koji se pojavljuju u uvjetu if naredbe ponekad treba c koristiti pravilo da se izraz izraˇunava sve dok njegova vrijednost ne postane c poznata. c Istu bismo if naredbu mogli napisati i u obliku if(!strcmp(string1. ....

Vrijednost nula znaˇi da je program uspjeˇno izvrˇen.h> i ona zaustavlja izvrˇavanje s programa. ekvivalenta s if(a>b) max=a. onda se izvrˇava naredba1 a u suprots nom naredba2. Vrijednost status predaje se operacijskom sustavu. POGLAVLJE 6. obje naredbe mogu biti blokovi naredbi. Koristit ´emo sljede´i model: c c .. Na taj naˇin nije potrebno pokretati program za testiranje c svakog pojedinog broja. else naredba2. }else y/=x.2. s Uoˇimo da je sljede´a naredba s uvjetnim operatorom c c max = a > b ? a : b. analizira i c ispisuje rezultat. 6.102 if(uvjet) naredba1. if(!x){ printf("Djelitelj jednak nuli!\n").. a vrijednost razliˇita od nule c s s c signalizira da je program zaustavljen zbog greˇke. Na primjer.h> . exit(-1). Funkcija void exit(int status) deklarirana je u datoteci zaglavlja <stdlib.3 Primjer: Djelitelji broja Postavimo si sada sljede´i zadatak: za zadani prirodan broja treba ispisati c sve njegove djelitelje. else max=b. #include <stdlib. Program ´emo konstruirati kao petlju u kojoj se broj unosi.... KONTROLA TOKA PROGRAMA Ako izraz uvjet ima vrijednost istine.

2. Dijele´i redom dobivamo parove: 2. div). num. (div*div) <= num. 64. vrijednost ne´e biti uˇitana i funkcija ´e vratiti nulu ˇto ´e zaustaviti petlju. Uoˇimo da je num/div cjelobrojno dijeljenje koje nam daje drugi djelitelj. div). div < num. Prvi se javlja u sluˇaju da je s c broj kvadrat nekog broja. (div*div) <= num. Ovu petlju moˇemo uˇiniti puno efikanijom ako primjetimo da svaki puta z c dobivamo dva djelitelja. div. ++div) if(num % div == 0) printf("%d je djeljiv s %d\n". num/div). div. num. ++div) if(num % div == 0) { if(div * div != num) printf("%d je djeljiv s %d i %d\n". zagrade iza prve if-naredbe nisu nuˇne. 128%2 = 0 ˇto daje da je 128 djeljivo s s 2. ++div) if(num % div == 0) printf("%d je djeljiv s %d i %d\n". Tada bismo ispisivali 144 je djeljiv s 12 i 12 Da bismo to izbjegli uvodimo joˇ jednu if naredbu:1 s for(div=2.16. else printf("%d je djeljiv s %d. Na primjer. 8. Ako pri sljede´em pozivu c c s c c s c c funkciji scanf upiˇemo npr. c U ovom pristupu imamo joˇ dva problema. Ta nam c c c primjedba znaˇajno reducira koliˇinu raˇunaja jer sada moˇemo pisati: c c c z for(div=2.\n". num/div). 32. num. kao npr. NAREDBE UVJETNOG GRANANJA printf("Unos:") while(scanf vraca vrijednost 1){ analizirati broj i ispisati rezultat printf("Unos:") } 103 Funkcijom scanf uˇitavamo brojeve. 144 = 12 * 12. } 1 Budu´i da if-else ˇini jednu naredbu. 4.6. c c z . q ili neko drugo slovo. c Dalje od broja ˇiji je kvadrat ve´i ili jednak od 128 nije potrebno i´i. c s c Najjednostavniji naˇin za pronalaˇenje svih djelitelja zadanog broja num c z je sljede´i: c for(div=2. Kada je broj ispravno uˇitan scanf c c vra´a broj uˇitanih vrijednosti (1 u naˇem sluˇaju). num. ali i sa 128/2 = 64.

Jasno je da ako num nije djeljiv niti s jednim brojem onda nikad ne´emo u´i u tijelo if naredbe. div. long div.else naredbe dobivamo sloˇenu naredbu z if(uvjet1) . } return 0. else printf("%d je djeljiv s %d. ++div) { if(num % div == 0) { if(div * div != num) printf("%d je djeljiv s %d i %d\n".\n". flag=1. Stoga c c moˇemo uvesti novu varijablu flag koju ´emo inicijalizirati nulom i postaviti z c na 1 u tijelu if naredbe. Cijeli program sada izgleda ovako: #include <stdio. num/div).4 Viˇestruka if-else naredba s Viˇe if-else naredbi mogu se nadovezati jedna na drugu. num. Ako na kraju programa imamo flag == 0 znaˇi c da se radi o prim broju. Tako pomo´u s c dvije if .num).&num) == 1) { for(div=2.2. } } if(flag == 0) printf("%ld je prom broj.104 POGLAVLJE 6. } 6. (div*div) <= num. printf("Unesite cijeli broj ili q za izlaz: ").\n". num.h> int main(void) { long num. KONTROLA TOKA PROGRAMA Drugi problem je kako prepoznati prim broj. // broj koji provjeravamo // potencijalni djelitelj // prim broj? printf("Unesite cijeli broj ili q za izlaz: "). unsigned flag = 0. while(scanf("%ld". div).

z .0) y=3. return y. 105 u kojoj se prvo ispituje uvjet1 te ako je istinit izvrˇava se naredba1. Na primjer.0+exp(5. else if(x<5.0)).0+(x-2.0*(1.2.0)*exp(x). Za drugu if-else naredbu kaˇemo da je z ugnijeˇdena u prvu.0). } Uoˇimo da je viˇestruka if-else naredba u tijelu funkcije samo drukˇiji c s c naˇin pisanja dviju ugnijeˇdenih if-else naredbi: c z if(x<2.0). else y=3.0+(x-2. imamo funkciju #include <math. NAREDBE UVJETNOG GRANANJA naredba1. Ako uvjet1 nije istinit izraˇunava se uvjet2 c te se izvrˇava ili naredba2 ili naredba3 ovisno o istinitosti izraza uvjet2.6. s Program se zatim nastavlja naredbom naredba4.0+(x-2.0)*exp(x).0) y=3. else naredba3. if(x<2. naredba4. a zatim s se prelazi na naredbu nareba4.0) y=3.h> double f(double x) { double y.0)). Budu´i da je if-else jedna naredba nisu nam bile potrebne zagrade poslije c else dijela prve if-else naredbe.0+(x-2.0+exp(5. else y=3. else if(x<5.0*(1. else if(uvjet2) naredba2.0) y=3.

a/b).h> int main(void) { float a.5 Primjer. } 6.a+b). else printf("Nedopustena operacija!\n").2.&operacija).a*b). char operacija. a else dio zadnje z if-else naredbe moˇe se ispustiti. oduzimanje(o).&a). U ovisnosti o c uˇitanom znaku izvrˇava se jedna od ˇetiri raˇunske operacije.a-b). printf("Upisati operaciju: zbrajanje(z).6 Sparivanje if i else dijela Budu´i da se else dio if-else naredbe moˇe ispustiti mogu´e su amc z c bivalentne situacije ako je ugnjeˇdeno viˇe if-else naredbi od kojih neke z s nemaju else dio. c s c c #include <stdio. return 0. Viˇestruki izbor s Ugnijezditi se moˇe bilo koji broj if-else naredbi.b. scanf(" %f".&b). scanf(" %c". printf("Upisati drugi broj: "). else if(operacija==’m’) printf("%f\n".106 POGLAVLJE 6. U sljede´em primjeru uˇitavaju se dva z c c broja i jedan znak koji predstavlja izbor raˇunske operacije. KONTROLA TOKA PROGRAMA 6. else if(operacija==’d’) printf("%f\n". printf(" mnozenje(m). printf("Upisati prvi broj: ").dijeljenje(d) :"). scanf(" %f". else if(operacija==’o’) printf("%f\n". Stoga vrijedi pravilo: .2. if(operacija==’z’) printf("%f\n".\n").

SWITCH NAREDBA Svaka else naredba pripada sebi najbliˇoj if naredbi. z Pogledajmo sljede´i primjer: c if(n>0) if(a>b) z=a.3. break. Sljede´i naˇin pisanja sugerira da je else naredba pridriˇena vanjskoj if c c z naredbi (if(n>0)) if(n>0) if(a>b) z=a. c o c s Naredba ima sljede´i oblik: c switch(izraz) { case konstanta_1: naredba_1. 6.. . case konstanta_2: . Ona nam c z omogu´ava da selektiramo kˆd koji ´e se izvrˇiti na osnovu nekog uvjeta. 107 Ovdje je else naredba pridruˇena unutarnjoj if naredbi (if(a>b)) jer joj je z najbliˇa. Ako ˇelimo da else naredba bude pridruˇena vanjskoj if naredbi z z z moramo koristiti zagrade: if(n>0){ if(a>b) z=a. else z=b.. dok je ona ustvari pridruˇena unutarnjoj if naredbi (if(a>b)).6.... Takav stil z pisanja treba izbjegavati.3 switch naredba Naredba switch sliˇna je nizu ugnjeˇdenih if-else naredbi. } else z=b.. else z=b.

. Ukoliko je izraz = konstanta i program se nastavlja naredbom naredba i i svim naredbama koje dolaze nakon nje. onda se izvrˇava samo s naredba koja dolazi nakon kljuˇne rijeˇi default i sve naredbe iza nje.. . . c c sve od vitiˇaste zagrade koja omeduje switch naredbu. konstanta 2.h> int main(void) { . • Nakon kljuˇne rijeˇi case pojavljuju se cjelobrojne konstante ili konsc c tantni izrazi. case konstanta_n: naredba_n. Nakon toga program se nastavlja prvom naredbom iza switch naredbe.. .. . KONTROLA TOKA PROGRAMA naredba_2.konstanta n.. int ili enum). s Zatim se provjerava da li se dobivena vrijednost podudara s jednom od konstanti: konstanta 1. . sve do break naredbe. • Ako izraz nije jednak niti jednoj konstanti.108 POGLAVLJE 6. . break. Ako c z nije i ako nema podudaranja izaza i konstanti. c • Sluˇaj default ne mora nuˇno biti prisutan u switch naredbi. } • Izraz u switch naredbi mora imati cjelobrojnu vrijednost (char. default: naredba. .. . program se nastavlja prvom naredbom iza switch naredbe.. Naredba switch je ˇesto jasnija od niza if-else naredbi.. • Pri izvrˇavanju switch naredbe prvo se testira vrijednost izraza izraz.... .. Program iz c s c c c proˇle sekcije mogli smo napisati pomo´u switch naredbe na sljede´i naˇin: #include <stdio... break. .

} return 0. default: printf("Nedopustena operacija!\n"). printf("Upisati operaciju: zbrajanje(z). case ’m’: printf("%f\n".3. oduzimanje(o)..&a). Efekt ispuˇtanja z s s naredbe break je “propadanje kˆda” u niˇi case blok. Izvrˇavanje se nastavlja do prve break nac s o s redbe ili sve do kraja switch naredbe. scanf(" %c". ako nema break naredbi. Na primjer. printf("Upisati drugi broj: "). 109 printf("Upisati prvi broj: "). onda bi bilo ispisano oduzimanje.. break. printf(" mnozenje(m). break. z Naredba break moˇe se ispustiti na jednom ili viˇe mjesta. dijeljenje i poruka z "Nedopustena operacija!\n". SWITCH NAREDBA float a. } Namjera programera je sada jasnije izraˇena nego s nizom if-else naredbi. char operacija. scanf(" %f".\n"). case ’o’: printf("%f\n". break.&b). case ’d’: printf("%f\n". . scanf(" %f".a-b).dijeljenje(d) :"). .b.&operacija)..a*b). Pogledajmo sljede´i primjer: c int i. ako bio z smo u gornjem kˆdu ispustili sve break narebe i ako bi operacija bila jedo naka ’o’. break.6.. Selektirani case je stoga ulazna toˇka od c koje poˇinje izvrˇavanje kˆda. switch(operacija){ case ’z’: printf("%f\n". mnoˇenje.a+b).a/b).

dio kˆda o i=0. . tj. while (i<10){ printf("%d\n".5 c c izvrˇi naredba printf("i < 6\n"). 6. While petlja ima oblik naredba ´e se izvrˇavati sve dok izraz ima vrijednost istine. case 6: printf("i = 6\n"). c Na primjer.. kao u ovom primjeru: z #include <stdui. break.i).2.110 POGLAVLJE 6. tada je pogodnija for petlja.3.h> /* Srednja vrijednost upisanih brojeva (razlicitih od 0). . while petlja se rijetko koristi u sluˇaju kada je c c broj ponavljanja petlje unaprijed poznat (kao u prethodnom primjeru). Kako se ne bi izvrˇila naredba printf("i s s = 6\n") morali smo staviti break nakon printf("i < 6\n"). } ispisat ´e brojeve 0. default: printf("i > 6\n"). i++. */ int main(void) { . } “Propadanje” kroz case blokove omogu´ava da se u sluˇajevima i=1.9. sve dok je c s razliˇit od nule. Petlju while ´emo najˇeˇ´e koristiti kada se broj c c sc ponavljanja ne moˇe unaprijed znati. break.4.4 while petlja while (izraz) naredba. .1. naredba moˇe biti blok naredbi unutar vitiˇastih zagrada c z c koji obiˇno modificira izraz kako bi kontrolirao petlju. KONTROLA TOKA PROGRAMA switch(i) { case 1: case 2: case 3: case 4: case 5: printf("i < 6\n").

a s izraz 3 mijenja parametre koje je izraz 1 inicijalizirao.6. scanf("%lf". return 0. printf(" Srednja vrijednost = %f\n". while (x!=0. ili nulu za kraj.5 for petlja Petlja for ima oblik for(izraz_1. i ekvivalentna je konstrukciji izraz_1.sum).0){ sum+=x.5. sljede´i kˆd raˇuna skalarni produkt dva vektora: c o c float x[100000]. izraz_3) naredba. printf(" x[%d]= ". printf(" x[0]= ").). y[100000]. izraz_2. } sum/=i. for petlja zapoˇinje inicijalizacijom kontrolnih parametara. } izraz 1 inicijalizira parametre koji kontroliraju petlju. Stoga je for(. xydot. FOR PETLJA int i=0.&x)..\n"). izraz 2 predstavlja uvjete koji moraju biti zadovoljeni da bi se petlja nastavila izvrˇavati. izraz 2 se c izraˇunava na poˇetku svakog prolaza kroz petlju. while (izraz_2){ naredba. double sum=0. scanf("%lf". a izraz 3 se izraˇunava c c c na kraju svakog prolaza kroz petlju.0.&x). printf(" Upisite niz brojeva !=0. } 111 6. beskonaˇna c petlja koja ne radi niˇta. Niti jedan od tri izraza ne treba biti prisutan. double tmp. . s Na primjer. izraz_3.x.++i). Ako nema srednjeg izraza pretpostavlja se da je njegova vrijednost 1.

isdigit c z vra´a broj razliˇit od nule (istina) ako je znak znamenka.. . KONTROLA TOKA PROGRAMA // inicijalizacija varijable // u kojoj se sumira for(i=0.i<100000.. s z Kao sloˇeniji primjer napiˇimo implementaciju funkcije atoi iz stanz s dardne biblioteke (zaglavlje <stdlib. } Ovdje koristimo funkcije isspace i isdigit koje su deklarirane u datoteci zaglavlja <ctype. for(i=0. Sada znamo da su sve znamenke c c pred nama brojevi. Ovdje smo upotrijebili varijablu sumacije u dvostrukoj preciznosti kako bismo smanjili greˇke zaokruˇivanja. Analogno. U suprotnom vra´a nulu (laˇ).i++) tmp += x[i]*y[i].n. U prvoj for petlji preskoˇene su sve bjeline.sign. /* prazna naredba */ sign=(s[i]==’-’)?-1:1.i++) n=10*n+(s[i]-’0’). tmp=0.0. Zatim je uvjetnim operatoc rom odreden predznak broja (koji ne mora biti prisutan). Posljednja for petlja izvrˇava se dok ne naidemo na znak s koji nije broj.112 ..h> /* atoi : prevodi string s[] u cijeli broj */ int atoi(const char s[]) { int i... isspace vra´a broj razliˇit od nule (istina) ako je znak bjelina.i++) /* preskace sve bjeline */ . uˇitati predznak i redom proˇitati sve znamenke te ih c c c prevesti u broj tipa int... koja prevodi niz znakova koji predstavlja cjeli broj u njegovu numeriˇku vrijednost. POGLAVLJE 6. isdigit(s[i]). xydot=tmp. prijelaz c c u novi red ili tabulator.. Obje funkcije uzimaju jedan znak i vra´aju cijeli broj c (int). /* preskoci predznak */ for(n=0. Funkcija treba preskoˇiti c c sve poˇetne bjeline. #include <ctype. a nulu (laˇ) ako c c z nije.isspace(s[i]).h>). if(s[i]==’+’ || s[i]==’-’) i++. Ako je predznak prisutan sljede´a if naredba ga preskaˇe.h>. Znamenke se pretvaraju u brojeve u naredbi n=10*n+(s[i]-’0’). return sign*n.

c mogli bismo pisati i=(i=3. prevodi s u long . Izrazi separirani zarezom izraˇunavaju se slijeva c na desno i rezultat ˇitavog izraza je vrijednost desnog izraza. FOR PETLJA 113 Tu se oslanjamo na ˇinjenicu da se u svim skupovima znakova koji su u upoc trebi znamenke nalaze jedna iza druge u rastu´em poretku.6.h>. U tome nam pomaˇe operator zarez.j. } } Funkcija strlen deklarirana je u datoteci zaglavlja <string.h> i daje duljinu znakovnog niza (bez nul-znaka).h> Funkcija atoi koju smo realizirali u primjeru u prethodnoj sekciji implementirana je u standardnoj biblioteci i njen prototip se nalazi u datoteci zaglavlja <stdlib.i++.) koji separira dva c z izraza je ustvari operator. . Operator zarez koristi se uglavnom u for naredbi.h> /* invertiraj : invertiraj znakovni niz */ void invertiraj(char s[]) { int c. Pokaˇimo to na funkciji invertiraj koja invertira niz znakova. z #include <string.i.i+4). i dobili bismo rezultat i=7. U toj datoteci postoje i prototipovi nekih drugih funkcija za konerziju od kojih navodimo: double atof(const char *s) int atoi(const char *s) long atol(const char *s) prevodi s u double.5. Napomenimo joˇ da zarezi u pozivu funkcije (ako ona ima viˇe argus s menata) nisu operatori i stoga nije garantirano izraˇunavanje argumenata c funkcije slijeva na desno. s[i]=s[j].j--) { c=s[i].i<j. Tada s[i]-’0’ c daje numeriˇku vrijednost znamenke s[i].1 Operator zarez U jednoj for naredbi mogu´e je izvrˇiti viˇe inicijalizacija i promjena c s s brojaˇa. s[j]=c. 6.5. Na primjer. for(i=0. Uoˇimo da smo za pomo´nu varijablu c c c mogli koristiti varijablu tipa int (umjesto char).5. c 6. prevodi s u int .2 Datoteka zaglavlja <stdlib. Zarez (.j=strlen(s)-1.

KONTROLA TOKA PROGRAMA Pored ostalih navedimo joˇ i ove funkcije iz <stdlib. absolutna vrijednost.1. c Kao primjer napiˇimo funkciju itoa koja pretvara cijeli broj u znakovni s niz (string). 6.while petlja do-while petlja ima oblik naredba. ispisat ´e brojeve 0.i). absolutna vrijednost. . . s Na primjer. do{ printf("%d\n". dio kˆda o i=0. s[i]=’\0’. } while((n/=10)>0). } while (i<10).. /* zapamtimo predznak */ i=0. /* itoa : pretvara n u znakovni niz s */ void itoa(int n.9. if((sign=n)<0) n=-n. Za razliku od while c petlje vrijednost izraza se kontrolira na kraju prolaza kroz petlju. tj. . Petlja se stoga izvrˇava barem jednom. . if(sign<0) s[i++]=’-’. do { s[i++]=n%10+’0’. while (izraz). i++. sve dok je c s razliˇit od nule.h>: s void exit(int status) int abs(int n) long labs(long n) normalno zaustavljanje programa.sign.114 POGLAVLJE 6.6 do do . naredba ´e se izvrˇavati sve dok izraz ima vrijednost istine. char s[]) { int i. naredba moˇe biti blok naredbi unutar vitiˇastih zagrada c z c koji obiˇno modificira izraz kako bi zaustavio petlju.

6. while i do-while petlji. Kod for c c petlje program se nastavlja s naredbom inkrementacije brojaˇa. Naredba continue koristi se unutar for. Iz nje se izlazi ukoliko se uˇita negativan c c broj.7..&i). } while(1) je beskonaˇna petlja. Nakon toga se prelazi na izraˇunavanje testa c petlje. Nakon nailaska na continue preostali dio tijela petlje se preskaˇe i program nasc tavlja sa sljede´im prolazom kroz petlju.. 6. Taj se postupak obavlja u do--while petlji. Pri nailasku na naredbu z break kontrola programa se prenosi na prvu naredbu iza petlje ili switch naredbe unutar koje se break nalazi. . Kˆd koji bi samo preskakao negativne vrijednosti (i ne bi ih obradivao) o mogao bi se izvesti naredbom continue: int i. . while{1){ scanf("%d". z Moˇe se koristiti sa for. NAREDBE BREAK I CONTINUE invertiraj(s). Stoga znamenke dobivamo pomo´u operatora %. Nakon zavrˇetka te procedure dobives nom nizu znakova dodajemo predznak i nul-znak te ga prosljedujemo funkciji invertiraj. while i do-while petljom. budu´i da c c ona dolazi nakon tijela petlje.. while(1){ scanf("%d".. U sluˇaju while i do-while petlje c c to znaˇi da se program nastavlja s izraˇunavanjem testa petlje. if (i<0) continue.&i). Primjer upotrebe break naredbe u while petlji: int i..7 Naredbe break i continue Naredba break sluˇi za zaustavljanje petlje i izlazak iz switch naredbe.. if (i<0) break.. } 115 Algoritam slaˇe znamenke broja u obrnutom redoslijedu pa stoga koristimo z funkciju invertiraj() iz prethodne sekcije.. Ostatak u dijeljenju s 10 svakog cijelog broja je njegova posljednja dekadska znamenka. Nako ˇto izdvojimo posljednju znamenku c s broj dijelimo s 10 (cjelobrojno dijeljenje) kako bismo ju eliminirali.

putchar(ch). s c Oblik joj je goto label.. } POGLAVLJE 6.. Znak za c c prijelaz u novi red je sada ukljuˇen u ukupan zbroj uˇitanih znakova. c c Ako se naredba break nalazi u petlji koja je ugnjeˇdena u nekoj drugoj z petlji. if(ch == ’\n’) continue. onda se pomo´u break izlazi samo iz dublje petlje. . c 6. No u ovom sluˇaju prva sljede´a c c c naredba je pove´anje brojaˇa.116 . c o c c c Naime. if(ch == ’\n’) continue. Pogledajmo primjer c for(i=0. c Naredba continue u for petlji malo je drugaˇiji. i++. petlja se nastavlja iza posljednje naredbe u tijelu. } Ovaj ´e kˆd proˇitati 10 znakova ne raˇunaju´i znak za prijelaz u novi red. ++i) { ch=getchar(). i < 10. c Pogledajmo joˇ jedan primjer continue naredbe: s i=0.. nakon continue. koja pove´ava brojaˇ i c c c c prva sljede´a naredba je ispitivanje uvjeta i < 10.. ++i. KONTROLA TOKA PROGRAMA Sada nam u dijelu kˆda koji obraduje pozitivne brojeve treba neki drugi o naˇin izlaza iz petlje... while(i < 10) { ch=getchar().. tj. putchar(ch). a zatim testiranje i < 10. U while petlji to znaˇi da se preskaˇe naredba i++. putchar(ch).. } Nakon izvrˇenja naredbe continue dolazimo ponovo iz posljednje naredbe u s tijelu petlje.8 goto naredba goto naredba prekida sekvencijalno izvrˇavanje programa i nastavlja izs vrˇavanje s naredbom koja je oznaˇena labelom koja se pojavljuje u goto. se preskaˇe.

) goto cont. cont: ... kˆd o for(. while{i<=100){ .. if (..... exit(-1)..... /* detekcija greske */ error: { printf("Greska : negativna vrijednost!\n")..&i). pomo´u goto se ne moˇe iza´i iz funkcije.. GOTO NAREDBA 117 gdje je label identifikator koji sluˇi za oznaˇavanje naredbe kojom se nasz c tavlja program... scanf("%d". scanf("%d".... } je ekvivalentan s for(. neke upotrebe goto naredbe su .. } Sliˇno vrijedi i za continue unutar while i do-while petlje.&i).) { ... if (..... c c z c Naredbe break i continue mogu se izvesti pomo´u goto naredbe..) continue.. Nadalje... c Program napisan uz upotrebu goto naredbe op´enito je teˇe razumjeti c z od programa koji ju ne koriste.... Na primjer...8. Sintaksa je label: naredba.. s Drugim rijeˇima. ...... .6. .) { ... } . Na c primjer. } Labela na koju se vrˇi skok mora biti unutar iste funkcije kao i goto naredba........ if (i<0) goto error...

Op´enito stoga upotrebu goto naredbe treba izbjegavati. c .118 POGLAVLJE 6. Na primjer. skok u if ili else blok izvan if naredbe. c ili skok u blok naredbu s preskokom deklaracija lokalnih varijabli na poˇetku c bloka. KONTROLA TOKA PROGRAMA posebno zbunjuju´e.

Prvi argument arg 1 je varijabla tipa tip 1 itd.. Unutar zagrada nalazi se deklaracija formalnih argumenata s funkcije. kao ˇto je to sluˇaj s funkcijama iz standardne biblioteke. Funkcija vra´a rezultat svog izvrˇavanja pomo´u naredbe return. s c 7. Time postiˇemo ve´u jasno´u programa i olakˇavamo budu´e z c c s c modifikacije. Tada je funkciju mogu´e koristiti u razliˇitim c c programima. Op´i c s c c oblik te naredbe je return izraz. Svaka funkcija treba biti osmiˇljena tako da obavlja jednu dobro s definiranu zada´u te da korisnik funkcije ne mora poznavati detalje njene imc plementacije da bi ju koristio. Unutar vitiˇastih zagrada c pojavljuje se tijelo funkcije koje se sastoji od deklaracija varijabli i izvrˇnih s naredbi.Poglavlje 7 Funkcije Funkcija je programska cjelina koja uzima neke ulazne podatke. c s Pomo´u funkcija razbijamo sloˇene programske zada´e na niz jednostavc z c nijih cjelina.tip_n arg_n) { tijelo funkcije } c gdje je tip podatka tip podatka koji ´e funkcija vratiti kao rezultat svog izvrˇavanja. Deklaracije pojedinih argumenata medusobno se odvajaju zarezom..1 Definicija funkcije Definicija funkcija ima oblik tip_podatka ime_funkcije(tip_1 arg_1. . 119 . izvrˇava s odreden niz naredbi i vra´a rezultat svog izvrˇavanja pozivnom programu. .

return c. ali nije na primjer za EBCDIC c s naˇin kodiranja.’a’) : z. c Funkcija se poziva navodenjem svog imena i liste stvarnih argumenata. Vrijedi sljede´e pravilo: z c • Funkcija moˇe vratiti aritmetiˇki tip. } U gornjem programu malo je stvarni argument koji se kopira na mjesto formalnog argumenta z. Znak koji funkcija vra´a smjeˇten je u varijablu veliko c s prilikom poziva . &malo). izraz ´e biti konvertiran u tip podatka.veliko). Izraz se moˇe c z staviti u oble zagrade ali to nije nuˇno. printf("\nUneseno slovo pretvoreno u veliko = %c\n". char malo_u_veliko(char z) { char c. Ta je pretpostavka ispunjena za ASCII naˇin kodiranja. koji je najraˇireniji. Funkciju iz prethodnog primjera u glavnom programu pozvamo na sljede´i c naˇin: c int main(void) { char malo.120 POGLAVLJE 7. sljede´a funkcija pretvara mala slova (engleske abecede) u c c velika. Ime funkcije je malo u veliko. veliko. veliko = malo_u_veliko(malo). scanf("%c". c= (z >= ’a’ && z <= ’z’) ? (’A’ + z . Takvu situaciju treba naravno c c izbjegavati. Formalni argument je samo jedan i tipa je char. strukturu. z Ako je tip izraza u naredbi return razliˇit od tipa podatka koji funkcija c vra´a. FUNKCIJE Vrijednost izraza se vra´a dijelu programa koji poziva funkciju. Na primjer. uniju ili pokazivaˇ ali z c c ne moˇe vratiti drugu funkciju ili polje. return 0. } Funkcija pretpostavlja da slova abecede imaju rastu´e uzastopne kodove te c da mala slova prethode velikim ili obratno. printf("Unesite malo slovo: "). vra´ena vrijednost je takoder tipa char.

. Funkcija koja vra´a neku vrijednost najˇeˇ´e se poziva u izrazu pridruˇic c sc z vanja kao u gornjem primjeru.1. else return z. funkciju iz matematiˇke biblioteke c double sqrt(double) koja raˇuna drugi korijen broja moˇemo pozvati na sljede´i naˇin: c z c c double x. i tako eliminirati varijablu veliko. u glavnom c programi mogli smo pisati printf("\nUneseno slovo pretvoreno u veliko = %c\n". DEFINICIJA FUNKCIJE veliko = malo_u_veliko(malo). y=sqrt(2*x-3) √ i varijabla y ´e sadrˇavati vrijednost 2x − 3. no funkciju je mogu´e pozvati i unutar svakog c drugog izraza koji uzima tip podatka koji ona vra´a. 121 Vaˇno je primijetiti da funkcija prima kopiju stvarnog argumenta tako da z nikakva manipulacija unutar same funkcije ne moˇe promijeniti stvarni arz s gument (malo u naˇem primjeru).’a’).. Na primjer. Na primjer. onda je mogu´e imati c viˇe return naredbi unutar iste funkcije.y. . Na primjer. u funkciji c c . a onda ´e izraˇunate vrijednosti biti proslijedene c c c funkciji. pri pozivu funkcije na mjestu stvarnih argumenata mogu se nalaziti izrazi umjesto varijabli. } Kada funkcija ne vra´a nikakvu vrijednost onda se za tip “vra´ene vric c jednosti” koristi kljuˇna rijeˇ void.... Analogno.. Na primjer. prethodna funkcija s mogla je biti napisana u obliku char malo_u_veliko(char z) { if(z >= ’a’ && z <= ’z’) return(’A’ + z . Pri tome ´e c izrazi prvo biti izraˇunati.7. c z Ukoliko se programski tok unutar funkcije grana. malo_u_veliko(malo)).

ime funkcije. koje su onda lokalne za c z taj blok (vidi sekciju 9. } void oznaˇava da nikakva vrijednost ne´e biti vra´ena u pozivni program. tip_n arg_n.1). U takvoj situaciji naredba return moˇe biti izostaz vljena. FUNKCIJE void maximum(int x. .. imamo mogu´nost definiranja lokalnih varijabli c na poˇetku svakog bloka unutar tijela funkcije. Zagrade su obavezne. c c c Takva se funkcija poziva s praznim zagradama kao ime_funkcije(). c c c Naredba return samo transferira programski tok u pozivni program i stoga uz nju ne stoji izraz. no radi preglednosti programa bolje ju je zadrˇati.122 POGLAVLJE 7.. . printf("\nMaksimalna vrijednost =%d". u tijelu if naredbe c i sliˇno. Napomena. Prema njemu deklaracije varijabli ne moraju prethoditi prvoj izvrˇnoj naredbi u bloku. z=(x>=y) ? x : y. tako da se izvrˇne naredbe i deklaracije s s mogu ispreplitati. ili najˇeˇ´e u izrazu pridruˇivanja c sc z varijabla=ime_funkcije(). ime neke funkcije. Budu´i da na s c poˇetku svakog bloka moˇemo deklarirati varijable. one ustvari informiraju prevodioc da je simbol koji stoji ispred zagrade. Des klaracije varijabli moraju prethoditi prvoj izvrˇnoj naredbi. z Funkcija koja ne uzima nikakve argumente definira se na sljede´i naˇin: c c tip_podatka ime_funkcije(void) { tijelo funkcije } Kljuˇna rijeˇ void unutar zagrada oznaˇava da funkcija ne uzima argumente. U tradicionalnom C-u definicija funkcije je imala oblik tip_podatka ime_funkcije(arg_1. { tijelo funkcije } .. Tijelo funkcije sastoji se od deklaracija varijabli i izvrˇnih naredbi. na primjer. . int y) { int z.1.arg_n) tip_1 arg_1.. return. c To je pravilo izmijenjeno u standardu C99.z).

U svakom sluˇaju takvu s c c z c z c sintaksu treba izbjegavati. Ako ˇelimo da su funkcije koje koristimo definirane nakon main() funkz cije ili op´enitije iza mjesta na kojem se pozivaju.tip_n arg_n).2 Deklaracija funkcije Svaka bi funkcija prije svoga poziva u programu trebala biti deklarirana. onda definicija sluˇi kao deklaracija te zasebna deklaracija z nije potrebna. Deklaracija funkcije ima oblik tip_podatka ime_funkcije(tip_1 arg_1. trebamo koristiti njihove c deklaracije. funkcija moˇe biti definirana z prije svog poziva: #include <stdio. U takvoj situaciji nikakve posebne deklaracije nisu potrebne. Uloga deklaracije (prototipa) je omogu´iti prevodiocu kontrolu ispravnosti poziva funkcije. c= (z >= ’a’ && z <= ’z’) ? (’A’ + z . Ukoliko funkcija f1() poziva funkciju f2() treba paziti da je f2() definirana prije f1(). prije svog prvog poziva. s ˇto znaˇi da ju u budu´nosti prevodioci moˇda ne´e podrˇavati. . Vidimo da je posve ista definiciji s tom razlikom da nema tijela funkcije i deklaracija zavrˇava toˇka-zarezom. DEKLARACIJA FUNKCIJE 123 Na primjer. c Ako je funkcija definirana u istoj datoteci u kojoj se poziva.2. broju i tipu argumenata te tipu povratne vrijednosti (tipu funkcije). } Takva definicija je dozvoljena i u ANSI C-u.h> . Deklaracija se treba pojaviti prije poziva s c funkcije ukoliko funkcija nije prije toga definirana. char malo_u_veliko(z) char z. Definicija i deklaracija moraju biti u skladu. Prvo. return c. { char c. ..7. 7. odnosno prototipove.’a’) : z.. Deklaracija informira prevodilac o imenu funkcije. Ilustrirajmo to na jednom primjeru. no standard C99 ju proglaˇava zastarjelom. Takva se situacija nalazi u jednostavnim programima koji su smjeˇteni u jednu datoteku i u kojima su sve funkcije definirane prije s main() funkcije.

h> int main(void) { int x. printf("\nMaksimalna vrijednost =%d\n". printf("Unesite dva cijela broja: "). odn. void maximum(int x. scanf("%d %d". maximum(x.y). int y) { int z.y. &x. maximum(x. } int main(void) { int x. scanf("%d %d". } U trenutku svog poziva prevodilac zna da je maximum funkcija koja uzima dva argumenta tipa int i ne vra´a niˇta. c s Ako ˇelimo definiciju funkcije smjestiti nakon funkcije main() uvodimo z deklaraciju. z=(x>=y) ? x : y. return. return.z).124 POGLAVLJE 7. printf("\nMaksimalna vrijednost =%d\n".&y).z).y). FUNKCIJE void maximum(int x. return 0. printf("Unesite dva cijela broja: "). } . } void maximum(int x. z=(x>=y) ? x : y.&y). prototiop funkcije: #include <stdio. &x. int y) { int z.y. return 0. int y).

DEKLARACIJA FUNKCIJE 125 Ovdje je funkcija maximum() deklarirana kao i druge varijable u glavnom programu.3). return.z).7. return 0. Formalno govore´i te dvije c c varijable nisu vidljive izvan prototipa funkcije i stoga nisu u koliziji s varijablama istog imena deklariranim u funkciji main() (vidi sekciju 9. Prevodiocu su c potrebni samo broj i tipovi varijabli koje funkcija uzima. Radi kompatibilnosti sa starijim programima dozvoljeno je koristiti i funkcije koje nisu prethodno deklarirane.y). Spomenimo joˇ da s ovakvo ispuˇtanje imena varijabli u prototipu ne treba raditi ako imena varis c z jabli podsjeˇaju na njihovu funkciju i time sluˇe dokumentiranju programa.2.1. c #include <stdio. int y) { int z. &x. maximum(x. int y). printf("Unesite dva cijela broja: "). int main(void) { int x. z=(x>=y) ? x : y. Prototipovi su uvedeni u C sa standardom C90 (ANSI standardom). koja se ˇeˇ´e koristi. printf("\nMaksimalna vrijednost =%d\n". U tom sluˇaju kaˇemo da je funkcija definirana implicitno pravilima prevodioca.h> void maximum(int. Druga mogu´nost.&y). imaju samo formalni znaˇaj. Pri tome vrijedi sljede´e pravilo: c • Prevodilac pretpostavlja da funkcija vra´a podatak tipa int i ne pravi c nikakve pretpostavke o broju i tipu argumenata. je deklarirati funkciju izvan main() c c sc funkcije kao u ovom sluˇaju. c z ˇ Cinjenica da prevodilac ne pravi nikakve pretpostavke o broju i tipu argu- . int).y. Primijetimo da varijable x i y deklarirane u deklaraciji void maximum(int x. scanf("%d %d". } Uoˇimo da smo ovdje u prototipu ispustili nazive varijabli. } void maximum(int x. i mogu se ispustiti.

126 POGLAVLJE 7. Takve deklaracije treba izbjegavati. Prilikom poziva funkcije stvarni argumenti se izraˇunavaju (ako su izrazi) c i kopiraju u formalne argumente. Mogu´nost pozivanja funkcija koje nisu prethodno deklac rirane (prototipom ili definicijom) te funkcija s nepotpunim deklaracijama dozvoljeno je jedino zbog kompatibilnosti sa starijim programima pisanim u tradicionalnom C-u. Izrazi koji se pri pozivu funkcije nalaze na mjestima formalnih argumenata nazivaju se stvarni argumenti.h> void f(int x) { x+=1.x).x). Funkcija prima kopije stvarnih argumenata ˇto znaˇi da ne moˇe izmijeniti stvarne argumente. Za c s korektan poziv potrebno je da se stvarni i formalni argumenti slaˇu u broju z i tipu (preciznije u sljede´oj sekciji). f(x). Sljede´i primjer ilustrira s c z c naˇin prijenosa argumenata. printf("\nUnutar funkcije x=%d". printf("\nNakon poziva funkcije x=%d". printf("\nIzvan funkcije x=%d". Takva deklaracija informira prevodilac o tipu funkcije (double). ali ne i o argumentima. return. c U starijim programima mogu´e je na´i deklaraciju funkcije tipa c c double f(). Napomena. U svim novim programima imperativ je koristiti prototipove. c #include <stdio. Spomenimo na ovom mjestu joˇ i to da u C++ gornja deklaracija znaˇi s c double f(void). } int main(void) { int x=5. Ukoliko nije to ´e biti otkriveno tek prilikom izvrˇavanja programa. . 7.3 Prijenos argumenata Argumenti deklarirani u definiciji funkcije nazivaju se formalni argumenti. Prevodilac ponovo ne radi nikakve pretpostavke o broju i tipu argumenta. U C++ sve funkcije moraju imati prototip.x). FUNKCIJE menata znaˇi samo da nije u stanju provjeriti je li poziv funkcije korektan ili c nije.

a svaki stvarni argument tipa float konvertira se u tip double.f(2)). } Rezultat izvrˇavanja programa ´e biti: s c Izvan funkcije x=5 Unutar funkcije x=6 Nakon poziva funkcije x=5 Istaknimo najvaˇnija pravila o prijenosu argumenata.f(x)). tada prevodioc postupa na sljede´i naˇin: Na svaki c c stvarni argument cjelobrojnog tipa primijenjuje se integralna promocija (konverzija argumenata tipa short i char u int). tada se stvarni argumenti ˇije se tip razlikuje od odgovaraju´ih formalnih argumenta c c konvertiraju u tip formalnih argumenata. z 127 • Broj stvarnih argumenata pri svakom pozivu funkcije mora biti jednak broju formalnih argumenata. printf("%d\n". printf("%d\n". Pogledajmo na primjer sljede´i program: c #include <stdio.3. z Takva konverzija pri tome mora biti mogu´a. return 0. } // Neispravno // Ispravno .7.h> int main(void) { float x=2. PRIJENOS ARGUMENATA return 0. • Redosljed izraˇunavanja stvarnih argumenat nije definiran i moˇe ovic z siti o implemetaciji.0. • Ako je funkcija ispravno deklarirana (ima prototip). Nakon toga broj i tip (konvertiranih) stvarnih argumenta mora se podudarati s brojem i tipom formalnih argumenata da bi poziv funkcije bio korektan. isto kao pri pridruˇivanju. c • Ukoliko je funkcija na mjestu svog poziva deklarirana implicitno pravilima prevodioca.

128 int f(double x) { return (int) x*x. S druge strane kˆd o int main(void) { float x=2. printf("%d\n". a funkcija ´e argument interpretirati kao rec alan broj dvostruke preciznosti. FUNKCIJE U prvom pozivu funkcije int f(double) glavni program ´e funkciji poslati c cjelobrojno 2 kao argument.f(x)). printf("%d\n". } double f(double x) { return x*x.0. To ´e dati ili pogreˇan rezultat ili prekid c s izvrˇavanja programa. } int f(double x) { return (int) x*x. return 0. int main(void) { float x=2. } /* greska */ /* ispravno */ . Uvedemo li funkcijski prototip (ˇto dobar stil programiranja nalaˇe) s z int f(double). onda niti jedan poziv ne bi bio korektan. printf("%d\n". printf("%d\n". } POGLAVLJE 7.f(x)). U drugom pozivu funkcije argument tipa float bit s ´e konvertiran u double i funkcija ´e primiti ispravan argument.f(2)). } funkcija ´e u oba sluˇja biti ispravno pozvana jer ´e cjelobrojni argument 2 c c c biti konvertiran u tip double. return 0.f(2)).0. Uoˇimo da c c c ako definiramo funkciju f() tako da uzima argument tipa float.

onda se ono ne prenosi funkciji “po vrijednosti”. 7. To znaˇi da se s c stvarni argumenti kopiraju u formalne argumente te funkcija stvarne argumente ne moˇe dohvatiti. Prevodilac nije duˇan ispuniti taj c z zahtijev na svakom mjestu.4. taj ´e uvjet biti c c naruˇen. spremiti sve podatke nuˇne za njegov nass z tavak nakon izlaska iz funkcije. INLINE FUNKCIJE 129 ne bi bio uspjeˇno preveden. ˇto c s nije dozvoljeno. a koc s piranje svih elemenata velikih polja bilo bi vrlo neefikasno (vidi sekciju 10. Stoga definic ciju double f(double x) prevodilac shva´a kao redefiniranje simbola f. } sam poziv funkcije moˇe uzeti viˇe procesorskog vremena nego izvrˇavanje kˆda funkcije i z s s o c c time znaˇajno usporiti program ako se funkcija ˇesto poziva. c s o Kod malih funkcija. izbjegavaju´i tako poziv funkcije. Da bi se to izbjeglo C99 dozvoljava da se funkcija deklarira inline: inline double f(double x) { return x*x. c . o funkciji koja vra´a vrijednost tipa int). onda ona mora dobiti njihove adrese (vidi sekciju 11. funkcija f je uvedena u glavnom pros gramu bez eksplicitne deklaracije pa prevodilac pretpostavlja da se radi o funkciji tipa int (tj. Naime. Procesor treba zaustaviti izvrˇavanje glavnog programa.4 Inline funkcije s Svaki poziv funkcije predstavlja odreden utroˇak procesorskog vremena.2). Ukoliko funkcija ima argument tipa polja koje se u njoj ne mijenja. kao ˇto je to na primjer s double f(double x) { return x*x. Prijenos argumenata funkciji u C-u vrˇi se po vrijednosti.5). To ne predstavlja problem kod statiˇih funkcija.7.c datoteku koja funkciju koristi. onda ga treba deklarirati s modifikatorom const (sekcija 11.5). ve´ funkcija dobiva pokazivaˇ na prvi element polja pomo´u kojeg c c c moˇe dohvatiti svaki element polja putem indeksa polja. Ako je polje argument funkcije. predati funkciji argumente i poˇeti izvrˇavati kˆd funkcije. Ukoliko se ˇeli da funkcija mijenja stvarne arguz z mente. Razlog za ovakav z naˇin prijenosa polja je u tome ˇto se dimenzija polja ne zna unaprijed. } Kljuˇna rijeˇ inline je sugestija prevodiocu da ekspandira tijelo funkcije na mjestu na c c kojem se ona poziva. no ako je funkcija definirana u nekoj drugoj datoteci. Osnovno ograniˇenje kod upotrebe inline funkcije je ˇto njena definicija (a ne samo c s deklaracija) mora biti vidljiva na mjestu poziva funkcije. Tada se postupa tako da se definicija funkcije stavi u datoteku zaglavlja koja se s potom ukljuˇi u svaku .

f (4) f (3) f (2) f (4) → 4f (3) = 24 f (3) → 3f (2) = 6 f (2) → 2f (1) = 2 f (1) → f (1) = 1 Uoˇimo da svaka rekurzivna funkcija mora imati odreden kriterij izlaska c iz rekurzije. Funkcija koja ju z je pozvala vrati vrijednost 2*1.5 Rekurzivne funkcije C dozvoljava da se funkcije koriste rekurzivno. faktorijele moˇemo raˇunati i nerekurzivnim postupkom kao npr. dok konaˇno prvo c c pozvana funkcija ne vrati vrijednost n!. for(. Nasuprot c c tome.130 POGLAVLJE 7. odnosno da pozivaju same sebe. c Rekurzivno koriˇtenje funkcija ˇesto rezultira vrlo elegantnim algorits c mima no ono ima i svojih nedostataka. } Funkcija (rekurzivno) poziva samu sebe n-1 puta kako bi izraˇunala n!. } . else return n*faktorijeli(n-1). sve dok ne dode do poziva funkcije faktorijeli s argumentom 1. Osnovni je taj ˇto je npr. strogo ve´im c c od 1. U toj funkciji dolazi do poziva funkcije faktorijeli s argumentom n-2 i tako dalje. return f. u z c funkciji long faktorijeli(long n) { long f=1. FUNKCIJE 7. u ovom s sluˇaju bilo potrebno n poziva funkcije da bi se izraˇunao broj n!. za raˇunanje n! = 1 · 2 · 3 · · · n moˇemo napisati rekurzivnu c z funkciju long faktorijeli(long n) { if(n<=1) return 1. Tada najdublje ugnjeˇdena funkcija faktorijeli vrati vrijednost 1. sljede´a vrati 3*2*1 itd. Na primjer.n>1.n--) f*=n. U ovom sluˇaju je to uvjet n<=1. c Uoˇimo da nakon poziva funkciji faktorijeli s argumentom n. dolazi do poziva funkcije faktorijeli s argumentom n-1.

Tada pozvana funkcija ispiˇe znak za prijelaz u novi red i izade. unos(). Dakle. Ako izvrˇimo program c s #include <stdio. prisutnost viˇe razliˇitih varijabli s s c imenom znak je dozvoljeno. } dobit ´emo ovaj rezultat: c Unesite niz znakova: Zdravo ovardZ Prvo je ispisan znak za prijelaz u novi red. Svaki poziv funkcije unos uˇita jedan znak sa standardnog ulaza i spremi c ga u lokalnu varijablu znak.7. } 131 Funkcija uˇitava znakove sa standardnog ulaza sve dok ne naide do prijelaza c u novu liniju i ispisuje uˇitane znakove. pri svakom novom pozivu funkcije unos bit ´e proˇitan i pohrac c njen jedan znak sve dok ne naidemo do znaka za prijelaz u novi red. Svaka varijabla definirana unutar funkcije vidljiva je samo u toj funkciji (vidi sekciju 9.1). if((znak=getchar())!=’\n’) unos(). Ona se kreira kod poziva funkcije i nestaje nakon izlaza iz funkcije. Stoga pri svakom novom pozivu funkcije unos kreira se nova varijabla s imenom znak. a zatim niz Zdravo u obrnutom redoslijedu. Funkcija koja ju je s pozvala ispisat ´e znak koji je proˇitala (znak ’o’) i predaje kontrolu funkciji c c . putchar(znak). s Pogledajmo joˇ jedan primjer rekurzivne funkcije: s void unos(void){ char znak. Budu´i da varijabla definirana u funkciji (unutarnja c varijabla) nije vidljiva izvan te funkcije. Ukoliko uˇitani znak nije znak prijelaza u novi c red ponovo se poziva funkcija unos.h> void unos(void).1. int main(void) { printf("\n Unesite niz znakova: ").5. return 0. REKURZIVNE FUNKCIJE ˇto je efikasnije jer trebamo samo jedan poziv funkcije.

Nakon njega pa ´e pokazivati na sljede´i nedeklarirani argument funkcije. mada bi jedna bila sasvim dovoljna.h> je definiran tip podatka va list koji predstavlja pokazivaˇ na listu nedeklariranih argumenata. Stoga na poˇetku funkcije treba c c definirati varijablu tipa va list. FUNKCIJE koja ju je pozvala. Kada se ˇeli proˇitati jedan nedeklarirani argument koristi se va arg.. va arg uzima kao prvi argument pokazivaˇ pa..6 Funkcije s varijabilnim brojem argumenata Funkcije scanf i printf primjeri su funkcija koje primaju varijabilan broj argumenata..int). Varijabla pa se inicijalizira pomo´u funkcije va start. ako je argument na koji pa pokazuje tipa int i vali varijabla tipa int. a float u double. c Kao rezultat varijabla pa ´e pokazivati na prvi nedeklarirani argument funkc cije. deklaracija funkcije s varijabilnim brojem argumenata ima oblik: c tip ime_funkcije(tip_1 arg1. z c Pri tome moramo znati tip argumenta. Ona uzima kao prvi c argument varijablu pa. 7. .132 POGLAVLJE 7. a kao drugi argument posljednji deklarirani argument funkcije.). Konaˇno. Pri svakom novom rekurzivnom pozivu funkcije kreiraju se ponovo sve lokalne varijable. Na primjer: va_list pa. onda bismo imali poziv oblika vali=va_arg(pa. a kao drugi tip argumenta te vra´a vrijednost argumenta i c c pove´ava pokazivaˇ pa tako da pokazuje na sljede´i nedeklarirani argument.h> sadrˇi neke definicije i z makro naredbe (vidi sekciju 8) koje omogu´avaju programeru pisanje valstitih c funkcija s varijabilnim brojem argumenata. Datoteka zaglavlja <stdarg. U <stdarg. c c Nedeklarirani argumenti funkcije pri pozivu funkcije podvrgnuti su standardnoj promociji koja vrijedi za funkcije bez prototipa: manji integralni tipovi konvertiraju se u int. Na taj se naˇin svi upisani znakovi ispisuju u obrnutom c redoslijedu. tip_2 arg_2. U ovom jednostavnom primjeru vidi se drugi nedostatak rekurzivnog programa. To znaˇi da funkcija mora imati barem jedan deklarirani argument. c c c Na primjer. U gornjem primjeru koristili smo onoliko varijabli koliko je bilo upisanih znakova.

) { va_list ap. s c . Pokaˇimo jedan jednostavan primjer.double). va_end(ap). ++i) total += va_arg(ap.0). .4.0.n)..).0. FUNKCIJE S VARIJABILNIM BROJEM ARGUMENATA 133 Ovdje je dat primjer s dva deklarirana argumenata.0.h> #include <stdarg. return 0.3. va_start(ap.0.3. printf("%g %g\n".2. mada broj deklariranih argumenata moˇe biti bili koji broj ve´i ili jednak 1.1.2.0.0. t=suma(5. Pri tome je n zadan kao prvi argument. #include <stdio. double total=0. int i. i<n.0). } double suma(int n. int main(void) { double s.h> double suma(int. s=suma(3.1.5.. } Uoˇimo da kroz deklarirane argumente moramo na neki naˇin funkciji dati c c informaciju sa koliko je argumenata pozvana i kojeg su tipa. Napisat ´emo funkciju z c koja sumira n brojeva tipa double koji su joj dani kao argumenti.0. Tri toˇke iza posz c c ljednjeg deklariranog argumenta oznaˇava da funkcija uzima varijabilan broj c argumenata..7..6. return total. for(i=0.t). Nakon ˇto su svi parametri proˇitani potrebno je pozvati funkciju va end s c koja zavrˇava proces ˇitanja argumenta..s.t. Funkcije printf i scanf to rade putem kontrolnog stringa.

no neki stariji prevodioci (makroprocesori) zahtijevaju da # bude prvi znak u liniji. Svaka preproz cesorska naredba zavrˇava krajem linije (a ne znakom toˇka-zarez). 1 134 . z o Znaku # mogu prethoditi bjeline. na mjestu #include naredbe. o Op´i oblik preprocesorskih naredbi je c #naredba parametri i one nisu sastavni dio jezika C te ne podlijeˇu sintaksi jezika. Neke od s c preprocesorskih naredbi su #include #define #undef #if #ifdef #ifndef #elif #else 8.1 Naredba #include #include "ime_datoteke" Naredba #include moˇe se pojaviti u dva oblika: z ili #include <ime_datoteke> U oba sluˇaja preprocesor ´e obrisati liniju s #include naredbom i ukljuˇiti c c c sadrˇaj datoteke ime datoteke u izvorni kˆd.Poglavlje 8 Preprocesorske naredbe Prije prevodenja izvornog koda u objektni ili izvrˇni izvrˇavaju se pres s procesorske naredbe. Njih izvrˇava zaseban dio pres vodioca koji se naziva preprocesor. Svaka linija izvornog koda koja zapoˇinje znakom # c predstavlja1 jednu preporocesorsku naredbu. i koji prije samog procesa prevodenja na osnovu preprocesorskih naredbi mijenja izvorni kˆd.

NULL itd.2 Naredba #define Njena je forma sljede´a: c #define ime tekst_zamjene Preprocesor ´e od mjesta na kome se #define naredba nalazi do kraja datoc teke svako pojavljivanje imena ime zamijeniti s tekstom tekst zamjene. c c Tako ´e na primjer dio izvornog kˆda c o #define PI 3. onda preprocesor datoteku traˇi u direktoriju u kojem se izvorni program nalazi. NAREDBA #DEFINE 135 Ako je ime datoteke navedeno unutar navodnika..14 . 8. prije prevodenja biti zamijenjen s .. • Deklaracije funkcija — U string..... • Deklaracije struktura — U stdio. tj. . time t. stdio.14. Pod unix-om to je najˇeˇ´e direktorij /usr/include c sc a postoji i mogu´nost da se prevodiocu da instrukcija (-I) da te datoteke c traˇi u unaprijed zadanom direktoriju...8.h je deklariran niz funkcija za rad sa stringovima.h se definira struktura FILE.. x=2*r*3.h). Ime datoteke z navedeno izmedu oˇtrih zagrada signalizira da se radi o sistemskoj datoteci s (kao npr. z Datoteke zaglavlja se najˇeˇ´e koriste za ukljuˇivanje sljede´ih veliˇina: c sc c c c • Simboliˇkih konstanti — U stdio.2. Do zamjene ne´e do´i unutar znakovnih nizova. unutar dvostrukih navodnika. pa ´e preprocesor datoteku traˇiti na mjestu odredenom c z operacijskim sustavom. x=2*r*PI.h tako imamo EOF. itd.. Datoteka ukljuˇena u izvorni kˆd pomo´u #include naredbe moˇe i sama c o c z z sadrˇavati #include naredbe. c • Makro funkcija — Na primjer getchar() koji je obiˇno definiran kao c getc(stdin) gdje je getc() makro funkcija. • Definicije tipova — Na primjer size t.

s Sliˇnost makroa i funkcije moˇe zavarati.4). do zamjene ne bi doˇlo. Vidimo da je parametrizirani makro vrlo sliˇan funkciji no u njemu nema c funkcijskog poziva i prenoˇenja argumenata. ona ´e biti zamijenjena s c x=((a1+a2)>(a1-a2) ? (a1+a2) : (a1-a2)). preprocesor ´e ju zamijeniti s c x=((a1)>(a2) ? (a1) : (a2)).3 Parametrizirana #define naredba Naredba #define osim za definiranje simboliˇkih konstanti sluˇi i za dec z finiranje parametriziranih makroa.a1-a2). Ako bismo makro max pozvali c z na sljede´i naˇin c c . Ako se u kodu pojavi naredba x=max(a1. definicija se moˇe c z poniˇtiti pomo´u naredbe #undef (vidi sekciju 8. Sintaksa je sljede´a: c #define ime(argumenti) text_zamjene Jedan primjer parametriziranog makroa je #define max(A. s c 8. pa je stoga efikasniji.136 POGLAVLJE 8. U parametriziranoj #define naredbi simboliˇko ime i tekst koji zamijec njuje simboliˇko ime sadrˇe argumente koji se prilikom poziva makroa zamic z jenjuju stvarnim argumentima. Ako pak na drugom mjestu imamo naredbu x=max(a1+a2. Naredba #define moˇe se koristiti i bez teksta zamjene kao z #define ime Nakon te naredbe ime je definirano.a2).B) ((A)>(B) ? (A) : (B)) gdje su A i B argumenti. Ako je neko ime definirano pomo´u #define. Svako ime definirano s u nekoj #define naredbi nazivamo makro. ˇto se moˇe ispitivati pomo´u #if nas z c redbe. Formalni argumenti (parametri) A i B zamijenjeni su sa stvarnim argumentima a1 i a2. PREPROCESORSKE NAREDBE no u naredbi printf("PI").

int main(void) { int y=5. Pretpostavimo da smo definirali #define max(A.h> #define PSQR(X) printf("Kvadrat od " #X " je %d.3.\n". ovisno o y? Unutar dvostrukih navodnika parametar makroa ne´e biti zamijenjen c stvarnim argumentom. s c s Zadatak.0. jer je X u znakovnom nizu tretiran kao obiˇan c s c znak.\n". PARAMETRIZIRANA #DEFINE NAREDBA max(i++.j++). To oˇito nije ono ˇto smo htjeli. a ne kao parametar makroa. PSQR(y).((X)*(X))).2). ako imamo definiciju #define PSQR(X) printf("Kvadrat od X je %d. return 0.((X)*(X))).1. j ne bi bile inkrementirane samo jednom (kao pri funkcijskom pozivu) ve´ bi ve´a varijabla bila inkrementirana dva puta.8.B) (A)>(B) ? (A) : (B) ˇ Sto bi bio rezultat poziva #define x=y+max(0. To se moˇe ispraviti pomo´u operatora #. 137 varijable i. } . ˇto oˇito nije ono ˇto smo htjeli. c c Argumente makroa treba stavljati u zagrade kako bi se izbjegla situacija ilustrirana u sljede´em primjeru: ako definiramo c #define kvadrat(x) x * x onda bi kvadrat(x+1) dao x+1 * x+1. Na primjer. ispisao Kvadrat od X je 25. PSQR(2+4). Korektana definicija bi bila: #include <stdio. z c To je operator koji makro parametar pretvara u string. onda bi poziv PSQR(5).

dim) for(int i=0. ++i) \ (polje)[i]=0. Ako ˇelimo da ime bude zamijenjeno s viˇe linija teksta moramo z s koristiti kosu crtu (\) na kraju svakog reda osim posljednjeg. na primjer getchar i putchar. i < (dim). z s c #undef getchar U #define naredbi tekst zamjene se prostire od imena koje definiramo do kraja linije.h> ustvari makroi. Osnovna prednost parametriziranih makroa pred obiˇnim funkcijama je c u brzini izvrˇavanja – makro nam ˇtedi jedan funkcijski poziv. Neke su “funkcije” deklarirane u <stdio. Naredba #if ima sljede´i c c c oblik: #if uvjet blok naredbi #endif Ukoliko je uvjet ispunjen blok naredbi izmedu #if uvjet i #endif bit ´e c ukljuˇen u izvorni kˆd.h> uglavnom su izvedene kao makroi. c o c c . #elif moˇemo uvjetno ukc z ljuˇivati ili iskljuˇivati pojedine dijelove programa. 8. Napomena. Isto tako. s Generalna je konvencija da se parametrizirani makroi piˇu velikim slos vima. To moˇe biti s s z znaˇajno ukoliko se makro poziva veliki broj puta. Prednost moˇe biti i to c z ˇto mako ne kontrolira tip svojih argumenata. ako uvjet nije ispunjen blok ne´e biti ukljuˇen. #else.4 Uvjetno ukljuˇivanje c Pomo´u preprocesorskih naredbi #if.0. makro za inicijalizaciju polja moˇemo definirati na sljede´i naˇin: z c c #define INIT(polje. funkcije u <ctype. Definiciju nekog imena moˇe se poniˇtiti pomo´u #undef.138 Program ´e ispisati c POGLAVLJE 8. PREPROCESORSKE NAREDBE Kvadrat od y je 25. S druge strane osnovni je z nedostatak ˇto prevodilac ne moˇe provjeriti korektnost poziva makroa za s z vrijeme prevodenja. Na primjer. Tako npr. makro kvadrat(x) s moˇemo koristiti s x bilo kojeg skalarnog tipa. U tom smislu je inline funkcija bolje rjeˇenje. Na primjer. Kvadrat od 2+4 je 36.

c Tu nam pomaˇe izraz defined(ime) koji daje 1 ako je ime definirano. Na primjer.h__ #define __datoteka. To je standardna tehnika kojom c se izbjegava viˇestruko ukljuˇivanje . koji ima znaˇenje z c c else if.h ´e biti ukljuˇena. kad je definirano. Na primjer.h #endif Zagrade oko varijabli nisu obavezne.h).h datoteka u program (provjerite npr. #if SYSTEM == SYSV #define DATOTEKA "sysv. U suprotnom ´e cijela c c c datoteka. c Sloˇene if naredbe grade se pomo´u #else i #elif. Najˇeˇ´e se ukljuˇivanje odnosno iskljuˇivanje dijela kˆda pomo´u nac sc c c o c redbe #if ˇini u ovisnosti o tome da li je neka varijabla definirana ili nije.h__ /* ovdje dolazi datoteka. #if !defined(__datoteka. #ifdef ime i #if ime su ekvivalentne naredbe ako je ime simboliˇko ime koje.ˇ 8.h #endif Ako varijabla datoteka. a svaka vrijednost razliˇita od nule kao istina. Budu´i da se konstrukcije #if defined i #if !defined ˇesto pojavljuju c c postoje kra´i izrazi s istim znaˇenjem: #ifdef i #ifndef.h biti jednostavno preskoˇena. Napomena.h__) #define __datoteka. c razliˇit od nule. s c datoteku stdio.4.h nije definirana ona ´e u sljede´oj #define nac c redbi biti definirana i datoteka.h__ /* ovdje dolazi datoteka. a 0 z ako nije. ono se zamijenjuje nulom. Nula se interpretira kao laˇ. Tako smo pretc c hodnu konstrukciju mogli napisati u obliku #ifndef __datoteka. Ukoliko se u uvjetu pojavi simboliˇko ime koje nije prije toga definirano c nekom #define naredbom. z c Simboliˇka imena se prije izraˇunavanja izraza zamijenjuju svojim vrijednosc c tima. UVJETNO UKLJUCIVANJE 139 Uvjet koji se pojavljuje u #if naredbi je konstantan cjelobrojni izraz. daje cjelobrojni konstantan izraz.h" #elif SYSTEM == BSD */ */ .

. Kompilacija s nac redbom cc -DDEBUG -o prog prog.c dat ´e izvrˇni kˆd koji ukljuˇuje printf naredbu. Kada je program konaˇno c zavrˇen i testiran kompilira se bez -DDEBUG opcije.x). PREPROCESORSKE NAREDBE #define DATOTEKA "bsd. Tehnika koja se koristi u razvoju programa je sljede´a: svi ispisi medurec zultata ubacuju se izmedu para #ifdef DEBUG i #endif naredbi i program se u razvojnoj fazi kompilira s -DDEBUG opcijom. jer je sada varijabla DEBUG c s o c definirana.. ... uˇitana vrijednost ´e biti ispisana.. koja se koristi u obliku c -Dsimbol. #ifdef DEBUG printf("Debug:: x=%d\n". scanf("%d". Tada ´e kompilacija naredbom c cc -o prog prog. o s Napomena.c proizvesti program u koji ispis varijable x nije ukljuˇen.140 POGLAVLJE 8. Pomo´u #if naredbe moˇemo iskljuˇiti dio koda iz programa c z c na sljede´i naˇin: c c . pretpostavimo da je program koji sadrˇi prikazani dio kˆda smjeˇten z o s u datoteku prog. c U razvoju programa korisno je ispisivati ˇto ve´i broj medurezultata kako s c bismo mogli kontrolirati korektnost izvrˇavanja programa. #endif Ukoliko je varijabla DEBUG definirana. Na primjer.h" #endif Ovdje se testira ime SYSTEM kako bi se ukljuˇila prava datoteka zaglavlja.h" #elif SYSTEM == MSDOS #define DATOTEKA "msdos. c c Prevodioci pod unix-om obiˇno imaju -D opciju. U tome nam s s pomaˇe uvjetno ukljuˇivanje kˆda kao ˇto se vidi na sljede´em primjeru: z c o s c int x. Nakon ˇto je pros s gram zavrˇen i testiran sav suviˇan ispis treba eliminirati. i koja dozvoljava da se simbol definira na komandnoj liniji.h" #else #define DATOTEKA "default. Na taj naˇin se iz izvrˇnog s c s kˆda izbacuju sve suviˇne printf (i ostale) naredbe.c.&x).

\n".__FILE__). z 141 8.\n". printf("Ime funkcije: return 0. } Ovaj kˆd na jednom sustavu ispisuje o Ime datoteke: makro_2. datoteka %s\n". printf("Vrijeme: %s. Ovi se makroi najˇeˇ´e koriste za ispis poruka o greˇkama kao u sljede´em c sc s c primjeru: if(n != m) printf("Greska: linija %d. PREDEFINIRANI MAKROI #if 0 dio programa koji iskljucujemo #endif Time se izbjegava problem ugnijeˇdenih komentara.h> int main(void) { printf("Ime datoteke: %s. Datum: Feb 12 2003.__DATE__).5 Predefinirani makroi C standard specificira nekoliko makroa koje moraju biti definirane.__LINE__). Neki od njih su Makro Znaˇenje c DATE Datum preprocesiranja TIME Vrijeme preprocesiranja FILE Ime datoteke s izvornim kˆdom o LINE Trenutna linija kˆda o func Ime funkcije. printf("Linija koda: %d. __FILE__).5.__func__). Zadnji makro je uveden standardom C99.8.c.__TIME__).\n". . Linija koda: 9. %s.\n". #include <stdio.__LINE__. Vrijeme: 19:28:06. Ime funkcije: main. Na primjer.\n". printf("Datum: %s.

U takvim situacijama s s ˇeljeli bismo provjeriti na poˇetku izvrˇavanja funkcije jesu li uvjeti koje arz c s gumenti moraju zadovoljavati ispunjeni. Predaja negativne vrijednosti vrlo ˇesti c c signalizira da se desila greˇka u izvrˇavanju programa. Na primjer.h>. no ANSI-C nam nudi bolju opciju. #include <stdio. Budu´i da takvih provjera moˇe c z biti jako puno u ve´im programima namjera nam je iskljuˇiti sve te provjere c c u konaˇnoj verziji kˆda.f(x)). Zatim se assert koristi kao da se radi o funkciji oblika void assert(int izraz) Ako je izraz jednak nuli u trenutku kada se izvrˇava naredba s assert(izraz). printf("x=%d\n".h> #include <math. line br_linije gdje je ime datoteke ime datoteke u kojoj se naredba nalazi i br linije broj linije u kojoj se naredba nalazi. Da bismo iskoristili makro assert trebamo ukljuˇiti standardnu datoteku c zaglavlja <assert. return sqrt(2*x-1).h> int f(int x) { assert(2*x-1 >= 0). file ime_datoteke. Nakon toga assert zaustavlja izvrˇavanje s programa. Tu nam ponovo moˇe pomo´i tehnika s #ifdef c o z c DEBUG.6 assert Mnoge funkcije oˇekuju da ´e dobiti argumente koji zadovoljavaju odredene c c uvjete. assert ´e ispisati poruku c Assertion failed: izraz. a to je makro assert. return 0.142 POGLAVLJE 8. } . Na primjer. PREPROCESORSKE NAREDBE 8.h> #include <assert. funkcija koja prima jedan argument tipa double moˇe z oˇekivati samo pozitivne vrijednosti. } int main(void) { int x=-1.

.. ASSERT U ovom sluˇaju program na jednom sustavu ispisuje sljede´e: c c Assertion failed: 2*x-1 > 0. line 6 ˇ Zelimo li iskljuˇiti assert naredbe iz programa dovoljno je prije ukljuˇivanja c c datoteke zaglavlja <assert.h> definirati NDEBUG kao u ovom primjeru: #include <stdio.h> #define NDEBUG #include <assert. 143 file C:\prjC\test\testA\a1..h> ....6.. Svaki assert makro ´e sada biti reduciran na nul-naredbu. c .8.cpp.

Na c primjer. pomo´u kljuˇnih c c rije´i jezika. Pojedini elementi deklaracije zadaju se eksplicitno. Dva su z osnovna tipa dosega: to moˇe biti blok ili datoteka. c c 9. onda su globalne s varijable. Programski jezik C dozvoljava da se u svakom bloku deklariraju varijable.1 Doseg varijable Doseg varijable je dio programa u kojem je njena deklaracija vidljiva i u kojem se stoga varijabli moˇe pristupiti putem njenog imena. Ako je izvorni kod razbijen u viˇe datoteka. tijelo funkcije je jedan blok naredbi.Poglavlje 9 Struktura programa U programskom jeziku C sve varijable moramo deklarirati prije njihove upotrebe.1 Lokalne varijable c Blok naredbi ˇini svaki niz naredbi omeden vitiˇastim zagradama. 9. doseg i vijek trajanja. c c Tip varijable uvijek se uvodi eksplicitno kljuˇnim rijeˇima int. Varijable s dosegom bloka z nazivamo lokalnim varijablama. dok varijable s dosegom datoteke nazivamo globalnim. a mogu se modificirati kljuˇnim rijeˇima static i extern. Deklaracija definira tri svojstva varijable: tip. Deklaracija lokalne varijable unutar 144 .1. ili implicitno. Isto vrijedi i c z za deklaracije funkcija. poloˇajem deklaracije u programu. deklarirane extern. Takve varijable nazivamo lokalnim. vidljive i izvan datoteke u kojoj su definirane. float. Doseg i trajanje varijable odredeni su poloˇajem deklaracije z u programu. double i drugim.

Takvu varijablu nazivamo globalnom. Na primjer. Nac kon izlaza iz funkcije. } nova varijabla i definirana je u bloku if naredbe koji se izvrˇava u sluˇaju s c istinitosti uvjeta n>0.9. i<n. izvan bloka moˇe biti deklarirana varijabla istog imena.. s Ukratko: Doseg varijable definirane unutar nekog bloka je taj blok. tj..y.... Stoviˇe.h> int x=3.1. Ta je s z varijabla tada unutar bloka nedostupna. 9. int x.. ++i) . Izvan bloka ne moˇe joj se pristupiti. Na primjer. deklarirana u funkciji. . dok double varijable x i y viˇe ne postoje. onda je njen doseg ˇitava datoteka u kojoj je c definirana.. Doseg formalnog argumenta je dakle isti kao i doseg varijable definirane na poˇetku funkcije. #include <stdio. . void f(double x) { double y. Varijabla definirana unutar nekog bloka vidljiva je samo unutar tog bloka. U primjeru if(n>0) { int i. Stoga u prethodnom primjeru formalni argument c x i double varijabla y. Njih unutar funkcije nije mogu´e dosegnuti. jer ju skriva varijabla deklarirana u bloku. Doseg formalnog argumenta je tijelo funkcije.2 Globalne varijable Varijabla deklarirana u vanjskom bloku vidljiva je u svakom unutarnjem bloku.. ako u njemu nije definirana varijabla istog imena. } Formalni argument funkcije vidljiv je unutar funkcije i nije dohvatljiv izvan nje. DOSEG VARIJABLE 145 nekog bloka mora prethoditi prvoj izvrˇnoj naredbi u tom bloku (prema stans dardu C90). cjelobrojne varijable x i y su ponovo vidljive i imaju nepromijenjene vrijednosti. Ako je varijabla definirana izvan svih blokova. deklarirane izvan funkcije. njeno ime izvan bloka nije definiz ˇ rano. ..1. skrivaju int varijable x i y.. /* deklaracija varijable */ for(i=0..

h> char string[64]. ispisi(). int main(void) { ucitaj().h> /* pogresno */ void ispisi(void) { int y=4. S druge strane kˆd o #include <stdio. Svaka funkcija moˇe dose´i globalnu varijablu z c i promijeniti njenu vrijednost. y=%d\n".x.h> #include <ctype. void ispisi(void). return 0. y=%d\n". STRUKTURA PROGRAMA void ispisi(void) { int y=4. .y). void malo_u_veliko(void). return 0. void ucitaj(void). U sljede´em primjeru tri funkcije rade c na istom polju znakova: #include <stdio. Globalne varijable se definiraju izvan svih funkcija i njihova je svrha prijenos podataka izmedu funkcija. } nije ispravan jer varijabla x nije definirana u bloku koji sadrˇi definiciju z funkcije ispisi(). Na taj naˇni viˇe funkcija moˇe komunicirati c s z bez upotrebe formalnih argumenta. printf("x=%d. } int main(){ int x=3.146 POGLAVLJE 9. } int main(void){ ispisi().x. } Varijabla x vidljiva je unutar funkcije ispisi(). malo_u_veliko().y). printf("x=%d.

...string). DOSEG VARIJABLE ispisi(). } void ucitaj() { fgets(string.. return 0. ali ne i u funkciji main() Funkcije su po svojoj prirodi globalni objekti. Naglasimo joˇ da je globalna varijabla vidljiva od mjesta svoje deklaracije s do kraja datoteke u kojoj se nalazi.h> int main(void) { . Funkcija definirana na bilo kojem mjestu moˇe se dohvatiti iz bilo kojeg dijela programa ako ima z prototip. } varijabla a je vidljiva i u funkciji main() i u funkciji f().sizeof(string).. void f(int).1. c int a. Definicija funkcije unutar druge funkcije nije dozvoljena.3. } void ispisi() { printf("%s\n". void f(int i) { . Kakav ´e biti izlaz iz sljede´eg programa? c c #include <stdio...i++) string[i]=toupper(string[i]). U primjeru.string[i] !=’\0’.stdin). dok je varijabla b vidljiva u funkciji f(). ovdje standardnog ulaza. } int b. Zadatak.9. } void malo_u_veliko() { int i. for(i=0. Stoga globalne varijable deklariramo na poˇetku datoteke prije svih funkcija. int main(void) { . } 147 Uoˇimo da sve funkcije rade s istom vanjskom varijablom string. (Funkc cija fgets() uˇitava string iz datoteke. vidi sekc ciju 13.2).

++x. ˇto oteˇava odrˇavanje programa. prevodilac s nam dopuˇta da ispustimo imena varijabli.148 int x=30.x). z c s z z Dobar stil programiranja stoga nalaˇe ˇto manju upotrebu globalnih varijabli. i nije definirano c c Varijabla i definirana je unutar for petlje i njen doseg je for petlja. ovisnost o c globalnoj varijabli moˇe biti slabo uoˇljiva. Jedina iznimka je deklaracija s polja varijabilne dimenzije (sekcija 10. i<10. printf("x u unutarnjem bloku = %d\n". Time se postiˇe ve´a ˇitljivost kˆda i olakˇava prevodiocu zadatak optimizacije. c s • Proˇiruje se pojam bloka kako bi obuhvatio petlje for. } return 0.i). Funkcija koja zavisi od neke globalne varijable i vrijednosti koju ´e u nju postaviti neka druga funkcija nije samostalna cjelina i ne moˇe c z se bez modifikacija koristiti u razliˇitim programima.1. z s 9. // Greska. while(x++ < 33) { int x =100. To znaˇi da imena navedena u prototipu nisu vaˇna i mogu c z ˇ se podudarati s imenima drugih varijabli u programu. i u sluˇaju kada se ne koriste vitiˇaste zagrade. Izmjene su sljede´e: c • Varijabla moˇe biti deklarirana bilo gdje unutar bloka.x). POGLAVLJE 9. c c Ideja prvog pravila je omogu´iti deklaraciju varijable ˇto bliˇe mjestu na kojem se c s z koristi. 9. while i do while.4 Lokalne varijable i standard C99 Standard C99 je uveo izmjene u varijable blokovnog dosega koje C ˇine kompatibilnijim c s C++om. a ne samo na njegovom z poˇetku.i). z c c o s Drugo pravilo omogu´ava definiciju varijable unutar for naredbe kao u sluˇaju c c for(int i=0. K tomu. ++i) printf("Prema standardu C99 i= %d\n". printf("i=%d\n". Stoviˇe. c . To znaˇi da ve´ u sljede´oj naredbi i nije definirano.7). STRUKTURA PROGRAMA printf("x u vanjskom bloku = %d\n". Deklaracije i izvrˇne naredbe mogu se sada slobodno ispreplitati. } Napomena.3 Argumenti funkcijskog prototipa Argumenti navedeni u funkcijskom prototipu imaju doseg koji ne seˇe daz lje od prototipa. te naredbu s if.1.

2.. z Osim konstantnim izrazom... ali se on primijenjuje s samo na labele u goto naredbi. funkcijski doseg. . je automatska varijable. } varijable x i y su automatske dok z nije.2. dakle ukupno vrijeme njene egzistencije.1 Automatske varijable Svaka varijabla kreirana unutar nekog bloka (dakle unutar neke funkcije). Na primjer.8.71.. . bez obzira na blok u kojem se pojavljuje.9. c c Automatske varijable se kreiraju na ulasku u blok u kome su deklarirane i uniˇtavaju na izlasku iz bloka. s c z c 9. Tako ´e varijabla y biti ponovo kreirana i inicijalizirana c pri svakom novom pozivu funkcije f(). Memorija koju je automatska varijabla zauzis mala oslobada se za druge varijable.. koja nije deklarirana s kljuˇnom rijeˇi static. Najˇeˇ´e je to c sc vrijednost koja se zatekla na pridruˇenoj memorijskoj lokaciji. Inicijalizacija se vrˇi pri svakom novom ulazu u blok u kome je s varijabla definirana. Prema vijeku trajanja varijable c dijelimo na automatske i statiˇke.. pomo´i goto naredbe ne moˇe se iza´i iz funkcije. 9. Vijek z trajanja varijable je vrijeme za koje joj je pridruˇena njena memorijska lokaz cija.. VIJEK TRAJANJA VARIJABLE 149 9. Automatska varijabla koja nije inicijalizirana na neki naˇin. inicijalizaciju automatsje varijable mogu´e je c s c izvrˇiti i izrazom koji nije konstantan kao u ovom sluˇaju: .. void f(double x) { double y=2. Automatske varijable mogu se inicijalizirati.2 Vijek trajanja varijable Svakoj varijabli prevodilac pridruˇuje odredeni memorijski prostor. Kao ˇto s je reˇeno u sekciji 6. na ulasku c u blok u kome je definirana dobiva nepredvidivu vrijednost. Funkcijski doseg znaˇi da je goto labela c vidljiva u cijeloj funkciji. static double z.5 Funkcijski doseg Napomenimo joˇ da postoji i tzv. jer je deklarirana s kljuˇnom rijeˇi c c static.1. kao ˇto je to sluˇaj s varis c jablom y.

static char polje[10].. tako da je op´i oblik deklaracije c varijable: identifikator_mem_klase tip_varijable ime_varijable. . register int z.. extern.2. c 9. Postavljaju se u dekelaraciji vaz rijable prije identifikatora tipa varijable. .150 POGLAVLJE 9.2. auto int *pi. extern double l.. Naime. 9. Oni sluˇe preciziranju vijeka trajanja varijable. su automatske varijable. sve varijable definirane unutar nekog bloka.3 auto Identifikator memorijske klase auto deklarira automatsku varijablu. STRUKTURA PROGRAMA void f(double x. int n) { double y=n*x..4 register Identifikator memorijske klase register moˇe se primijeniti samo na z automatske varijable i formalne argumente funkcije.2. a bez kljuˇne rijeˇi static. } Inicijalizacija varijable y ovdje je samo pokrata za eksplicitno pridruˇivanje z y=n*x. 9. Na primjer f(register int m. Na primjer.2 Identifikatori memorijske klase Identifikatori memorijske klase su auto. static i register. register long n) { register int i. Sve c c varijable definirane izvan svih blokova su statiˇke varijable. Vrlo se rijetko susre´e u programima jer za njegovu upotrebu nema drugog razc loga osim iskazivanja namjere programera.

. s Na varijablu tipa register ne moˇe se primijeniti adresni operator.2. K tome z vrijedi i sljede´e pravilo: Statiˇka varijabla definirana unutar nekog bloka inic c cijalizira se samo jednom i to pri prvom ulazu u blok. Vijek trajanja statiˇke s s c varijable je cijelo vrijeme izvrˇavanja programa.. Ona postoji za cijelo vrijeme izvrˇavanja s programa ali se moˇe dohvatiti samo iz bloka u kojem je definirana.. } // neispravno 9. Prevodilac nije duˇan c z poˇtovati deklaraciju register.. (i = 3. . Statiˇke je varijable mogu´e inicijalizirati samo konsc c tantnim izrazima tako da sljede´i kˆd nije ispravan: c o int f(int j) { static int i=j. VIJEK TRAJANJA VARIJABLE ... tako da je ona samo sugestija prevodiocu. s Svaka varijabla definirana izvan svih funkcija je statiˇka.. To su brojevi definirani rekurzijom Fi = Fi−1 + Fi−2 . c Ukoliko statiˇka varijabla nije inicijalizirana eksplicitno prevodilac ´e je c c inicijalizirati nulom. Pogledajmo kako se to svojstvo koristi u jednom primjeru. To najˇeˇ´e c sc znaˇi smjestiti varijablu u registar mikroprocesora.6 Statiˇke lokalne varijable c Statiˇka lokalna varijabla je lokalna varijabla deklarirana s identifikatoc rom memorijske klase static. . .2.) F1 = F2 = 1. 4...9..2.. a uniˇtavaju se tek na zavrˇetku programa.. Varijabla dekc larirana u nekom bloku (npr... z 9. funkciji) s identifikatorom memorijske klase static je takoder statiˇka varijabla. Prostor za statiˇke varis c jable alocira se u dijelu memorije razliˇitom od dijela u kojem se alociraju c automatske varijable (ˇto je standardno programski stog). } 151 Kljuˇna rijeˇ register sugerira prevodiocu da ´e varijabla biti ˇesto koriˇtena c c c c s i da bi trebala biti alocirana tako da se smanji vrijeme pristupa. . .5 Statiˇke varijable c Statiˇke varijable alociraju se i inicijaliziraju na poˇetku izvrˇavanja proc c s grama. ˇ Zelimo napisati program koji ispisuje prvih 20 Fibonaccijevih brojeva.

STRUKTURA PROGRAMA Glavni program imat ´e sljede´i oblik: c c #include <stdio. 9. Kljuˇna rijeˇ static ispred varijable definirane izvan svih c c blokova ne oznaˇava statiˇku varijablu ve´ reducira njen doseg (vidi sljede´u c c c c sekciju). Tu ´e nam pomo´i statiˇke varijable: c c c c long fibonacci(int i) { static long f1=1. f=(i<3) ? 1 : f1+f2.i.i++) printf("\n i= %d. f2=1.h> long fibonacci(int). Proces prevodenja izvornog kˆda smjeˇtenog u viˇe datoteka ima dvije o s s faze. svaka funkcija z s s definirana u programu moˇe biti smjeˇtena u zasebnu . Takvo z s razbijanje izvornog kˆda ve´ih programa olakˇava njihovo odrˇavanje i nao c s z dogradnju. return f.3 Vanjski simboli C program moˇe biti smjeˇten u viˇe datoteka. } Dakle. f2=f1. Na primjer.152 POGLAVLJE 9.i<=20. treba nam funkcija koja ´e za svaki i izraˇunati Fi uz uvjet da se c c brojevi raˇunaju redom od F1 do F20 . f1=f. long f.o).c datoteku. int main(void) { int i. Izmedu svaka dva poziva funkciji fibonacci one zadrˇavaju svoju vrijednost i stoga pri i-tom pozivu funkcije imamo z f1= Fi−1 i f2= Fi−2 . Upozorenje. for(i=1. Pri tome je nuˇno da se funkcije i globalne varijable definirane u z jednoj datoteci mogu koristiti i u svim ostalima. } Statiˇke varijable f1 i f2 bit ´e inicijalizirane jedinicama samo pri prvom c c pozivu funkcije fibonacci. U prvoj prevodilac prevodi izvorni kˆd svake pojedine datoteke u objeko tni kˆd (datoteku s ekstenzijom .fibonacci(i)). return 0. F= %ld". u drugoj linker povezuje viˇe datoteka o s .

To se ˇini pomo´u kljuˇne rijeˇi static. Zadatak je linkera da pronade one s simbole (imena funkcija i globalnih varijabli) koji se u pojedinoj objektnoj datoteci koriste.9. pretpostavimo da u prvoj datoteci imamo kˆd: o #include <stdio.3).3. Na primjer. Analogno je s vanjskim varijablama.3.h> int g(int). odnosno navesti njen prototip: extern void f(int). tj. funkcija dec finirana u datoteci moˇe se pozvati bilo gdje u toj datoteci ako je na mjestu z ˇ poziva vidljiva njena deklaracija. c c c c 9. Stoviˇe. Svaki takav simbol mora imati jednu i samo jednu definiciju u nekoj od datoteka. s Linker ´e povezati simbol naveden u prototipu s definicijom funkcije. U svakoj takvoj datoteci navodi se samo prototip funkcije. Pri c tome svi prototipovi (deklaracije) moraju odgovarati definiciji. s kojom linker onda povezuje simbol.g(i)). sva su ta imena vanjski simboli. kako bi se smanjio broj vanjskih simbola (vidi sekciju 9. ime funkcije je automatski vidljivo s linkeru. void f(int i) { printf("i=%d\n". ˇto znaˇi da se funkcija moˇe pozivati i u drugim datotekama. } /****** Datoteka 2 *******/ /****** Datoteka 1 ******/ . } int g(int i) { return 2*i-1. ali nisu u njoj definirani. a pozivati se z u viˇe drugih.1 Funkcije Vidjeli smo ve´ da funkcije uvijek imaju doseg datoteke. VANJSKI SIMBOLI 153 s objektnim kodom u izvrˇni program. C nam dozvoljava da neka imena ne eksportiramo linkeru. funkcija moˇe biti definirana u jednoj datoteci. Tada je u toj datoteci funkciju f() potrebno deklarirati. koja poziva funkciju f(). Osnovno je pravilo da su imena svih vanjskih varijabli i funkcija dostupna linkeru. nalazi u drugoj datoteci. int main(void) { f(3). } i da se funkcija main(). i definicija treba biti samo jedna. s c z Na primjer.3.

/* Neispravno */ } jer linker ne bi pronaˇao funkciju g. } Sada funkciju g viˇe ne moˇemo dohvatiti iz druge datoteke. Kljuˇna rijeˇ extern kod funkcija ima stoga samo ulogu dokumentiranja c c programa.g(2)). void f(int i) { printf("i=%d\n". z To znaˇi da je deklaracija c extern void f(int). c Funkcija moˇe biti deklarirana i s identifikatorm memorijske klase static. extern moˇemo ispustiti. modificirali z c s bismo prvu datoteku na sljede´i naˇin: c c #include <stdio.154 POGLAVLJE 9. printf("g(2)=%d\n". Stavit ´emo ju uvijek u prototipu funkcije koja je definirana u c nekoj drugoj datoteci. Na primjer. Budu´i da c su sva imena funkcija automatski poznata linkeru. c Takvu funkciju nije mogu´e pozivati iz druge datoteke. } static int g(int i) { return 2*i-1. tj. s Funkcije koje ne koristimo izvan datoteke u kojoj su definirane treba u principu deklarirati kao statiˇke kako njihova imena ne bi bila poznata u c ˇitavom programu. c . STRUKTURA PROGRAMA Kljuˇna rijeˇ extern je identifikator memorijske klase koji oznaˇava da dekc c c larirano ime (ovdje f) vanjski simbol. ako bic smo ˇeljeli onemoguˇiti koriˇtenje funkcije g izvan prve datoteke. ekvivalentan s void f(int). kako bi oznaˇili da se radi o vanjskoj funkciji. /****** Datoteka 2 *******/ /* pogresno */ int main(void) { /* Ispravno */ f(3). extern int g(int).h> /****** Datoteka 1 *******/ static int g(int).g(i)). da je poznato linkeru. z Efekt takve deklaracije je da ime funkcije ne´e biti eksportirano linkeru. pa je sljede´i s z c program neispravan: extern void f(int).

. Ako na primjer. 9.. s .9. koja je definirana u standardz noj biblioteci.h> koja sadrˇi njen c z prototip. dok se kod deklaracije samo uvodi ime varijable. VANJSKI SIMBOLI 155 Uobiˇajena je praksa deklaracije svih funkcija koje su vanjski simboli c staviti u datoteku zaglavlja koja se onda ukljuˇuje u svaku datoteku koja te c funkcije ˇeli koristiti. onda moramo ukljuˇiti datoteku <stdio.3.. dok deklaracija (prototip) nema. je automatski vanjski simbol. Kod varijabli razlika nije tako oˇita. Na taj se z c naˇin izbjegava mogu´e nepodudaranje prototipova i definicija.2 Globalne varijable Svaka globalna varijabla.. Ukljuˇivanje c c c zaglavlja u datoteku u kojoj su funkcije definirane omogu´ava prevodiocu da c provjeri jesu li prototipovi u skladu s definicijama. Kada se jedna globalna varijabla koristi u viˇe datoteka. c Prevodilac mora jasno razlikovati definiciju od deklaracije varijable. Uoˇimo da je kljuˇna rijeˇ static ima drugaˇije znaˇenje kad se primjeni c c c c c na lokalnu varijablu. Stoga je jasno da mora postojati toˇno jedna definicija. Kod funkcija je to pravilo lako primijeniti jer se definicija i deklaracija funkcije jasno razlikuju. ˇak i u onu u kojoj su funkcije definirane. onda nije jasno koja je od viˇe deklaracija iste s varijable u viˇe datoteka njena definicija. kao i kod funkcije. ako z inicijalizacija nije prisutna.. Kod definicije se za varijablu rezervira memorijski prostor. Svaka deklaracija c varijable u kojoj se varijabla i inicijalizira nuˇno je njena definicija. To je postupak koji se koristi s funkcijama iz standardne biblioteke. ˇelimo koristiti funkciju printf(). Kod lokalne varijable static mijenja vijek trajanja varijable. No. njeno ime je vidljivo linkeru.. a kod globalne reducira doseg. static int d. Takvo ponaˇanje moˇemo promijes z niti pomo´u identifikatora memorijske klase static. a deklarirana u svim ostalima.3. } Globalna varijabla d vidljiva je samo u svojoj datoteci (nije vanjska varijabla). U c primjeru. jednako kao i svaka funkcija. Definicija funkcije ima tijelo funkcije. odnosno na globalnu varijablu. onda ona mora s biti definirana u jednoj od njih.. int main(void) { . Posve isto pravilo vrijedi i za funkcije. i smatra se da definicija dana negdje drugdje.

definiranoj c c u nekoj drugoj datoteci.0.. Na primjer.) i prvih 31 znakova vanjskih identifikatora. Kljuˇna rijeˇ extern ovdje indicira da se radi o vanjskoj varijabli. Njena uloga ovdje nije iskljuˇivo dokumentiranje c koda. STRUKTURA PROGRAMA Na ˇalost. c • Definicija varijable ima eksplicitnu inicijalizaciju i identifikator memorijske klase extern nije prisutan. Standard C90 propisuje 31 znak lokalnog identifikatora i samo 6 znakova vanjskog identifikatora.. varijable.3 Vanjska imena Standard C99 propisuje da prevodilac treba prepoznati prva 63 znaka svakog lokalnog identifikatora (imena funkcije.3. razliˇiti prevodioci koriste razliˇite naˇine za razlikovanje defiz c c c nicije i deklaracije. extern int z[]. int z[3]={0.. Da bi se izbjegli problemi treba usvojiti sljede´i model. double a =0. Imena vanjskih varijabli treba stoga drˇati dovoljno kratkim.0}. • Sve deklaracije sadrˇe identifikator memorijske klase extern i ne sadrˇe z z inicijalizaciju: extern double a.0. z .156 POGLAVLJE 9. 9.

druga je x[1] i tre´a je x[2]. a izraz mora biti konstantan cjelobrojni pozitivni izraz. u C-u su polja uvijek indeksirana c poˇevˇi od nule.Poglavlje 10 Polja Polje je niz varijabli istog tipa koje su numerirane i mogu se dohvatiti pomo´u cjelobrojnog indeksa. gdje je mem klasa memorijska klasa. ime je ime polja.1 Definicija i inicijalizacija polja Polje se definira jednako kao i skalarna varijabla s tom razlikom da dimenzija polja (broj elemenata) mora biti zadana. je deklaracija polja od tri varijable tipa double koje ˇine polje x. Dimenzija se zadaje kao pozitivni cjelobrojni izraz u uglatim zagradama. Unutar funkcije polje deklarirano bez memorijske klase je automatska varijabla. 10. Prva varic jabla je x[0]. Na primjer. tip je tip podatka. Stoga je polje element jezika C pomo´u kojeg se realiziraju c vektori i matrice. Polje definirano naredbom 157 . Jednodimenzionalno polje definira se na sljede´i naˇin: c c mem_klasa tip ime[izraz]. c s Radi efikasnosti pristupa elementi polja smjeˇtaju se na uzastopne mes morijske lokacije. a izvan svih funkcija je statiˇka varijabla. Unutar funkcije polje se moˇe uˇiniti statiˇkim pomo´u c z c c c identifikatora memorijske klase static. izraz u definiciji polja je najˇeˇ´e pozitivna konstanta ili simboliˇka konsc sc c tanta. c double x[3]. Deklaracija memorijske klase nije obavezna.

17. 6. for(i=0.3. b=sqrt(b)/dim. int main(void) { int i. Na primjer. onda ´e preostale vrijednosti biti inicijalizirane nulom.v_n}. Sintaksa je sljede´a: c c mem_klasa tip ime[izraz]={v_1.6..b=0.dim=sizeof(x)/sizeof(float).6. Stoga moˇemo pisati c z float v[]={1.i<dim.++i) a+=x[i]. z float v[3]={1.0.i<dim. drugi indeks 1 itd. gdje je v 1 vrijednost koja ´e biti pridruˇena prvom elemetu polja (ime[0]).v_2. printf("Srednja vrijednost = %f. a. } . double a=0. POLJA je polje od tri elementa v[0].b). daje pridruˇivanje v[0]=1. s c Sljede´i program za dano polje x[dim] raˇuna c c a= 1 dim dim−1 xi ..7.17. a/=dim. i=0 b= 1 dim dim−1 i=0 (xi − a)2 .43..11}. v[1]. #include <stdio.h> #include <math..158 float v[3].0.17. Prilikom inicijaz lizacije polja dimenzija ne mora biti specificirana ve´ ´e biti automatski c c izraˇunata. Ako je manji. c Ako je broj inicijalizacijskih vrijednosti ve´i od dimenzije polja javlja se c greˇka. for(i=0. Polja se mogu inicijalizirati navodenjem vrijednosti elemenata unutar vitiˇastih zagrada. 7.43.h> float x[]={1.11}.4. v[2].++i) b+=(x[i]-a)*(x[i]-a). Uoˇimo da prvi element uvijek c ima indeks 0.6. v[1]=2. 5. POGLAVLJE 10.43.8}. 2.2. c z v 2 vrijednost pridruˇena drugom (ime[1]) itd.11. Polje x je definirano kao globalna varijabla. v[2]=6. return 0. odstupanje = %f\n".2. i prevodilac ´e kreirati polje dimenzije 3.

. Nazivi c drugih float funkcija dobivaju se na isti naˇin.10. 6.6f. c Napomena. c Verzija funkcije sqrt koja uzima float i vra´a float naziva se sqrtf.. Sumiranje u varijabli ˇireg tipa z s s korisno je ako se sumira veliki broj ˇlanova jer se na taj naˇin smanjuju c c greˇke zaokruˇivanja.7). kao i kod obiˇne inicijalizacije. U raˇunu srednjeg odstupanja koristimo funkciju sqrt (drugi korijen) iz c standardne biblioteke..8f}. Zbog gornjeg pravila. Ako je varijabla polje.3f}. onda sizeof daje broj okteta potreban da se zapamti cijelo polje.. 5.7f..3.4f. Neinicijalizirani elementi polja dobit ´e vrijednost c c nula.. budu´i da se on automatski konvertira u pokazivaˇ.3f. s c Da bi smo izbjegli upozorenje mogli smo koristiti konstante tipa float: float x[]={1. 7. to je nemogu´e. s z Obrnuta konverzija. Stoga u naredbi a+=x[i] i b+=(x[i]-a)*(x[i]-a) imamo konverziju iz uˇeg tipa u ˇiri tip podatka. Kao i sve matematiˇke funkcije koje rade s realnim c brojevima i sqrt uzima argument tipa double i vra´a vrijednost tipa double. ovakav naˇin raˇunanja broja elemenata polja nije mogu´e promijeniti na z c c c argument funkcije tipa polja.. Naˇalost. Ovdje se tre´i element inicijalizira s 1. c c Polje x je tipa float dok su varijable a i b u kojima se vrˇi sumacija s tipa double. Sintaksa je kao u sljede´em c c primjeru float x[5]={[3]=1. Zbog toga C ne dozvoljava deklaraciju polja unutar funkcije na sljede´i c naˇin: c void f(int n) { double A[n][n]. na ˇto ´e nas prevoditelj eventualno upozoriti. DEFINICIJA I INICIJALIZACIJA POLJA Dimenziju polja dobivamo naredbom dim=sizeof(x)/sizeof(float). } // pogresno U funkciji f() smo pokuˇali deklarirati matricu ˇiji je red zadan argumentom s c n. pa smo stoga broj elemenata polja dobili dijeljenjem s brojem okteta koji zauzima jedna varijabla tipa float. c Standard C99 omogu´ava parcijanu inicijalizaciju polja.. 159 Operator sizeof primijenjen na varijablu daje broj okteta potreban da za memoriranje varijable. 2. To ograniˇenje ANSI C-a (C90) ukc c lonio je standard C99 uvodenjem polja varijabilne duljine (vidi sekciju 10. iz ˇireg u uˇi tip se deˇava pri inicijalizaciji polja x jer s z s su sve konstante tipa double.1. . U definiciji polja dimenzije polja moraju biti konstantni izrazi.

svako od po 128 znakova. funkcija strcpy kopira string ime u string ime i prezime. c Sljede´i program uˇitava ime i prezime i ispisuje ih u jednom retku: c c #include <stdio. Funkcija vra´a pokazivaˇ na prvi string.h> char ime [128].h>. c[2]=’i’. POLJA 10. U naredbi strcpy(ime_i_prezime. int main(void) { printf("Unesite ime:"). Nije dozvoc z c ljeno pisati c="tri". Na primjer. Ona automatski zamijenju znak za c prijelaz u novi red s nul znakom.prezime). Nizove znakova unosimo pomo´u gets naredbe. printf("Unesite prezime:").160 POGLAVLJE 10.h> #include <string. gets(prezime). char ime_i_prezime[128]. definirano je polje od 4 znaka: c[0]=’t’. no s c c u programu tu vrijednost zanemarujemo. deklaracijom char c[]="tri".2 Polja znakova Polja znakova mogu se inicijalizirati stringovima. } Definirali smo tri globalna polja znakova. c[3]=’\0’. strcat(ime_i_prezime. return 0. Pri tome se kopira i nul znak kojim string zavrˇava." ").ime). char prezime [128]. ime_i_prezime). gets(ime). U naredbi . strcat(ime_i_prezime. /* pogresno */ ve´ trebamo koristiti funkciju strcpy deklariranu u <string. printf("Ime i prezime: %s\n". c[1]=’r’. strcpy(ime_i_prezime. Takav naˇin pridruˇivanja mogu´ je samo pri definiciji varijable.ime).

strstr(). strcat(). Funkcija strlen vra´a vrijednost za jedan manju od operatora c sizeof. Cjelobrojni tip size t je definiran u <stdio. 161 koristimo funkciju strcat koja povezuje dva stringa. Dobiveni niz znakova opet zavrˇava nul znakom. Funkcija strcat povezuje dva stringa u jedan. const char *s2). Ta je definicija ovisna o raˇunalu pa je stoga stavljena u standardnu datoteku c zaglavlja. c 10. .h> npr. Najˇeˇ´e upotrebljavane su c sc strlen(). Prvi znak u polju znakova na koje pokazuje c s2 bit ´e prepisan preko null-znaka kojim zavrˇava s1. strncat(). bez zadnjeg null znaka. tj. strncpy(). Njen prototip je char *strcat(char *s1. strcmp(). U ovom sluˇaju rec zultat je novi string koji sadrˇi vrijednost varijable ime i jedan razmak iza z zadnjeg slova. broj znakova c c u stringu. const char *s2. Funkciju strlen() smo ve´ sreli. Ponovnom c c upotrebom funkcije strcat dobivamo konaˇan string koji zatim ispisujemo. strcpy().3 Funkcije za rad sa stringovima Datoteka zaglavlja <string. Ona nam omogu´ava pisanje c c pouzdanijih programa. FUNKCIJE ZA RAD SA STRINGOVIMA strcat(ime_i_prezime. String s2 se nadovezuje (eng. concatenation) na string s1 koji se pri tome pove´ava dok se s2 ne mijenja. Ova funkcija ne vodi c s raˇuna o tome da li u polju na koje pokazuje s1 ima dovoljno mjesta za c oba stringa. Ukoliko nema do´i ´e do pisanja preko susjednih memorijskih c c lokacija. strchr(). Funkcija strncat uzima tre´i argument koji ima znaˇenje maksimalnog c c broja znakova koji ´e biti dodani stringu s1. kao typedef unsigned long size_t.h> deklarira niz funkcija za rad sa stringovima.3." "). Ona vra´a duljinu stringa. size_t n). Prototip funkcije je char *strncat(char *s1.10. Prototip funkcije je size_t strlen(const char *s). strncmp(). I ova funkcija s vra´a pokazivaˇ na prvi string no u programu ga zanemarujemo.

onda strncat dodaje n znakva iz s2 na kraj niza s1 te dodaje nul-znak. ukupno se dodaje n+1 znakova. } Pri spajanju nizova ime i prezime i prezime moramo imati strlen(ime_i_prezime) + strlen(prezime) + 1 <= SIZE. if(strlen(ime_i_prezime) < SIZE-2) strcat(ime_i_prezime. const char *s2).prezime. Ako cijeli ime i c prezime ne stane u polje ime i prezime program ´e naprosto staviti ono ˇto c s stane (kvalitetnije rjeˇenje bi bilo pomo´u dinamiˇki alocirane memorije).n). Sljede´i program c c ilustrira leksikografski poredak stringova. POLJA Ako u prvih n znakova na koje pokazuje s2 nema nul-znaka. Za usporedivanje stringova imamo dvije funkcije int strcmp(const char *s1. puts("Vase ime: ").162 POGLAVLJE 10. a u suprotnom pozitivnu. . Ukoliko su dva stringa jednaka funks cija vra´a nulu. int main(void){ int n. strncat(ime_i_prezime.h> #define SIZE 30 char ime_i_prezime[SIZE]. char prezime[SIZE]. puts(ime_i_prezime). Ako prvi string leksikografski prethodi drugom funkcija c ´e vratiti negativnu vrijednost. s c c #include <stdio. gets(prezime). const char *s2. int strncmp(const char *s1. gets(ime_i_prezime). size_t n). return 0." "). Sljede´i program ilustrira upotrebu funkcije strncat. n=SIZE-strlen(ime_i_prezime)-1. puts("Vase prezime: "). Usporedivanje se vrˇi znak po znak. dakle.h> #include <string.

printf("%d\n". vra´a pointer na prvo pojavljivanje znaka c u stringu na koji pokazuje s (nulc znak se takoder moˇe traˇiti). printf("%d\n". int c)."aaab")). c c na primjer. . Obje funkcije kopiraju niz znakova na koji pokazuje s2 na lokaciju na koju pokazuje s1. dovoljno je limitirati c c usporedbu na prvih pet znakova. size_t n). 32 i 1. FUNKCIJE ZA RAD SA STRINGOVIMA #include <stdio. strcmp("A"."A")). Nova funkcija omogu´ava ve´u efikasnost usporedivanja jer ako. printf("%d\n". Druga verzija funkcije kopira najviˇe n znakova. Funkcija ´e redom usporedivati znakove u dva stringe c sve dok ne usporedi n parova ili ne naide do kraja jednog od stringova. provjeravamo da li rijeˇ poˇine s "astro".3. printf("%d\n".h> #include <string. const char *s2). strcmp("a". Ako znak c nije prisutan u s bit ´e vra´en z z c c nul-pointer."A")). Funkcija char *strstr(const char *s1. strcmp("C". char *strncpy(char *s1. Funkcija s strcpy kopira uvijek i nul-znak. strcmp("B". const char *s2. 1. U tom sluˇaju treba naknadno dodati nul-znak. c Funkcija char *strchr(const char *s. return 0. printf("%d\n". const char *s2). Funkcija strcmp uvijek usporeduje parove znakova sve do kraja jednog od dva stringa. c Funkcija strncmp ima dodatni argument n koji predstavlja broj znakova c koji ´e biti usporedeni. } 163 strcmp("A". Za kopiranje jednog znakovnog niza u drugi koristimo funkcije char *strcpy(char *s1. -1. Program ´e redom ispisati vrijednosti 0. strcmp("aaac"."A")). Obje c funkcije vra´aju s1."B")). dok strncpy ne´e kopirati nul-znak ako je c n <= strlen(s2)."A")). 2.h> int main(void){ printf("%d\n".10.

&n). ako c je realan broj x potrebno pretvoriti u string najlakˇe je to uˇiniti pomo´u s c c funkcije sprintf: char x_str[64].. c Ako s2 nije prisutan u s1 bit ´e vra´en nul-pointer. z Ako smo. . pomo´u kˆda c o char line[256]. a n varijabla tipa int. Jedina razlika prema tim funkcijama je u tome ˇto c s se ˇitanje i pisanje vrˇi iz i u znakovni niz s... c s Tako funkcija sscanf ˇita iz stringa s (umjesto s tastature) prema zadanom c formatu. /* args */ . .164 POGLAVLJE 10.... ..). Za razliku od funkcija scanf i printf one kao prvi argument uzimaju pokazivaˇ na znakovni niz..."%12..4 sscanf()."%lf %lf %d".x).. ona c z moˇemo pomo´u sscanf izvrˇiti daljnju konverziju z c s sscanf(line.h> deklarira i mnoge druge funkcije za rad sa stringovima. na primjer. Ove funkcije prvenstveno sluˇe za formatiranje stringova. POLJA vra´a pointer na prvo pojavljivanje stringa s2 u stringu na koji pokazuje s1.......... float x. ... koji je dan kao prvi argument.). sprintf() Ove dvije funkcije deklarirane su u zaglavlju <stdio.7f". c c Datoteka zaglavlja <string. funkcija sprintf piˇe u string s (umjesto na ekran) prema zadanom s formatu.. gets(line). int sscanf(const char *s.h>... sekcija B3. Sliˇno. /* args*/ . uˇitali jedan string koji sadrˇi dva broja tipa double i jedan tipa int. Za potpuniju informaciju vidi man-stranicu na raˇunalu ili c [4]. 10. gdje su x i y varijable tipa double.. sprintf(x_str. int sprintf(char *s. const char *format. const char *format.&y.&x.

Na taj se naˇin jasno pokazuje da funkcija ne mijenja polje. stvarni argument je ime polja.3. Pri pozivu funkcije c koja ima polje kao formalni argument. c c Unutar funkcije sa v[i] dohva´amo i-ti element polja v. POLJE KAO ARGUMENT FUNKCIJE 165 10.5.5 Polje kao argument funkcije Polje moˇe biti formalni argument funkcije. onda polje treba deklarirati kao const polje. U tom sluˇaju argument se z c ne prenosi po vrijednosti ve´ funkcija dobiva pokazivaˇ na prvi element polja.i++) rez+=v[i]. n=3. c jer je funkcija dobila pokazivaˇ na prvi element polja. } Prevodilac ime polja v pri pozivu funkcije pretvara u pokazivaˇ na prvi elec ment polja. double v[]={1. double rez=0. int main(void) { int n.i<n. U deklaraciji z (jednodimenzionalnog) polja kao formalnog argumenta dimenziju polja nije potrebno navoditi.sv. return rez/n. sv=srednja_vrijednost(n. Prevodilac ne´e dozvoliti promjenu c polja unutar tijela funkcije.10. s c s z s c Naredba return ne moˇe biti iskoriˇtena za vra´anje polja u pozivni program jer funkcija moˇe vratiti samo skalarnu vrijednost ili strukturu. z .0. Time se dobiva na efikasnosti jer se izbjegava kopiranje (velikih) polja. return 0.v). for(i=0.2. double v[]) { int i. ˇtite´i nas tako od vlastitih pogreˇaka. Na primjer. } Polje v je argument funkcije deklariran s double v[]. c c Funkcija tada moˇe dohvatiti i promijeniti svaki elemet polja. a ne kopije polja. Broj elemenata z polja ˇiju srednju vrijednost treba izraˇunati takoder je argument funkcije. Tako na primjer. Ako funkcija uzima polje kao formalni argument i ne mijenja njegove c elemente. bez navodenja dimenzije polja.0.0. funkcija koja uzima polje realnih brojeva i izraˇunava srednju vrijednost moˇe biti napisana na sljede´i naˇin: c z c c double srednja_vrijednost(int n. Dimenzija je mogla biti navedena ali to nije nuˇno.0}.

a MAXY=3 je broj stupaca matrice. Na primjer..1.0. m[1][1]=5.0. Element na mjestu (i. tip je tip podatka.5.2.. POLJA 10.0.j) matrice m je m[i][j]. Poznavanje te ˇinjenice vaˇno je pri konsc z trukciji numeriˇkih algoritama koji rade s matricama te za razumijevanje c inicijalizacije polja. polje m moˇe biti inicijalizirano na sljede´i z c naˇin: c static float m[2][3]={1.4. drugi od 0 do izraz 2 . Na primjer. Tako se prvi indeks kre´e c od 0 do izraz 1 . Elementi viˇedimenzionalnog polja pamte se u memoriji raˇunala kao s c jedno jednodimenzionalno polje. Njene elemente moˇemo prosz torno zamisliti na sljede´i naˇin: c c m[0][0] m[1][0] m[0][1] m[1][1] m[0][2] m[1][2] U prvom retku su elementi m[0][i] za i=0. izraz n su konstantni cjelobrojni pozitivni izrazi koji odreduju broj elementa polja vezanih uz pojedine indekse.0. a u drugom m[1][i] za i = 0. m[0][1]=2.6 Viˇedimenzionalna polja s Deklaracija viˇedimenzionalnog polja ima oblik s mem_klasa tip ime[izraz_1][izraz_2]. 1. . polje m deklarirano sa static float m[2][3].6. .0.0}. Kod dvodimenzionalnog polja m poredak elemenata u memoriji bio bi m[0][0] m[0][1] m[0][2] m[1][0] m[1][1] m[1][2] Preciznije element m[i][j] biti ´e na l-tom mjestu u memoriji.2. ime je ime polja. . m[0][2]=3.1 itd. m[1][2]=6. .1. a izraz 1. Inicijalne vrijednosti ´e biti pridruˇene elementima matrice po recima: c z m[0][0]=1.3. gdje je mem klasa memorijska klasa. Pri tome su elementi poredani po recima ˇto s znaˇi da se pri smjeˇtanju elemenata u memoriju najdesniji indeks najbrˇe c s z varira.0.0.[izraz_n]. predstavlja matricu s dva retka i tri stupca.0. .166 POGLAVLJE 10.0.0. m[1][0]=4. gdje je c l=i*MAXY+j. 2.0.

MAXY=3 i MAXZ=4 pojedine dimenzije polja.ˇ 10.0. a zatim druga. m[1][][]. funkcija koja ˇita matricu s MAXX redaka i MAXY stupaca moˇe biti c z deklarirana kao void readinput(int m[MAXX][MAXY].0}. Trodimenzionalno polje float m[2][3][4]. Elementi polja m bit ´e stoga smjeˇteni c s u memoriju u redoslijedu m[0][][]. inicijalne se vrijednosti c mogu pomo´u vitiˇastih zagrada formirati u grupe koje se pridruˇuju pojec c z dinim recima.3.2. odnosno prvo prva matrica dimenzije 3×4. int m) gdje su n i m stvarni brojevi redaka i stupaca koje treba uˇitati. gdje su MAXX=2. int n.0.6. je jednodimenzionalno polje dimenzije 2 ˇiji su elementi m[0] i m[1] dvodic menzionalna polja tipa float[3][4].0. jedno jednodimenzionalno polje dimenzije 2 ˇiji su elementi m[0] i m[1] tipa c float[3] (jednodimenzionalna polja dimenzija 3). {4.0. VISEDIMENZIONALNA POLJA 167 Kako je takav naˇin inicijalizacije jako nepregledan.5. ˇto daje istu inicijalizaciju polja m.0} }. Preciznije element m[i][j][k] bit ´e smjeˇten c s na mjesto i*MAXY*MAXZ+j*MAXZ+k. to su reci matrice m. ali je namjera programera puno jasnija. Tako je npr. s Ukoliko neka od grupa ima manje elementa od dimenzije retka. dvodimenzionalno polje deklarirano naredbom float m[2][3]. ostali elemeti ´e biti inicijalizirani nulama. Kada je viˇedimenzionalno polje argument funkcije ono se moˇe deklas z rirati sa svim svojim dimenzijama ili sa svim dimenzijama osim prve. c Viˇedimenzionalna polja se definitaju rekurzivno: s • Viˇedimenzionalno polje je jednodimenzionalno polje ˇiji su elementi s c polja dimenzije manje za jedan. Na primjer.6. Tako moˇemo pisati z static float m[2][3]={{1. Ta ista c funkcija moˇe biti deklarirana na sljede´i naˇin: z c c .

kod viˇedimenzionalnih polja funkcija mora znati sve s dimenzije polja osim prve. z c z .a[0][1]).{’g’. Pri tome treba uoˇiti da dimenzije moraju biti c konstantni izrazi. int m) Naime. } Uoˇimo da prvu dimenziju u deklaraciji c char A[][2][2]={ {{’a’. gdje je l=i*MAXY+j. z c Posve analogno.{’g’. printf("Matrica A[1]:\n"). To je bitno ograniˇenje ANSI-C.’h’}}}.a[1][1]).{{’e’. printf("%c %c\n".168 POGLAVLJE 10.{{’e’.’h’}}}. Sve ˇto s funkcija mora znati je da se element m[i][j] nalazi na l-tom mjestu.’d’}}.a[0][0]. budu´i da je u deklaraciji funkcije polje isto ˇto i pokazivaˇ na c c s c prvi element polja (stoga ˇto se pri pozivu funkcije vrˇi implicitna konverzija s s polja u pokazivaˇ) moˇemo istu funkciju deklarirati na tre´i naˇin: c z c c void readinput(int (*m)[MAXY].{’c’. int n. z Konaˇno.h> char A[][2][2]={ {{’a’. int main(void) { printf("Matrica A[0]:\n"). } void f(char a[2][2]) { printf("%c %c\n". f(A[0]). f(A[1]). prevodioc moˇe izraˇunati.’f’}. POLJA void readinput(int m[][MAXY]. return 0.jezika. int n. int m) Zagrade su nuˇne da nebismo dobili “polje pokazivaˇa”.’b’}. broj redaka nije bitan za adresiranje elemenata matrice.a[1][0]. c Sljede´i program ilustrira inicijalizaciju trodimenzionalnog polja. ali sve ostale dimenzije su nuˇne. c #include <stdio. Stoga je samo MAXY nuˇan pri pozivu funkcije.’f’}.’d’}}.{’c’.’b’}. void f(char a[2][2]).

Matrica A je dimenzije 2 × 2. double B[2][3]={{0.4.0.000000 .6.0}.i<2. double C[2][3].000000 3. Budu´i da poredak petlji moˇe z s c z biti proizvoljan imamo 3! = 6 verzija tog algoritma.2.0. #include <stdio.0. Rezultat izvrˇavanja programa je s Matrica C: 2.0}}.++i) for(j=0.0.j<3.{1. Rezultat z izvrˇavanja programa bit ´e: s c Matrica A[0]: a b c d Matrica A[1]: e f g h Sljede´i program mnoˇi dvije matrice. a c z matrica B ima dimenziju 2 × 3.++i){ for(j=0.k<2.i<2.k.++j) for(k=0.1.++k) C[i][j]+=A[i][k]*B[k][j]. printf("Matrica C:\n").0}.h> double A[2][2]={{1.++j) printf("%f ". Uoˇimo da smo u c programu iskoristili ˇinjenicu da se statiˇka varijabla (polje C) automatski c c inicijalizirana nulama.ˇ 10.0. Produkt je matrica C = AB dimenzije 2 × 3.j<3.000000 1. } return 0. for(i=0.0.000000 2. printf("\n").000000 4.C[i][j]).1. } Mnoˇenje matrica vrˇi se u trostrukoj petlji.000000 4.0}}. to funkciji koja uzima dvodimenzionalno polje (char a[2][2]) kao argument moˇemo predati A[0] i A[1].{3. VISEDIMENZIONALNA POLJA 169 Budu´i da je trodimenzionalno polje zapravo (jednodimenzionalno) poc lje dvodimenzionalnih polja. int main(void) { int i.0.0.j. for(i=0.

.++i) for(k=0. Traˇena varijabla se sada ˇita iz z z c cachea. s Programski jezik C (C90) pokazuje ozbiljan nedostatak u radu s matricama. tzv. onda je ona ve´ u c c cacheu i njen dohvat je vrlo brz..j Funkcija bi mogla izgledati ovako: #include <math.++k) for(j=0. Ve´ina z c raˇunala danas ima hijerarhijski organiziranu memoriju u kojoj se bliske mec morijske lokacije mogu dohvatiti brˇe od udaljenih. Ali.j. double A[][M]) { double norm=0. uzastopni elementi jednog stupca nalaze se na memorijskim lokacijama koje su medusobno udaljene za duljinu retka. s s 1 . Kada treba dohvatiti varijablu na nekoj memorijskoj lokaciji dohvati se ˇitav blok c memorije i smjesti se u brˇu memoriju.j<3... for(i=0. int i. Ako je sljede´a varijabla koju treba dohvatiti blizu prethodne.0. ˇto predstavlja dodatni utroˇak vremena. Da bismo to ilustrirali pokuˇajmo napisati funkciju koja raˇuna Frobeniusovu normu matrice.. POLJA U unutarnjoj petlji redak matrica A mnoˇi se sa stupcem matrice B.. i.++j) C[i][j]+=A[i][k]*B[k][j]. cache.1 Pri mnoˇenju jednog z z retka matrice A i jednog stupca matrice B treba dohvatiti elemente stupca matrice B. 10.h> #define M 9 double Fnorm(int n. Stoga je efikasnija s o verzija algoritma u kojoj je petlja po j unutarnja: .j=1 a2 . Sada se u unutarnjoj petlji radi s recima matrica C i B.. Ako pak nije. Kod velikih matrica ta je udaljenost velika.. Ako s c matrica reda n. . onda se mora dohvatiti novi blok memorije i spremiti u cache.i<2..j=1 n A = i. variable-length arrays) uvedena su u jezik u standardu C99 i mnogi prevodioci ih joˇ ne implementiraju u potpunosti.170 POGLAVLJE 10.k<2. onda je njena Frobeniusovu norma je A = (ai.j )n i. ˇto rezultira sporijim kˆdom..7 Polja varijabilne duljine Polja varijabilne duljine (engl.h> #include <assert.

Kao argument funkcije. tzv. void vandermond(int n. double A[n][m]). int m.h> #include <math.h> double Fnorm(int n. Posljedica toga je da naˇa z s funkcija moˇe raditi samo s matricama reda 9. for(i=0.j<n. int m. return sqrt(norm). int m. o Zbog tih razloga C99 standard uvodi novi element jezika. int n. Osnovna razlika prema standardnim poljima u C-u je ˇto dimenzije polja mogu biti zadane varijablama. double A[n][m]. te stoga mora biti definirano u nekoj z funkciji ili kao argument funkcije. U polju varijabilne duljine ih tada treba zamijeniti zvjezdis cama: double Fnorm(int. double A[n][m]). polje varijabilne duljine.7.i<n. polje varijabilne c duljine bilo bi deklarirano na sljede´i naˇin: c c double Fnorm(int n. c #include <stdio. Polje varijabilne duljine moˇe biti jedino automatsko. // a je PVD Deklaracija double Fnorm(double A[n][m]. int main(void) { int n=3. a da za matricu drukˇije dimenzije moramo z c promijeniti M i rekompilirati kˆd. int m). double A[n][m]). // neispravno je neispravna zbog pogreˇnog poretka argumenata. broj stupaca. Drugim rijeˇima prevodilac nam ne´e dozvoliti da funkciji Fnorm c c c umjesto konstante M damo varijablu koja sadrˇi red matrice. } Prevodilac zahtijeva da su dimenzije matrice u deklaraciji argumenta funkcije konstantni izrazi. double A[*][*]).10. . Standard dozvoljava da se u prototipu s ne piˇu imena argumenata.++i) for(j=0. Tako moˇemo definirati s z int n=3. int. predamo funkciji kao simboliˇku konstantu. double A[n][m]). // a je PVD Pogledajmo kako bi izgledao program koji raˇuna Frobeniusovu normu matrice. To nas prisiljava da esencijalnu dimenziju. // PVD Uoˇimo da n i m moraju biti definirani prije A. int m. void print(int n. int m=3.m=3. POLJA VARIJABILNE DULJINE 171 assert(n < M).++j) norm += A[i][j]*A[i][j].

POLJA double A[n][m]. } } Izlaz iz programa bi bio 0. return 0.j<m.j.j. int m.++j) printf("%f ".500000 0.m. for(i=0. return sqrt(norm).i<n. for(i=0.++i) for(j=0.i<n.++i) for(j=0.166667 norma matrice = 0.200000 0. Ono k tome podlijeˇe z z jednom broju ograniˇenja u odnosu na obiˇna polja.0/(2.A[i][j]).250000 0.0+i+j). double A[n][m]) { double norm=0. spomenimo da sliˇnu funkcionalnost osigurava u brojnim implementacijama c c jezika nestandardna funkcija alloca(). int i.m.333333 0. vandermond(n. int m. printf("norma matrice = %f\n". double A[n][m]) { int i.++j) A[i][j] = 1.i<n.0.j.j<m. for(i=0.172 POGLAVLJE 10.A).++i){ for(j=0. double A[n][m]) { int i.A). Fnorm(n.333333 0.m. printf("\n").200000 0. c c Konaˇno. } void vandermond(int n.250000 0.876071 Polje varijabilne duljine moˇe biti deklarirano samo unutar neke funkcije. } void print(int n.A)). Ono se alocira z na programskom stogu i zato ne moˇe biti deklarirano static. print(n.j<m. } double Fnorm(int n. .++j) norm += A[i][j]*A[i][j]. int m.250000 0. vidi [3].

a pv pokazivaˇ na taj tip. Pokazivaˇ na neki tip deklarira se na sljede´i naˇin: c c c tip *ime. Za varijablu tipa int tipiˇno se rezervira 16 ili 32 c bita. a tip je tip podatka na koji pokazuje. ako je pv=&v. Zvijezdica c oznaˇava da se radi o pokazivaˇu. Pri manipulacijama s varijablom tu adresu ne z moramo eksplicitno poznavati nego u tu svrhu sluˇi ime varijable. Ako je v varijabla danog tipa. za varijablu tipa double 64 bita itd. pokazivaˇ na int je varijabla koja c sadrˇi adresu varijable tipa int. onda je *pv isto ˇto c s i v. Pokazivaˇ na neki tip je varijabla koja sadrˇi c c z adresu varijable danog tipa.Poglavlje 11 Pokazivaˇi c Svakoj varijabli u programu pridruˇena je memorijska lokacija ˇija veliˇina z c c ovisi o tipu varijable. gdje je ime pokazivaˇa. onda je naredbom c pv=&v. a ne o vrijabli tipa tip. Pored adresnog operatora koc z ristimo joˇ i operator dereferenciranja * koji vra´a vrijednost spremljenu na s c adresu na koju pokazivaˇ pokazuje. Program dohva´a memorijsku c lokaciju na kojoj je varijabla pohranjena pomo´u jedinstvene adrese koja c je toj lokaciji pridruˇena. z 11.1 Deklaracija pokazivaˇa c Da bi se dohvatila adresa neke varijable koristi se adresni operator &. Tako. Na primjer varic c jable pi deklarirana naredbom 173 . Na primjer. pokazivaˇu pv pridruˇena adresa varijable v. z Programski jezik C nudi mogu´nost rada neposredno s adresama varijabli c putem pokazivaˇa (pointera).

174 int *pi;

ˇ POGLAVLJE 11. POKAZIVACI

je pokazivaˇ na int. Prilikom definicije varijable ona moˇe biti inicijalizirana c z kao u ovom sluˇaju c int int i=5; *pi=&i;

Naravno, varijabla i ˇija se adresa uzima mora biti definirana prije nego ˇto c s se na nju primjeni adresni operator.

0xBFFFF966 0xBFFFF962 0xBFFFF958

i=5

0xBFFFF954 pi=0xBFFFF966

Adresni operator moˇe se primijeniti na operande kojima je pridruˇena jez z dinstvena adresa. Stoga ga ne moˇemo primijeniti na npr. aritmetiˇke izraze z c i sliˇno. Operator dereferenciranja djeluje samo na pokazivaˇke varijable. c c Adresni operator i operator dereferenciranja su unarni operatori i imaju isti prioritet kao ostali unarni operatori. Njihov prioritet je ve´i od prioriteta c aritmetiˇkih operatora tako da u aritmetiˇkim izrazima *pi nije potrebno c c stavljati u zagradu. Npr. uz gornje deklaracije izraz i=2*(*pi+6); dat ´e i=22 jer se *pi izraˇunava i daje 5 prije aritmetiˇkih operacija. c c c Operator dereferenciranja moˇe se pojaviti na lijevoj strani jednakosti tj. z moˇemo imati z *pi=6; ˇto je ekvivalentno s i=6. Adresni operator, s druge strane, ne moˇe se s z pojaviti na lijevoj strani jednakosti. Deklaracija int *pi indicira da je *pi objekt tipa int. Sintaksa deklaracije varijable imitira sintaksu izraza u kojem se ona pojavljuje. Isto se odnosi i na deklaraciju funkcija. U primjeru int *f(char *);

ˇ 11.2. POKAZIVACI I FUNKCIJE

175

f je funkcija koja uzima pokazivaˇ na char i vra´a pokazivaˇ na int. Dekc c c laracija sugerira da *f(s) mora biti tipa int (s je pokazivaˇ na char ili c jednostavno niz znakova). Pokazivaˇ se u printf naredbi ispisuje s kontrolnim znakom %p: c #include <stdio.h> int main(void) { int i=5; int *pi=&i; printf("i= %d, adresa od i= %p\n",i,pi); return 0; }

11.2

Pokazivaˇi i funkcije c

Pokazivaˇi mogu biti argumenti funkcije. U tom sluˇaju funkcija moˇe c c z promijeniti vrijednost varijable na koju pokazivaˇ pokazuje. c Uzmimo da ˇelimo napisati funkciju zamjena koja uzima dva cijelobrojna z argumenta x i y i zamijenjuje njihove vrijednosti: x preslikava u y, a y u x. Funkciju bismo mogli ovako napisati: void zamjena(int x, int y) { int temp=x; x=y; y=temp; } Ova funkcija ne daje traˇeni rezultat zbog prijenosa argumenta po vrijedz nosti. Pri pozivu funkcije zamjena(a,b) ona dobiva kopije stvarnih argumenata a i b koje medusobno zamijenjuje, ali to nema nikakvog utjecaja na stvarne argumente. Stoga, funkcija treba uzeti pokazivaˇe na varijable ˇije c c vrijednosti treba zamijeniti. Poziv funkcije treba biti zamjena(&a,&b); a funkcija treba imati oblik void zamjena(int *px, int *py) { int temp=*px; *px=*py; *py=temp; } /* POGRESNO */

176

ˇ POGLAVLJE 11. POKAZIVACI

Kada je polje argument funkcije, onda funkcija ne dobiva kopiju ˇitavog c polja ve´ samo pokazivaˇ na prvi element polja. Pri pozivu fukciji se daje c c samo ime polja (bez uglatih zagrada) jer ono predstavlja pokazivaˇ na prvi c element. ˇ Cinjenica da funkcija koja uzima polje kao argument oˇekuje pokazivaˇ c c na prvi element polja moˇe se iskoristiti da se funkciji dade samo dio polja. z Na primjer, funkcija f u programu char z[100]; void f(char *); ..... f(&z[50]); dobit ´e samo zadnjih 50 elemenata polja z. c Uoˇimo da funkciju koja uzima kao argument jednodimenzionalno polje c moˇemo deklarirati i kao funkciju koja uzima pokazivaˇ na tip polja, kao ˇto z c s to pokazuje sljede´i primjer: c #include <stdio.h> int a[3]={1,2,3}; void f(int *); void g(int []); int main(void) { f(a); g(a); return 0; } void f(int *x) { int j; for(j=0;j<3;++j) printf("%d ",x[j]); printf("\n"); } void g(int x[]) { int j; for(j=0;j<3;++j) printf("%d ",x[j]); printf("\n"); } U oba sluˇaja bit ´e ispisano polje a. c c Nadalje, funkcija moˇe vratitit pokazivaˇ kao u sljede´em primjeru. z c c

ˇ 11.3. OPERACIJE NAD POKAZIVACIMA #include <stdio.h> #include <stdlib.h> char *tocka(char *niz) { char *p; for(p=niz; *p !=’\0’; ++p) if(*p == ’.’) return p; return NULL; } int main(void) { char *p="bigjob.com"; printf("Pokazivac na prvi znak=%p,\n" "pokazivac na tocku= %p\n",p,tocka(p)); return 0; }

177

Funkcija tocka vra´a pokazivaˇ na char. Tu je vaˇno primijetiti da funkcija c c z ne smije vratiti pokazivaˇ na lokalnu varijablu. Takav pokazivaˇ ne pokazuje c c na korektnu memorijsku lokaciju jer se lokalna varijabla uniˇtava nakon izs laska iz funkcija. Iznimka tog pravila je statiˇka lokalna varijabla. Naime, c statiˇka varijabla postoji za cijelo vrijeme izvrˇavanja programa i ako funkc s cija vrati pokazivaˇ na nju, ona se moˇe koristiti i izvan funkcije; vidi primjer c z u sekciji 11.6.

11.3
11.3.1

Operacije nad pokazivaˇima c
Poveˇavanje i smanjivanje c

Aritmetiˇke operacije dozvoljene nad pokazivaˇima konzistentne su sa c c svrhom pokazivaˇa da pokazuju na varijablu odredenog tipa. Ako je pi c pokazivaˇ tipa int, onda ´e pi+1 biti pokazivaˇ na sljede´u varijablu tipa c c c c int u memoriji. To znaˇi da dodavanje jedinice pokazivaˇu ne pove´ava c c c adresu koju on sadrˇi za jedan, ve´ za onoliko koliko je potrebno da nova z c vrijednost pokazuje na sljede´u varijablu istog tipa u memoriji. c #include <stdio.h> int main(void)

stoga ˇto je asocijativnost unarnih operatora zdesna na lijevo pa se prvo pris mijenjuje dereferenciranje. onda su dozvoljene operacije c ++px --px px+n px-n Pokazivaˇ px+n pokazuje na n-ti objekt nakon onog na kog pokazuje px. Iz tog razloga.3.x[1]. a ne samog c pokazivaˇa. x[2]=%g\n".x[2]). printf("Vrijednosti: x[0]=%g. px=&x[0]. dolazi do pove´anja za jedan vrijednosti na koju px pokazuje. Isti izraz bismo mogli napisati kao c ++*px. c Unarni operatori & i * imaju viˇi prioritet od aritmetiˇkih operatora i s c operatora pridruˇivanja. .178 { ˇ POGLAVLJE 11. x[1]=%x. return 0. a zatim inkrementiranje. px.px+1. Svakom pokazivaˇu mogu´e je dodati i oduzeti cijeli broj. POKAZIVACI float x[]={1.0}. x[0]. } U ovom primjeru vidimo da ´e pokazivaˇ biti inkrementiran dovoljno da c c pokaˇe na sljede´u float vrijednost. Uoˇimo da smo pokazivaˇe ispisali u z c c c formatu %x kao heksadecimalni cijeli broj (usporedite s ispisom u formatu %p). Stoga u izrazu z *px += 1. Izraz *px++ inkrementirao bi pokazivaˇ nakon ˇto bi vratio vrijednost na koju c s px pokazuje. ˇelimo z li koristiti postfiks notaciju operatora inkrementiranja. x[2]=%x\n". printf("Adrese : x[0]=%x. x[1]=%g. moramo koristiti zagrade: (*px)++.2. Stoga ako je c c px pokazivaˇ i n varijabla tipa int.px+2).*px.0.0.

Legalno je pisati z double *p=0. OPERACIJE NAD POKAZIVACIMA 179 11. To je naroˇito korisno kod automatskih varijabli koje pri pozivu funkcije c ˇ imaju nedefiniranu vrijednost. Uoˇimo da je c c c py-px vrijednost cjelobrojnog tipa (a ne pokazivaˇkog)..h>).. Ako su px i py dva pokazivaˇa na isto polje te ako je py > px.3.3 Usporedivanje pokazivaˇa c Pokazivaˇe istog tipa moˇemo medusobno usporedivati pomo´u relacijskih c z c operatora. Pokazivaˇe je osim c c s drugim istovrsnim pokazivaˇem mogu´e usporedivati i s nulom. tako da je c c mogu´e pisati c if(px != 0) . c Ako su px i py dva pokazivaˇa istog tipa.... Cesto se koristi u ovu svrhu simboliˇka konsc tanta NULL #define NULL 0 .ˇ 11.. C garantira da nula nije legalna adresa i omogu´ava da se nula pridruˇi bilo kojoj pokazivaˇkoj varijabli s ciljem da c z c se signalizira kako varijabla ne sadrˇi legalnu adresu..2 Pokazivaˇi i cijeli brojevi c Pokazivaˇu nije mogu´e pridruˇiti vrijednost cjelobrojnog tipa. Usporedivanje s drugim cijelim brojevima nije dozvoljeno: if(px == 0xBFFFF986) .3. double *p=NULL. tada je c py-px+1 broj elemenata izmedu px i py.3. Na primjer. Takva operacija ima smisla ako pokazivaˇi pokazuju na isto polje. funkcija c koja daje broj znakova u stringu moˇe biti napisana na sljede´i naˇin: z c c . Iznimku c c z jedino predstavlja nula. ukljuˇuju´i krajeve. 11...3.4 Oduzimanje pokazivaˇa c Jedan pokazivaˇ moˇe se oduzeti od drugoga ukoliko oni pokazuju na isto c z polje. onda je mogu´e koristiti izraze c c px < py px > py px == py px != py Rezultat tih operacija je 1 ili 0 ovisno o tome da li je reacija zadovoljena ili ne... // POGRESNO 11. Naime. (simboliˇka konstanta NULL definirana je u <stdio.

Stoga se u datoteci zaglavlja <stddef. c z z • Pokazivaˇu moˇe biti pridruˇen pokazivaˇ istog tipa (npr. --px itd. POKAZIVACI p 5 6 \0 Mogu´e je da na nekim sustavima razlika dva pokazivaˇa ne stane nuˇno c c z u varijablu tipa int. while(*p != ’\0’) p++. while((s[i] = t[i]) != ’\0’) i++. char *t) { int i=0. px=py). c Navedimo ih sve ponovo.). • Pokazivaˇu moˇe biti pridruˇena adresa (npr. • Dva pokazivaˇa mogu biti oduzeta ukoliko pokazuju na isto polje. px=&x). 11. c z z c • Pokazivaˇu moˇe biti pridruˇena nula (npr. c Gore navedene operacije su jedine koje su dozvoljene s pokazivaˇima.180 int strlen(char *s) { char *p=s. c • Dva pokazivaˇa mogu biti povezana relacijskim operatorom ako pokac zuju na isto polje. ++px.5 Primjer Kao primjer napiˇimo verziju funkcije strcpy iz standardne biblioteke. } . s Prva verzija ima oblik void strcpy(char *s. c z px+3.h> definira cjelobrojni tip podatka ptrdiff t ˇija je ˇirina dovoljna da primi razliku bilo c s koja dva pokazivaˇa. return p-s. } s 1 2 3 4 p-s=6 ˇ POGLAVLJE 11. px=0 ili px=NULL).3. c z z • Pokazivaˇu moˇe biti dodana ili oduzeta cjelobrojna varijabla (npr.

pa moˇemo pisati z void strcpy(char *s. t++.3. Budu´i da unarni z c c operatori imaju asocijativnost zdesna na lijevo gornji kˆd moˇemo skratiti i o z pisati void strcpy(char *s. c c c z Na primjer.. char *pc... char *t) { while((*s++ = *t++) != ’\0’) . } Pokazivaˇi s i t bit ´e pove´ani nakon ˇto pridruˇivanje bude izvrˇeno. c void strcpy(char *s. char *t) { while((*s = *t) != ’\0’) { s++. koriste´i aritmetiku z c pokazivaˇa. } } Ovdje koristimo viˇi prioritet operatora dereferenciranja od operatora pris druˇivanja i inkrementiramo pokazivaˇe umjesto indeksa. pi=pc. char *t) { while(*s++ = *t++) . kˆd moˇemo joˇ malo skratiti ako uoˇimo da je . /* NEISPRAVNO */ . Kopiranje se zaustavlja kada se kopira nul znak ’\0’. c c c s z s Konaˇno. 11.... ovakav kˆd slabo izraˇava namjeru programera te ga treba izbjeo z gavati. int *pi. } Naravno.6 Generiˇki pokazivaˇ c c Pokazivaˇi na razliˇite tipove podatatka op´enito se ne mogu pridruˇivati.!=’\0’ usc o z s c poredivanje izraza s nulom. OPERACIJE NAD POKAZIVACIMA 181 Funkcija kopira polje znakova na koje pokazuje t u polje na koje pokazuje s. . Istu funkciju moˇemo izvesti i bez uglatih zagrada.ˇ 11.3.

nije garantirano s c da se vrijednost pokazivaˇa ne´e promijeniti. */ S druge strane. int *pi. c double *pd0. /* O. c c void *p. p=pd0. c c z c U sljede´em primjeru funkcija print uzima generiˇki pokazivaˇ ptr i jec c c dan argument tipa char koji govori na kakav objekt ptr pokazuje.*pd1. . void *p... /* ISPRAVNO */ Ako ponovna izvrˇimo konverziju u polazni tip pokazivaˇa..h> #include <stdlib. c double *pd0.182 ˇ POGLAVLJE 11. c Generiˇki pokazivaˇ se ne moˇe dereferencirati. pi=(int *) pc. Argument se zatim ispisuje na odgovaraju´i naˇin. c z c bez promjene pokazivaˇa.h> . z c onda formalni argument treba deklarirati kao pokazivaˇ na double. . z c Ipak.. POKAZIVACI Razlog je u tome ˇto konverzija pokazivaˇa na jedan tip u pokazivaˇ na s c c neki drugi tip moˇe dovesti do promjene interne reprezentacije pokazivaˇa.. c c Pokazivaˇ moˇe biti deklariran kao pokazivaˇ na void i tada govorimo o c z c generiˇkom pokazivaˇu. Pokazivaˇ na bilo koji tip moˇe se konvertirati u pokazivaˇ na void i obratno. . z tj.. /* ISPRAVNO */ pd1=p. pove´avati i smanjivati.. f(pd0). ako se ˇeli da funkcija primi upravo pokazivaˇ na double.. /* ISPRAVNO */ Osnovna uloga generiˇkog pokazivaˇa je da omogu´i funkciji da uzme pokac c c zivaˇ na bilo koji tip podatka.K... svako takvo pridruˇivanje je dozvoljeno uz eksplicitnu promjenu tipa. void f(void *). upotrebu cast operatora: char *pc... c c #include <stdio.

p). printf("c=%s\n". Ime jednodimenzionalnog polja je konsc tantan pokazivaˇ na prvi elemet polja. Isti efekt postiˇemo ako napiˇemo z s px=x. char *pc. if(c == ’i’){ pi=ptr.pc).p). print(’i’. char *s="string". void *ptr) { int *pi. print(’f’.*pi). printf("i=%d\n".ˇ 11. } else if(c == ’f’){ px=ptr. p=&a. p=s.0.4.4 Pokazivaˇi i jednodimenzionalna polja c Pokazivaˇi i polja su usko vezani.p). p=&j.*px). void *p. POKAZIVACI I JEDNODIMENZIONALNA POLJA void print(const char c. return 0. } 183 11. double *px. int j=8. printf("x=%f\n". Ako imamo polje x i pokazivaˇ istog c c tipa px. onda px nakon naredbe px=&x[0]. print(’s’. } } int main(void) { double a=8. . } else{ pc=ptr. pokazuje na prvi element polja x.

U skladu s time. char *pporuka="Dolazim odmah.i sliˇno. onda moˇemo koristiti c z notaciju px[i] umjesto *(px+i). Svaka funkcija f() koja c kao argument uzima jednodimenzionalno polje nekog tipa moˇe se deklarirati z kao tip_rez f(tip x[]) ili kao tip_rez f(tip *x) Ipak postoje i bitne razlike. naredbe tipa x= . tj. U prvoj se deklarira i inicijalizira polje od 15 znakova koje je mogu´e dohvatiti c i mijenjati putem indeksa u uglatim zagradama..". prevodilac c ima slobodu konstantan niz znakova smjestiti bilo gdje u memoriji pa stoga ne moˇe garantirati da ´e se vrijednosti niza mo´i mijenjati. POKAZIVACI ˇ Stoviˇe.". x-. ime polja je konstantan pokazivaˇ pa nije doc zvoljeno pisati x++. ako imamo pokazivaˇ px.. Pokazivaˇi i polja su stoga gotovo ekvivalentni. Ali. Sve ˇlanove niza moˇemo dohvatiti putem pokazivaˇa. u izrazu x+1 bit ´e iskoriˇtena pokac c s zivaˇka aritmetika. imamo da je *(px+i)==x[i] i dvije forme moˇemo koristiti ravnos z pravno. nisu dozvoljene. ali modific z c ciranje niza putem pokazivaˇa je nedefinirana operacija. z s Tada funkciju f() moˇemo pozvati s z f(&x[5]). S druge strane z c c pokazivaˇ pporuka moˇe pokazivati i na svaku drugu char varijablu. ili f(x+5). Naime. c z . Da bismo naglasili razliku izmedu polja i pokazivaˇa pogledajmo sljede´e c c dvije deklaracije: char poruka[]="Dolazim odmah. Isto tako.. To moˇemo iskoristiti u sljede´oj situaciji: uzmimo da c z c imamo funkciju f(float *) koja oˇekuje polje tipa float i da kao stvarni c argument funkciji ˇelimo predati dio polja x od ˇestog do zadnjeg elemeta.".184 ˇ POGLAVLJE 11. U drugoj deklaraciji deklarira se pokazivaˇ na char i inicijalizira adresom znakovnog niza "Dolazim c odmah. Polje x nije l-vrijednost.

c c *pp=3. Uzmimo sljede´i primjer z c c double polje[5]={0.2.0.9.2.3.0. // O. /* ISPRAVNO */ double *pt=polje. POKAZIVACI I CONST 185 11. Pokazivaˇ pp smo c inicijalizirali s nekonstantnim poljem polje.0.4. pp = &polje[1].0.5}.4. /* NIJE DOZVOLJENO */ Pokazivaˇ na konstantan tip se deklarira kao argument funkcije da bi c se pokazalo da funkcija ne´e korititi pokazivaˇ za mijenjanje varijable na c c koju pokazuje. Sam pokazivaˇ moˇemo slobodno mijenjati c z pp++. /* DOZVOLJENO */ . Prevodilac s nam jedino ne´e dozvoliti mijenjanje elemenata polja putem pokazivaˇa pp.0. /* NIJE DOZVOLJENO */ *pp=56. Na primjer. Funkcija koja samo ispisuje elemente polja mogla bi biti deklarirana na ovaj naˇin: c void print_array(const double *array. // O. Takva ´e funkcija prihvatiti konstantno i nekonstantno polje kao argument.1. const double *pp=polje.0. int n). z const double polje[5]={0.1.ˇ 11. Pokazivaˇ pp deklariran je kao pokazivaˇ na konstantan double. c Mogu´e je definirati konstantan pokazivaˇ na nekonstantan tip.0.0.3. Treba c c samo pomaknuti const u definiciji.0.5. // nije dozvoljeno polje[3]=1.0. Jednako tako moˇemo ga primijeniti na pokazivaˇe.3.2.0.1.5}.4.0.14159. const double *pp=polje. z Na primjer const double pi=3. double polje[5]={0.5}.K. Ako definiramo konstantnu varijablu.5 Pokazivaˇi i const c Vidjeli smo da modifikator const moˇemo koristiti za definiciju konstanti. // nije dozvoljeno pp[3]=1. ˇto je dozvoljeno. Na primjer.0. To znaˇi c c c da on pokazuje na konstantnu varijablu tipa double. double * const pp=polje. onda samo pokazivaˇ na konstantan c tip moˇe pokazivati na tu varijablu.K.0.2.

z mjeseci[2][2] je jednako ’u’. "listopad". c z char *mjeseci[]={ "sijecanj". ppi je polje od 10 pokazivaˇa na int. "lipanj". Polje mjeseci moˇemo iskoristiti u funkciji koja za zadani broj vra´a z c pripadni mjesec. pp = &polje[1].186 ˇ POGLAVLJE 11.9. "ozujak". "kolovoz". Stoga gornja deklaracija predstavlja polje od 10 elemenata koji su pokazivaˇi na tip int.1. "prosinac"}. "veljaca". Uoˇimo da uglate zagrade imaju viˇi prioritet od operatora c c s dereferenciranja. Na primjer.2.0. /* NIJE DOZVOLJENO */ *pp=56. Pojedine znakove moˇemo dohvatiti indeksiranjem.4. moˇemo defic c z nirati i konstantan pokazivaˇ na konstantan tip: c double polje[5]={0. . Analogno se definiraju i viˇedimenzionalna c s polja pokazivaˇa. Na primjer. Na primjer int *ppi[10]. Ovdje smo iskoristili ˇinjenicu da pokazivaˇ na char moˇemo inicijalizirati c c z stringovima. POKAZIVACI Ovakav pokazivaˇ pokazuje uvijek na istu lokaciju. "srpanj". "sijecanj".0. Konaˇno.6 Polja pokazivaˇa c Polje pokazivaˇa ima deklaraciju c tip_pod *ime[izraz]. "travanj".0. /* NIJE DOZVOLJENO */ 11. "studeni". char *ime_mjeseca(int n) { static char *mjeseci[]={ "Nekorektan broj mjeseca". Polja pokazivaˇa koriste se umjesto dvodimenzionalnih polja kada treba c zapamtiti niz polja razliˇite duˇine. "svibanj". "rujan". a ne pokazivaˇ na polje od 10 varijabli tipa c c int. U ovom drugom sluˇaju morali bismo pisati c int (*ppi)[10]. "veljaca".3. "ozujak".5}.0. const double * const pp=polje.

"listopad". jedno dvodimenzionalno polje."kolovoz". return (n<1 || n>12) ? mjeseci[0] : mjeseci[n]. . POKAZIVACI I VISEDIMENZIONALNA POLJA 187 "travanj". Sliˇno je i s viˇedimenzionalnim poljima. -> *(x[2]+3) -> *(*(x+2)+3). *(*(x+2)+3) je sam taj element. } Uoˇimo da smo polje mjeseci deklarirali static kako ne bi bilo uniˇteno na c s izlazu iz funkcije. 11. c c *(x+2)+3 je tada pokazivaˇ na ˇetvrti element tog polja.ˇ ˇ 11. "studeni". onda je x[i][j] element na mjestu (i. Na primjer. svaki od kojih je polje od 4 elemeta. x[i][j] je polje od 4 elemeta. dakle pokazivaˇ na prvi element tre´eg polja. c c c c c *(x+2) tre´e polje. Operacija indeksiranja E1[E2] identiˇna je s *(E1+E2) i stoga je to koc mutativna operacija (ˇto nije naroˇito korisno). ako je static int x[MAXX][MAXY]. "srpanj". Stoga. "lipanj". "rujan".7. z Kod dvodimenzionalnog polja izraza oblika x[2][3] interpretira se na sljede´i naˇin: c c x[2][3] Ovdje je x pokazivaˇ na jednodimenzionalno polje ˇiji su elemeti polja.7 Pokazivaˇi i viˇedimenzionalna polja c s c U C-u dvodimenzionalno polje je polje ˇiji je svaki element jedno jednodimenzionalno polje. c s Ako imamo static int x[2][3][4]. ako je x polje s c onda drugi element polja moˇemo zapisati kao x[1] ili kao 1[x]. c c x+2 je pokazivaˇ na tre´e polje."svibanj".j). onda je x polje dimenzije 2 × 3 × 4 dok je x[i] polje od tri elementa. "prosinac"}. dok je x[i] polje od MAXY elemenata.

++i) aa[i]=A[i]. int n.n. Unutar funkcije f element A[i][j] moˇemo dohvatiti kao aa[i][j]. U zadnjem primjeru su zagrade nuˇne jer bi u suprotnom imali polje pokaz zivaˇa na tip. Zatim funkciju koja uzima matricu A deklariramo kao void f(double **aa.1 Matrica kao pokazivaˇ na pokazivaˇ c c Osnovni nedostatak programskog jezika C u numeriˇkim primjenama je c naˇin na koji se matrice predaju funkcijama.. int m) gdje su n i m dimenzije matrice.[izraz_n].188 ˇ POGLAVLJE 11... c f(aa.[izraz_n].7... Tada definiramo polje pokazivaˇa c double *aa[1024].[izraz_n]. Neka je definirano na primjer polje double A[1024][1024]. Na primjer. c s Prilikom deklaracije viˇedimenzionalnog polja (ali ne i definicije) mogu se s koristiti dvije ekvivalentne forme: potpuna tip_pod ime[izraz_1][izraz_2].m).. Slijedi z jedan kompletan primjer: .. ili pomo´u pokazivaˇa c c tip_pod (*ime)[izraz_2]. Funkciju f() ne pozivamo s poljem A ve´ s poljem c c pokazivaˇa aa. c 11..i<1024. Standard C99 je to rijeˇio c s uvode´i polja varijabilne duljine. Ove pokazivaˇe inicijaliziramo pokazivaˇima na retke matrice A: c c for(i=0. Sama matrica je deklarirana kao pokazivaˇ c na pokazivaˇ na double. ili bez prve dimenzije tip_pod ime[][izraz_2]. POKAZIVACI Na analogan naˇin tretiraju se tri i viˇedimenzionalna polja. Ovdje ´emo pokazati jedno rjeˇenje proc c s blema koje se ne oslanja na PVD..

Funkcije malloc i calloc deklarirane su na sljede´i naˇin c c . void f(double **aa.j) void f(double **aa. /* POGRESNO */ bio nekorektan i prevodilac bi javio greˇku. ali ta se konverzija vrˇi samo c s jednom. Dvodimenzionalno polje tada postaje pokazivaˇ na jednodimenzic onalno polje. ˇto nije isto ˇto i pokazivaˇ na pokazivaˇ. int main(void) { int i. int i.aa[i][j]). int m). Naime.8. s s c c 11.++i) aa[i]=A[i]. } 189 Program ´e naravno ispisati 123. int j) { printf("%f\n".445. return 0.8 Dinamiˇka alokacija memorije c Varijable i polja moˇemo alocirati za vrijeme izvrˇavanja programa prema z s potrebama za memorijskim prostorom. gdje je n broj redaka matrice.ˇ 11.1000). for(i=0.56. U tu svrhu sluˇimo se funkcijama z malloc i calloc deklariranim u <stdlib. int n.h>.h> double A[1024][1024]. A[56][1000]=123. Uoˇimo da bi poziv c f(A.n.44500. pri pozivu funkcije dolazi s do konverzije polja u pokazivaˇ na prvi element. f(aa. } // ispisi element na mjestu (i. double *aa[1024]. Cijena koju smo morali platiti je alokacija jednog polje pokazivaˇa c duljine n.m). Ovom tehnikom postiˇemo to da c z funkcija f ne mora znati stvarne dimenzije matrice A da bi s njom mogla raditi. DINAMICKA ALOKACIJA MEMORIJE #include <stdio.i<1024.

} Uoˇimo da operatorom sizeof postiˇemo neovisnost o stvarnoj duljini varic z jable double.h>. *calloc(size t n. Funkcija vra´a pokazivaˇ na blok memorije dovoljno velik da primi c c polje od n objekata veliˇine size.. Prvi je broj je broj varijabli za koje ˇelimo rezervirati memoriju. Ispitivanje je li pokazivaˇ koji malloc vra´a NULL pointer je c c nuˇno za ispravno funkcioniranje programa. *realloc(void *ptr.. POKAZIVACI void void void void *malloc(size t n).. exit(-1).190 ˇ POGLAVLJE 11. a drugi je broj bajtova koji svaka varijabla z zauzima.. tipa void* pa ga c c c stoga prije upotrebe treba konvertirati u potrebni tip pokazivaˇa. s c Funkcija malloc uzima jedan argument n koji predstavlja broj bajtova koji treba alocirati i rezervira memorijski blok od n bajtova.. . ili NULL ako zahtijev za memorijom nije c mogao biti ispunjen.1 Funkcija vra´a c pokazivaˇ na rezervirani blok memorije ili NULL ako zahtijev za memorijom c nije mogao biti ispunjen.. Vra´eni pokazivaˇ je generiˇki. z Funkcija calloc uzima dva argumenta. p=(double *) malloc(128*sizeof(double)). p=(int *) calloc(128. Primjer upotrebe je int *p.. Najˇeˇ´e iznosi uobiˇajenih c sc c osam bitova. size t size). if(p==NULL) { printf("Greska: alokacija memorije nije uspjela!\n"). size t je cjelobrojni tip bez predznaka definiran u <stddef. exit(-1). size t n). tj. Memorija se inicijalizira nulama tako da je svaki bit postavi na nulu.. Tipiˇan c c primjer upoterbe funkcije malloc je sljede´i: c double *p. 1 .. free(void *ptr). } Jedan bajt u C-u je duljina varijable char u bitovima.sizeof(int)).. . if(p==NULL) { printf("Greska: alokacija memorije nije uspjela!\n"). dovoljno ˇirok da primi vrijednost koju vra´a sizeof operator.

pf je pokazivaˇ na funkciju koja uzima dva argumenta. c Funkcija vra´a pokazivaˇ na alocirani blok. Ako je nova dimenzija memorijskog bloka ve´a od stare. Ona uzima pokazivaˇ na alocirani blok memorije c c i oslobada memoriju. Nova z c veliˇina memorijskog bloka (u bajtovima) dana je u drugom argumentu. Ako je potrebno.ˇ 11. double)). . onda c c je stari memorijski blok dealociran. c s 11.tip_2 arg_2. Ukoliko realloc vrati pokazivaˇ razliˇit od prvog argumenta.9. onda se samo dealocira dio memorije. c Funkcija realloc uzima kao prvi argument pokazivaˇ na memoriju alocic ranu s malloc ili calloc funkcijom i mijenja njenu veliˇinu.. prvi tipa char. nije inicijalizirana... U primjeru c int (*pf)(char c. a stari sadrˇaj je premjeˇten na novu z s lokaciju. POKAZIVAC NA FUNKCIJU 191 Primijetimo da svi bitovi postavljeni na nulu ne reprezentiraju nuˇno.tip_n arg_n). ili NULL ako realokacija nije usc c pjela. ime je tada pokazivaˇ na funkciju koja uzima n argumenata tipa tip 1 do c tip n i vra´a vrijednost tipa tip pod. c z Uzmimo kao primjer funkciju koja raˇuna pribliˇno integral zadane funkc z cije po trapeznoj formuli. Ako je nova dimenzija bloka manja od stare. double a). ˇuvaju´i sadrˇaj c c c z memorije. onda se dodaje c nova memorija koja. Memoriju alociranu pomo´u funkcija malloc i calloc potrebno je delocic rati pomo´u funkcije free. U sluˇaju neuspjeha realokacije stari memorijski blok ostaje nepromic jenjen. Tako ako funkcija g uzima kao argument pokazivaˇ c na funkciju gornjeg tipa i ne vra´a niˇta. onda bismo deklarirali c s void g(int (*)(char. kao i kod malloca. Kod deklaracija (koje nisu definicije) imena varic jabli se mogu ispustiti. free(p). Ako je pf pokazivaˇ na funkciju onda je (*pf) moˇe koristiti kao ime funkcije. na svakoj hardwaz reskoj platformi. Zagrade su obavezne. a drugi c tipa double te vra´a int. Memorijski prostor treba delocirati ˇim viˇe nije potreban. sadrˇaj ´e biti kopiran na novu lokaciju. .9 Pokazivaˇ na funkciju c Pokazivaˇ na funkciju deklarira se kao c tip_pod (*ime)(tip_1 arg_1. nulu u sustavu s pokretnim zarezom i nul-pokazivaˇ.

Kao i kod polja.sin)).10 Argumenti komandne linije U sistemskim okruˇenjima kao ˇto su dos i Unix.integracija(0. Zato smo funkciju c integracija moglo pozvati kao integracija(0. int main(void) { printf("Sinus: %f\n". zbog viˇeg prioriteta zagrada. double (*f)(double)) { return 0.1.1.&sin) i dobili bismo isti rezultat.integracija(0. double b.192 #include <stdio. double. Na primjer. ime funkcije se kao stvarni argument neke funkcije konvertira u pokazivaˇ na funkciju.1. Unutar same funkcije integracija pokazivaˇ na funkciju mora se nalaziti c unutar zagrada je bi *f(a). POKAZIVACI double integracija(double. programu se mogu prez s dati odredeni parametri.cos)). 11.h> #include <math. separirani razmacima. printf("Kosinus: %f\n". } double integracija(double a.1. double (*)(double)). argumenti komandne linije. bio interpretiran s kao dereferenciranje objekta f(a).sin) Na funkciju moˇemo primijeniti adresni operator tako da smo mogli pisati z integracija(0. naredba (program) cp u operacijskom sustavu Unix koja vrˇi kopiranje das toteka poziva se sa dva parametra: cp ime1 ime2 . tako da se navedu na komandnoj liniji iza imena programa.5*(b-a)*((*f)(a)+(*f)(b)).h> ˇ POGLAVLJE 11. } Funkcija integracija uzima donju i gornju granicu integracije i pokazivaˇ c na funkciju koju treba integrirati. return 0. tzv.

a ime2 ime datoteke na koju z ime1 ˇelimo kopirati.. Ostali parametri su smjeˇteni redom kojim su s s upisani na komandnoj liniji. for(i=0.neki."). vidi joˇ z c c s odgovaraju´e man-stranice). odnosno shell koji se koristi ekspandirat ´e specic jalne znakove ?. i++) printf("%s%s". } Mehanizam je sljede´i: operacijski sustav u varijablu argc smjeˇta broj arc s gumenta komandne linije koji su utipkani pri startanju programa. ksh. char *argv[]) { . onda je argc=1.h> int main(int argc. return 0.ovo. * i [] prije nego ˇto ih preda pozivnom programu. printf("\n"). argv[i]. Nova forma funkcije main() glasi: c c int main(int argc. 2 . } Pozovemo li program args narebom args ovo su neki parametri on ´e ispisati c args. U argv se nalaze pokazivaˇi na argumente komandne linije.10.(i<argc-1) ? ". csh ili bash.11. Ako nema argumenata komandne linije.2 Nadalje argv[argc] mora biti nul-pokazivaˇ. Tu funkcionalnost osigurava funkcija main().i<argc. spremljeni u argv[0] do c argv[argc-1]." : ". char *argv[]) { int i. onda argc daje toˇno c broj argumenata komandne linije. Viˇe s s detalja moˇe se na´i priruˇniku za dani shell (sh. ARGUMENTI KOMANDNE LINIJE 193 gdje je ime1 ime datoteke koju ˇelimo kopirati. obiˇno nazvan argv. c Na primjer. Programi pisani u C-u takoder mogu primiti argumente z komandne linije.. uve´an za c jedan. Pri tome je argv[0] uvijek pokazivaˇ na string koji sadrˇi c z ime programa koji se izvrˇava.su. Funkcija main() prihva´a dva argumenta: argc tipa int i polje pokac zivaˇa na char. Operacijski sustav Unix.parametri.. c Ako shvatimo ime programa kao argument komandne linije. program koji samo ispisuje argument komandne linije koji su mu dani izgledao bi ovako: /* program args */ #include <stdio.

/* p je funkcija koja uzima pokazivac na char i vraca pokazivac na int */ int (*p)(char *a). /* p je pokazivac na funkciju koja uzima pokazivac na char i vraca int */ int (*p(char *a))[10]. ˇto komplis cira ˇitanje deklaracija. c c Prilikom interpretacije deklaracije uzimaju se u obzir prioriteti pojedinih operatora. /* p je funkcija koja uzima pokazivac na char i vraca int */ int *p(char *a).11 Sloˇene deklaracije z Pri ˇitanju sloˇenih deklaracija osnovno pravilo je da je deklaracija objekta c z u skladu s naˇinom koriˇtenja objekta. deklaracija pokazivaˇa na funkciju koja ne uzima argumente i vra´a int. POKAZIVACI 11. Tako je c int (*f)(void). int p(char *a). /* p je funkcija koja uzima pokazivac na polje znakova i vraca int */ .194 ˇ POGLAVLJE 11. int (*p)[10]. /* p je funkcija koja uzima pokazivac na char i vraca pokazivac na polje od 10 elemenata tipa int int */ int p(char (*a)[]). To znaˇi da je f funkcija koja ne uzima c argumente i vra´a pokazivaˇ na int. c c Evo nekoliko primjera: int *p. /* p je pokazivac na int */ /* p je polje od 10 pokazivaca na int */ /* p je pokazivac na polje od 10 elemenata tipa int */ /* p je funkcija koja ne uzima argumente i vraca pokaziva\v c na int */ int *f(void). int *p[10]. Ti prioriteti mogu se promijeniti upotrebom zagrada. onda *f() mora biti tipa int. Ako deklariramo c s int *f(void).

/* p je pokazivac na funkciju koja uzima pokazivac na polje znakova i vraca pokazivac na int */ int *(*p[10])(char *a).ˇ 11. /* p je pokazivac na funkciju koja uzima pokazivac na polje znakova i vraca int */ int *(*p)(char (*a)[]). SLOZENE DEKLARACIJE 195 int (*p)(char (*a)[]).11. /* p je polje od 10 pokazivaca na funkciju koja uzima pokazivac na char i vraca pokazivac na int */ .

}.. moˇemo definic c z rati strukturu tocka koja definira toˇku u ravnini c struct tocka { int x. tip_2 ime_2. s c 12. osnovni tipovi. a unutar vitiˇastih zagrada su pojedini ˇlanovi strukture.1 Deklaracija strukture Struktura se deklarira na sljede´i naˇin: c c struct ime { tip_1 ime_1. pokazivaˇi. Ova struktura sadrˇi dva ˇlana istog tipa.. }... . tip_n ime_n... int y.1. struct je rezervirano ime. Strukture sluˇe grupiranju c z viˇe podataka razliˇitih tipova. Nakon ˇto je struktura deklarirana moˇemo definirati varijable 196 . Na primjer. no ˇlanovi strukture mogu biti bilo z c c kojeg tipa.Poglavlje 12 Strukture Polja grupiraju ve´i broj podataka istog tipa. ali ne rezervira memorijs z ski prostor. polja i druge strukture.1 Varijable tipa strukture Deklaracija strukture samo definira tip podatka. c 12. ime je naziv koji dajemo strukturi. .

int y.. druge varijable tog tipa na drugom mjestu nije mogu´e definirati. Ovdje su varijable p1 i p2 tipa tocka i za njih je ovdje rezerviran potreban memorijski prostor. } varijabla1. tip_2 ime_2.. } p1... varijabla2..1. moˇemo pisati z struct { int x. struct tocka p1.p2. } p1. .. Naravno. v_2.1.. U tom sluˇaju ne c moramo davati ime strukturi... .. c 12. int y. .. v_n}.. DEKLARACIJA STRUKTURE tog tipa: mem_klasa struct ime varijabla1. tj. varijabla2. Varijable tipa strukture mogu´e je definirati unutar deklaracije strukture c na sljede´i naˇin: c c mem_klasa struct ime { tip_1 ime_1.p2. ime je naziv strukture. Takav naˇin deklaracije strukture i definicije varijabli pogodan je kada vac rijable tipa struture uvodimo samo na jednom mjestu.....12.. a varijabla1.2 Inicijalizacija strukture Varijabla tipa strukture moˇe se inicijalizirati prilikom definicije na ovaj z naˇin: c mem_klasa struct ime varijabla={v_1. Na primjer... 197 gdje je mem klasa memorijska klasa. varijable koje definiramo.p2. tako smo mogli pisati struct tocka { int x.. varijabla2. tip_n ime_n.

Pravokutnik z c moˇe biti odreden donjim lijevim (pt1) i gornjim desnim vrhom (pt2): z struct pravokutnik { struct tocka pt1."Josip S. STRUKTURE pri ˇemu se v 1 pridruˇuje prvom ˇlanu strukture."Ivo R. c z c Uzmemo li strukturu struct racun { int broj_racuna.".".2. Ista imena ˇlanova mogu se koristiti u razliˇitim strukturama."Dalibor M. 12. v 2 drugom itd.00}.3 Struktura unutar strukture Strukture mogu sadrˇavati druge strukture kao ˇlanove.).00. 456. 35. c c 12. Sliˇno se moˇe inicijalizirati i polje struktura c z struct racun kupci[]={34. Ako je var varijabla tipa strukture koja sadrˇi ˇlan memb.2 12.1.memb ˇlan memb u strukturi var. float stanje.00.". char ime[80].". -234. c . }. 234. Ako je pt varijabla tipa struct tocka. onda je c z c var. 36."Goran S. onda moˇemo inicijalizirati varijablu kupac na ovaj naˇin: z c struct racun kupac={1234. struct tocka pt2. }. Pri tome deklaracija strukture tocka mora prethoditi deklaraciji strukture pravokutnik.1 Rad sa strukturama Operator toˇka c ˇ Clanovima strukture moˇe se pristupiti individualno putem operatora z toˇka (.00}.00.198 POGLAVLJE 12.

Adresni operator moˇe se primijeniti na z ˇitavu strukturu i na pojedine njene ˇlanove. Operator toˇka (.12.ime[7].x + (double) pt.) separira ime varijable i ime ˇlana strukture. Njihova asocijativnost je slijeva na c desno.y int varijable koje daju x i y koordinatu toˇke pt.clan). RAD SA STRUKTURAMA struct tocka pt.clan[izraz] Na primjer. isto tako &varijabla. je ekvivalentan s ++(varijabla. c Kada struktura sadrˇi polje kao ˇlan strukture. Ako pak imamo z polje struktura.pt1. strukture itd. Zbog najviˇeg s s prioriteta toˇka operatora izraz c ++varijabla. S ˇlanovima strukture postupa se jednako kao i s obiˇnim varijablama c c istog tipa. z U zadnja dva primjera dolazi do izraˇaja socijativnost.2. ako je kupac varijabla tipa struct racun.clan je ekvivalentan s &(varijabla. polja. Udaljenost toˇke pt do toˇke (0. onda su rect. tako da se operandi grupiraju prvo oko lijevog operatora.clan Na primjer. ako je varijabla kupci polje tipa struct racun.y). onda pojedini ˇlan elementa polja doseˇemo izrazom c z polje[izraz].pt1.x * pt.x i rect.clan. 199 onda su pt.broj racuna. Isto vrijedi i za sloˇene ˇlanove strukture.0) izraˇunali bismo ovako c c c double dist. Ako c je rect varijabla tipa struct pravokutnik. onda broj raˇuna c osmog kupca doseˇemo izrazom kupci[7]. Broj memorijskih lokacija koje c c struktura zauzima moˇe se dobiti pomo´u sizeof operatora kao i za jednosz c tavne tipove podataka.x i pt.clan). z c .y * pt. onda osmi znak u imenu kupca moˇemo dohvatiti izrazom kupac. sqrt(double). Spada u c c najviˇu prioritetnu grupu i ima asocijativnost slijeva na desno.y. jer su uglate zaz grade i operator toˇka istog prioriteta. struct pravokutnik rect. onda ˇlanovi polja doseˇu z c c z izrazom varijabla. dist=sqrt((double) pt. Taj broj moˇe biti ve´i od sume broja memorijskih z c lokacija za pojedine ˇlanove strukture. x i y koordinate donjeg lijevog vrha pravokutnika itd.

x je ekvivalenta s *(pp1. Struktura moˇe z c z biti argument funkcije koja tada dobiva kopiju strukture koja je stvarni argument funkcije. } 12. return p1.2. STRUKTURE 12. U primjeru struct tocka { int x. Uoˇimo da su zagrade nuˇne jer operator toˇka ima viˇi prioritet c z c s od operatora dereferenciranja.200 POGLAVLJE 12. } p1. p1.y += p2.3 Strukture i pokazivaˇi c Pokazivaˇ na strukturu definira se kao i pokazivaˇ na osnovne tipove c c varijabli. int y. z Na primjer. a pp1 je pokazivaˇ na strukturu tipa c tocka.1 Operator strelica (->) C nudi semantiˇki jednostavniji naˇin dohva´anja ˇlana strukture putem c c c c pokazivaˇa: ako je ptvar pokazivaˇ na strukturu. onda je izraz ptvar->clan .y. Pojedini ˇlan strukture moˇe se putem pokazivaˇa dose´i izrazom (*pp1). sljede´a funkcija uzima dvije strukture tipa tocka kao argumente c i vra´a strukturu tipa tocka koja je suma dvaju argumenata: c struct tocka suma(struct tocka p1.y. Izraz *pp1.x += p2.x i c z c c (*pp1). Pokazivaˇ moˇemo inicijalizirati adresom varijable: c z pp1= &p1. struct tocka p2) { p1. *pp1. p1 je varijabla tipa struct tocka. ˇto s nije korektno formiran izraz. Isto tako funkcija moˇe vratiti strukturu u pozivni program.x. 12.2 Struktura i funkcije Jedine operacije koje se mogu provoditi na strukturi kao cjelini su pridruˇivanje i uzimanje adrese pomo´u adresnog operatora.x).3. a clan jedan ˇlan strukc c c ture.

s Ako je ˇlan strukture i sam stuktura. i ->. neka je s struct pravokutnik r. onda elemente polja dostiˇemo izrazom c z ptvar->clan[izraz] Struktura moˇe sadrˇavati ˇlanove koji su pokazivaˇi. zbog najviˇeg prioriteta koji imaju operatori .pt1.x Tu se koristi asocijativnost operatora .clan ili *ptvar->clan Sliˇno. i -> z z c c c operatori imaju viˇi prioritet od * operatora. c Evo joˇ nekoliko promjera izraza s -> i . onda moˇemo kombinirati operatore c z -> i .podclan). Budu´i da . . i ++(ptvar->clan. vrijednost na koju ˇlan pokazuje s c moˇemo dohvatiti pomo´u z c *var.x isto ˇto i pp1->x itd. c c c s Pri tome se adresa koju sadrˇi ptvar pove´a za onoliko okteta koliko iznosi z c veliˇina strukture.x (pr->pt1). i -> izrazi poput c s ++ptvar->clan i ++ptvar->clan.3.clan. Tako je p1. *pr=&r.pt1).podclan ekvivalentni su izrazima ++(ptvar->clan) Izraz (++ptvar)->clan pove´at ´e pokazivaˇ na strukturu prije nego ˇto se dohvati clan strukture.x pr->pt1..ˇ 12. STRUKTURE I POKAZIVACI 201 ekvivalentan s (*ptvar).x (r.podclan Ako je neki ˇlan strukture polje. Tada su sljede´i izrazi ekvivalentni: c r. da bismo doˇli podˇlana uloˇene strukture: s c z ptvar->clan. koja je slijeva na desno.

Kako je operator inkrementiranja u postfiks formi. dok c c c c c drugi prvo vra´a ch ˇlan strukture. . operator -> ima najviˇi prioritet i prvi se primijenjuje. Konaˇno. razbijati ih u viˇe izraza i maksimalno se koristiti zagradama kako bi znaˇenje izraza bilo s c jasnije.2 Sloˇeni izrazi z Kada imamo pokazivaˇ na strukturu s ˇlanom koji je pokazivaˇ mogu se c c c javiti sloˇeniji izrazi. to ´e c do inkrementiranja do´i nakon ˇto se dohvati vrijednost na koju pt->ch poc s kazuje.202 POGLAVLJE 12. U c c c c ovom drugom primjeru zagrada nije potrebna. STRUKTURE 12. Izraz *pt->ch++ pove´at ´e clan ch nakon ˇto dohvati vrijednost na koju pt->ch pokazuje. c *pt++->ch ´e inkrementirati pt nakon ˇto dohvati objekt na koji ch pokazuje. Unarni opes ratori imaju isti prioritet i asocijativnost D->L. moˇemo pisati z pt++->ch (asocijativnost od ++ je zdesna na lijevo. To znaˇi da se prvo primic jenjuje operator inkrementiranja na pokazivaˇ pt->ch. c s Ne treba niti spominjati da ovakve izraze treba izbjegavati. Da bismo inkrementirali sam objekt na koji pt->ch pokazuje treba pisati (*pt->ch)++. c c s Naime. char *ch. a zatim vra´a ch ˇlan strukture. Tada moˇemo imati izraze z (++pt)->ch (pt++)->ch pri ˇemu prvi pove´ava pokazivaˇ pt.3. a tek onda operator c dereferenciranja. a -> slijeva na desno). tj. Na primjer. } *pt. neka je z struct { int n. i nakon toga pove´ava pokazivaˇ pt.

To su strukture u kojima je jedan ili viˇe s ˇlanova pokazivaˇ na strukturu istog tipa. struct element *next.4 Samoreferentne strukture Za implementaciju tipova podataka kao ˇto su vezane liste i stabla kos ristimo samoreferentne strukture.h> /* oznaka kraja liste */ #define KRAJ (struct ime *) 0 /* deklaracija samoreferentne strukture */ struct ime { char *p_ime.1 Jednostruko povezana lista Ilustrirajmo upotrebu samoreferentne strukture na jednom jednostavnom primjeru jednostruko povezane liste ˇiji su elementi znakovni nizovi.h> #include <stdlib. Poˇetak c c programa ima sljede´i oblik: c #include <stdio.4. }. posljednji element ima u polju c c next upisanu vrijednost NULL. SAMOREFERENTNE STRUKTURE 203 12. struct ime *next. }. Struktura ime sadrˇi pokazivaˇ na niz znakova p ime i pokazivaˇ na sljede´i z c c c element u listi next. Jedan primjer je c c struct element { char ime[64]. Na taj naˇin dobivamo jednostruko povezanu c listu. c c Taj nam pokazivaˇ sluˇi za povezivanje istovrsnih podataka u listu tako ˇto c z s ´e next pokazivati na sljede´i element u listi.12. /* pokazivac na prvi element liste (globalna varijabla) */ struct ime *start=KRAJ. . Struktura element ima ˇlan next koji je pokazivaˇ na strukturu tipa element.4. 12.h> #include <string.

. Kˆd ne uzima u obzir eventualni pretek o spremnika line. zadnji->next != KRAJ. // ne radi nista /* neka zadnji pokazuje na novi element */ zadnji->next=novi. exit(-1).. if(start==KRAJ) /* prazna lista */ start=novi. if(novi == NULL) { printf("Nema dovoljno memorije .line). ").\n"). novi->p_ime=(char *) malloc(strlen(line)+1). *novi. U ovom trenutku c c lista je posve definirana i ne sadrˇi niti jedan element. printf("Unesite ime > "). scanf(" %[^\n]"."). . z Napiˇimo funkciju unos koja dodaje novi element na kraj liste.line). } } Kˆd prvo alocira memoriju za novi element. else { /* pronadji kraj liste */ for(zadnji=start. printf("Dodavanje novog elementa na kraj liste. } strcpy(novi->p_ime. Zatim se alocira memorija za c novo ime koje se uˇitava u spremnik line pomo´u scanf funkcije i kopira c c na rezerviranu memoriju sa strcpy. s /* dodavanje novog elementa na kraj liste */ void unos(void) { struct ime *zadnji. } novi->next=KRAJ. novi = (struct ime *) malloc(sizeof(struct ime)).. exit(-1). njegov next ˇlan se postavlja na KRAJ. Budu´i da ´e on biti zadnji u o c c listi. if(novi->p_ime == NULL) { printf("Nema dovoljno memorije . z Element se u vezanu listu moˇe dodati i izbrisati na bilo kojem mjestu.. zadnji=zadnji->next) . STRUKTURE Pokazivaˇ na prvi element liste start definiran je kao globalna varijabla c i inicijaliziran nul-pokazivaˇem koji oznaˇava kraj liste.204 POGLAVLJE 12. char line[128].

12.line). char line[128]. element=element->next) { printf("%d. int i. c Za pretraˇivanje liste koristimo istu for petlju kojom obilazimo ˇitavu z c listu. /* Pretrazivanje liste */ printf("Nalazenje imena u listi. scanf(" %[^\n]". printf("Unesite ime "). test=test->next) { if( !strcmp(test->p_ime.i. c nalazimo zadnji element i njegov next ˇlan postavljamo na novi koji time c postaje zadnji element liste.\n").line) ) { printf("Podatak je %d. Ukoliko je lista prazna poˇetak liste inicijaliziramo s novi. test != KRAJ. return. c void ispis(void) { struct ime *element.element->p_ime). } printf("Ispis liste \n"). return.i). SAMOREFERENTNE STRUKTURE 205 start KRAJ Novi element je sada inicijaliziran i lokalna varijabla novi pokazuje na njega. . %s\n". /* Ispis liste */ if(start == KRAJ ) { printf("Lista je prazna. element != KRAJ.\n"). for(element=start. void trazi(void) { struct ime *test.4. Sljede´a funkcija ispisuje listu. i=1. u listi\n". Ako nije. int i=1. for(test=start. i++. } return. } Ukoliko lista nije prazna u jednoj for petlji se prolazi svim njenim ˇlanovima.

\n"). /* ako je prvi element onaj koji trazimo */ if( !strcmp(start->p_ime. Ukoliko ime nije u listi c s bit ´e ispisano: Podatak nije u listi. } printf("Unesite ime "). if(!(tmp=test->next)) break. tmp=start. return. poˇevˇi od prvog do zadnjeg elementa liste. test != KRAJ. for(test=start. nije nadjen if( !strcmp(tmp->p_ime. if(start == KRAJ){ printf("Lista je prazna. s z void brisi(void) /* Brisanje elementa iz liste */ { struct ime *test. %d u listi\n". c Brisanje elementa iz liste je neˇto sloˇenije.\n"). u listi\n"). STRUKTURE } printf("Podatak nije u listi. return. char line[128]. test=test->next) { i++. *tmp. int i. .line) ) { printf("Brisemo podatak br.206 } i++. } Funkcija vrˇi test usporedivanja upisanog imena sa svakim podatkom u listi s linearno.line) ) { printf("Podatak je 1.i). // elm. free(tmp).line). } i=1. start=start->next. POGLAVLJE 12. free(tmp->p_ime).\n"). scanf(" %[^\n]". test->next=test->next->next. printf("Brisanje imena iz liste.

Stoga se stara vrijednost pokazivaˇa start pamti c u lokalnoj varijabli tmp i memorija se oslobada pozivom funkcije free(tmp). prvi element iza onog kojeg briˇemo i time je izvrˇeno izbacivanje iz liste. printf("\n\n\n").\n"). Kod brisanja elementa na proizvoljnom mjestu porebno je imati pokazivaˇ c na element koji prethodi elementu za brisanje jer njegov next ˇlan treba c preusmjeriti na element iza onog koji briˇemo. return. onda je tmp=test->next=KRAJ pa bi tmp->p ime u strcmp funkciji dalo greˇku. test=test->next) provjeravamo pokazuje li tmp=test->next na element za brisanje. Budu´i c da je memorija za svaki element alocirana dinamiˇki. return. Ostaje joˇ samo delocirati s s s memoriju izbrisanog elementa. Konaˇno da bismo kompletirali program uvodimo funkciju menu koja isc pisuje menu s mogu´im operacijama c int menu(void) { int izbor. onda se test->next postavlja na test->next->next. free(tmp). } } printf("Podatak nije u listi. U ovom kˆdu postoji problem zadnjeg elemeta.4. Ako je test zadnji eleo ment. Ako nije. Stoga tesiramo taj pokazivaˇ i izlazimo iz petlje pomo´u break s c c naredbe ako smo na zadnjem elementu. . SAMOREFERENTNE STRUKTURE free(tmp->p_ime).12. unosimo ime koje ˇelimo izbrisati i provjeravamo da li je ono na prvom z mjestu u listi. } 207 Kod brisanja elementa iz liste prvo provjeravamo da li je lista prazna. Dovoljno je samo s start postaviti da pokazuje na drugi element: start=start->next. test != KRAJ. prilikom brisanja elec menta treba ju osloboditi. Stoga u petlji s for(test=start. Ako da. To radimo stoga ˇto je brisanje elementa na prvom mjestu s neˇto jednostavnije od brisanja proizvoljnog elementa.

4. break. break. break. case 3: trazi(). case 4: brisi(). break. 1. 2. Ispis liste ").208 printf("\n printf("\n printf("\n printf("\n printf("\n printf("\n printf("\n printf("\n POGLAVLJE 12. } } while(izbor != 5). return 0. 5. izbor = "). } . } i dajemo glavni program: int main(void) { int izbor. return izbor.&izbor). case 5: break. ========="). scanf("%d". Unos novog elemeta "). case 2: ispis(). do { switch(izbor=menu()) { case 1: unos(). default: printf("\n Pogresan izbor."). Izlaz "). 3. STRUKTURE OPERACIJE : "). Pretrazivanje liste "). Brisanje iz liste ").

onda varijable tipa tocka deklariramo kao struct tocka p1. Znaˇenje simbola koji se uvodi typedef deklaracijom je sljede´e: Ako c c se ispusti kljuˇna rijeˇ typedef iz deklaracije. no kako c typedef interpretira prevodilac mogu´e su sloˇenije supstitucije. onda moˇemo pisati typedef double Masa. int y. Op´enito typedef ima oblik c typedef tip_podatka novo_ime_za_tip_podatka.*pm1.Pdouble). }. typedef se ˇesto koristi sa strukturama kako bi se eliminirala potreba da c se pri deklaraciji varijabli navodi struct.5. TYPEDEF 209 12. ako imamo struct tocka { int x. Masa elementi[10]. *pp1. c z pokazivaˇ na double moˇemo nazvati Pdouble: c z typedef double * Pdouble. ako neki podaci tipa double imaju znaˇenje c z mase. i tako dalje. i moˇemo pisati z Pdouble px. Vidimo da je typedef vrlo sliˇan preprocesorskoj naredbi #define. i Masa ´e postati sinonim za double.5 typedef Pomo´u kljuˇne rijeˇi typedef moˇemo postoje´im tipovima podataka c c c z c dati nova imena. void f(Pdouble.12. px= (Pdouble) malloc(100*sizeof(Pdouble)). Na primjer. Na primjer. Deklarirani simbol predstavlja tip koji bi ta varijabla imala. .m2. Na primjer. dobiva se obiˇna deklaracija c c c varijable u kojoj deklarirani simbol postaje varijabla. Nakon toga moˇemo deklarirati varijac z ble tipa double kao Masa m1.

nakon s c typedef int (*PF)(char *. int y.210 Umjesto toga moˇemo iskoristiti typdef: z typedef struct int x.. PF postaje ime za pokazivaˇ na funkciju koja uzima dva pokazivaˇa na char c c i vra´a int. Na primjer.. char *). ili struct pravokutnik { tocka pt1. typedef struct element *Pelement. STRUKTURE i sada moˇemo pisati z tocka p1. *pp1. moˇemo pisati z .. c Sljede´a ˇesta upotreba typedef-ova je za skraˇivanje kompleksnih dekc c c laracija kao ˇto su to pokazivaˇi na funkcije.. Na primjer. int (*g)(char *. }. typedef struct element { char ime[64]. { POGLAVLJE 12. Sada moˇemo definirati z Element root. } tocka. } Element. tocka pt2.. Tada moramo uvesti jednu prethodnu deklaraciju tipa. i sliˇno. Pelement next. Sloˇenija je situacija ako ˇelimo napraviti typedef samoreferentne strukz z ture. char *)) { . Sada umjesto deklaracije c void f(double x.

Tako su u.6. tip_n ime_n. uvodenje smislenih imena za standardne tipove podataka pridonosi boljoj dokumentaciji programa. }. Ako u uniju upiˇemo vrijednost jednog tipa. union ime x. Drugo... Varijabla tipa ime moˇe se deklarirati pri deklaraciji unije ili odvojeno. 12. Na primjer.. . kod unije svi ˇlanovi dijele istu c c memorijsku lokaciju. Komponentama varijable u moˇemo pristupiti preko operatora . UNIJA void f(double x. union pod { int i.... PF g) { .i i pu->i varijable tipa int. a u.. s c c Sintaksa unije analogna je sintaksi strukture.12.x i pu->x varijable tipa float. Osnovna svrha unije je uˇteda memorijskog prostora.. Na primjer. . kao z kod struktura. ali dok kod strukture z c c svaki ˇlan ima zasebnu memorijsku lokaciju. 211 typedef se koristi za parametrizaciju kˆda koja omogu´ava ve´u prenoo c c sivost kˆda s jednog na drugo raˇunalo: na primjer size t je izveden kao o c typedef. *pu. Pri tome moramo paziti da uniji pristupamo konsistentno. Op´enito imamo union ime { tip_1 ime_1. } u. tip_2 ime_2.. u dijelu programa u kojem smo uniju u inicijalizirali kao varijablu tipa int moramo joj pristupiti s kao varijabli tipa int i obratno. Memorijska ´e lokacija biti dovoljno ˇiroka da u nju c s stane najˇiri ˇlan unije. Na istoj memos z c c rijskoj lokaciji moˇemo ˇuvati varijable razliˇitih tipova. . ili operatora z ->.y..6 Unija Unija kao i struktura sadrˇi ˇlanove razliˇitog tipa. float x..

234375 binarno = %x\n".x=0. c c Uniju pod moˇemo iskoristiti kako bismo ispisali binarni zapis broja s z pokretnim zarezom na sljede´i naˇin: c c u.i). STRUKTURE a onda vrijednost unije proˇitamo kao varijablu drugog tipa rezultat ´e ovisiti c c o raˇunalu i najvjerojatnije ´e biti besmislen. . Ovaj se kˆd oslanja na ˇinjenicu da se (na danom raˇunalu) za pam´enje o c c c varijabli tipa float i int koristi isti broj okteta.234375.212 POGLAVLJE 12. printf("0.u.

1 Vrste datoteka c c sc Datoteka je imenovano podruˇje u sekundarnoj memoriji.Poglavlje 13 Datoteke U ovom poglavlju opisujemo funkcije iz standardne ulazno-izlazne biblioteke koje sluˇe za rad s datotekama. C promatra datoteku posve funkcionalno kao cjelinu u koju je mogu´e upisati niz znakova ili ga iz nje proˇitati.h>. odn. Prilikom ˇitanja iz datoteke niz okteta c c z c je usmjeren od datoteke prema programu. koje sluˇi za smjeˇtaj podataka. Kod pisanja u datoteku niz znakova ide od programa prema datoteci. c c Preciznije. 213 . Ovakva apstraktna definicija datoteke omogu´ava da se tastatura i ekran c raˇunala tretiraju kao datoteke. Program koji koristi ove funkcije mora z ukljuˇiti datoteku zaglavlja <stdio. najˇeˇ´e tvrdom disku (disketi. CD-u i sliˇno). stream) koje je mogu´e dohvatiti individualno. c 1 Niz znakova. program moˇe ˇitati ulazni niz znak-po-znak. c 13. Sa stac z s noviˇta operacijskog sustava datoteka je sloˇen objekt koji se obiˇno sastoji s z c od viˇe povezanih fragmenata smjeˇtenih na razliˇitim lokacijama u sekuns s c darnoj memoriji. S tastature je mogu´e samo ˇitati. dok je c c c na ekran mogu´e samo pisati. Kako operacijski sustav brine o svim detaljima rada s datotekama. niz varijabli tipa char. Najmanja jedinica koja moˇe biti proˇitan ili upisana je jedan znak z c (oktet). u C-u je datoteka kontinuirani niz okteta1 (eng.

) moˇe preslikati u niz vrijednosti z tipa char.1. 1) Mogu´e je neposredno c koristiti funkcije za ulaz/izlaz koje nudi operacijski sustava. Svaka linija sadrˇi z nula ili viˇe znakova iza kojih slijedi znak za prijelaz u novi red ’\n’. Treba uoˇiti da C omogu´ava da danu datoteku promatramo kao binarnu c c ili tekstualnu prema naˇim potrebama. Macintosh koristi znak ’\r’. Tekstualna datoteka je isto tako niz znakova ali se on interpretira na malo drugaˇiji naˇin. u binarnu je datoteku mogu´e upisati podatke onako kako su c c reprezentirani u raˇunalu. to je niska razina ulaza/izlaza. Razlika medu njima je u naˇinu na koji se interpretira sadrˇaj datoteke. Tekss tualne datoteke su namijenjene pohranjivanju tekstualnih podataka. Vidimo da pojam binarne datoteke odgovara naˇoj prethodnoj definiciji s datoteke kao niza znakova. Kada C otvori tekstualnu datoteku kreiranu pod MS-DOSom on ´e svaki par znakova ’\r’ ’\n’ pretvoriti u c znak ’\n’. Pored toga kˆd koji s o .1. neovisno o tome da li datoteka sadrˇi s z tekstualne podatke ili ne. Pored toga neki MS-DOS editori oznaˇavaju kraj datoteke c znakom Ctr-Z dok C nema poseban znak za kraj datoteke. Funkcije iz standardne biblioteke jezika C skrivaju od korisnika jedan niz detalja vezanih uz operacijski sustav pa su stoga jednostavnije za koriˇtenje od funkcija koje nudi operacijski sustav. znak za kraj linije je ’\n’ (kao i u C-u) te stoga pod UNIXom nema razlike izmedu binarne i tekstualne datoteke. Razlika je prikazana z na slici 13. Teksualna datoteka je niz znakova podijeljenih u linije. 13.214 POGLAVLJE 13.h> datoteci. Budu´i da se svaki tip c podatka u C-u (int. Dvije vrste datoteka implementirane su na isti naˇin. Tekstualnu datoteku formiranu npr.1.1 Tekstualne i binarne datoteke ANSI standard poznaje dvije vrste datoteka: tekstualne i binarne..1. c Operacijski sustav MS-DOS kraj linije oznaˇava s dva znaka ’\r’’\n’. Analogna se transformacija provodi s Macintoshovim tekstualnim datotekama. struct . To je standardna visoka razina ulaza/izlaz. 2) Mogu´e je koristiti standardne ulazno/izlazne funkcije ˇiji c c su prototipovi dani u <stdio.2 Razine ulaza/izlaza Postoje dvije razine ulazno-izlaznih operacija.. Potreba za razlikovanjem izmedu tekc c stualnih i binarnih datoteka pojavila se stoga ˇto razliˇiti operacijski sustavi s c koriste razliˇite naˇine oznaˇavanja kraja linije: programski jezik C oznaˇava c c c c kraj linije znakom ’\n’. c z Binarna datoteka je niz podataka tipa char. double. c dok npr. pod MSDOSom moˇemo otvoriti i kao tekstualnu i kao binarnu. Pod operacijskim sustavom UNIX. DATOTEKE 13.

VRSTE DATOTEKA slika 13.1. DOS.13.) mogu´e je standardni ulaz. izlaz i izlaz za greˇke c s preusmjeriti. Istaknimo da C s tastaturu i ekran tretira kao datoteke budu´i da datoteka predstavlja konc ceptualno naprosto ulazni ili izlazni niz oktetat. Nasuprot s tome funkcije operacijskog sustava pruˇaju programeru viˇe funkcionalnosti. Na nekim operacijskim sustavima (Unix. To su standardni ulaz.. a standarni izlaz i izlaz za greˇke s ekranom. standardni izlaz i standardni izlaz za greˇke. \r\n ^Z 215 otvorena tekstualno Promotrimo jednostavni pokus \n bacanja simetriˇnog novˇi´a \n c cc ili popularno nazvanu igru “pismo-grb”.1. z s no takav kˆd nije prenosiv na druge operacijske sustave. Mi ´emo u ovom o c poglavlju opisati samo neke funkcije iz standardne biblioteke. 13. Standardni ulaz je povezan s s tastaturom.. \n koristi standardnu biblioteku lakˇe je prenosiv na druge sustave.3 Standardne datoteke C automatski otvara tri datoteke za korisnika.1 Tekstualna i binarna datoteka MS-DOS tekstualna datoteka Promotrimo jednostavni pokus \r\n bacanja simetriˇnog novˇi´a \r\n c cc ili popularno nazvanu igru “pismo-grb”. \r\n ^Z otvorena binarno Promotrimo jednostavni pokus \r\n bacanja simetriˇnog novˇi´a \r\n c cc ili popularno nazvanu igru “pismo-grb”.1. .

. } gdje je ime ime datoteke koja se otvara. c Otvaranje postoje´e datoteke za ˇitanje i dodavanje teksta. if(ptvar==NULL) { printf("Poruka o gresci"): . c c c • Ako datoteka koju otvaramo s tipom ”a” ili ”a+” ne postoji bit ´e c kreirana. Otvaranje postoje´e datoteke za dodavanje teksta. DATOTEKE 13. Svrha je s spremnika smanjiti komunikaciju s vanjskom memorijom (diskom) i tako pove´ati efikasnost ulazno-izlaznih funkcija. .h> deklac rirana je struktura FILE koja sadrˇi sve podatke potrebne za komunikaciju z s datotekama (ukljuˇuju´i veliˇinu i poloˇaj spremnika)... Tipiˇno se fopen koristi na sljede´i naˇin: c c c c ptvar=fopen(ime. c c Kreiranje nove datoteke za ˇitanje i pisanje. c Otvaranje postoje´e datoteke za ˇitanje i pisanje.. a tip jedan od sljede´ih stringova: c tip "r" "w" "a" "r+" "w+" "a+" Znaˇenje c Otvaranje postoje´e datoteke samo za ˇitanje c c Kreiranje nove datoteke samo za pisanje.. const char *tip) int fclose(FILE *fp) s Komunikacija s datotekama vrˇi se preko spremnika (buffer) u koji se privremeno pohranjuju informacije koje se ˇalju u datoteku. Program koji ˇeli c c c z z otvariti datoteku mora prvo deklarirati pokazivaˇ na FILE kao u ovom pric mjeru: FILE *ptvar.2 Otvaranje i zatvaranje datoteke FILE *fopen(const char *ime. U datoteci <stdio. Datoteka mora biti otvorena pomo´u funkcije fopen prije prve operacije pic sanja ili ˇitanja.216 POGLAVLJE 13. c c Pri tome treba znati da: • Ako se postoje´a datoteka otvori s ”w” ili ”w+” njen sadrˇaj ´e biti c z c izbrisan i pisanje ´e poˇeti od poˇetka.tip).

izlazom i izlazom za greˇke. fclose(fpt). Time dolazimo do tipova • "rb".. Pomo´u gornjih oznaka tipova datoteka ´e biti otvorena u tekstualnom c c modu. s .\n"). Ti s pokazivaˇi imaju imena stdin (standardni ulaz). if((fpt=fopen("primjer. FILE *fpt."w"))==NULL) printf("\nGRESKA: Nije moguce otvoriti datoteku. pisanje. OTVARANJE I ZATVARANJE DATOTEKE 217 • Ako se datoteka otvara s tipom ”a” ili ”a+” novi tekst ´e biti dodavan c na kraju datoteke (”a” dolazi od eng. . otvaranje i zatvaranje datoteke primjer.h> definirani su konstantni pokazivaˇi na FILE c strukturu povezanu sa standardnim ulazom.... c • "wb+" ili "w+b" = binarno ˇitanje/pisanje (kreiranje). c • "ab+" ili "a+b" = binarno dodavanje. append). c c Funkcija fopen vra´a pokazivaˇ na strukturu FILE povezanu s datotekom ili NULL ako datoteka nije mogla biti otvorena. c • "rb+" ili "r+b" = binarno ˇitanje/pisanje (otvaranje).2. c s 13. stdout. Na kraju programa datoteka treba biti zatvorena funkcijom fclose koja uzima kao argument pokazivaˇ na spremnik: c fclose(ptvar). Da bi se otvorila u binarnom modu treba treba svakom tipu dodati b. stdout (standardni izlaz) c i stderr (standardni izlaz za greˇke).. "ab" = binarno ˇitanje..dat izgledalo bi ovako: #include <stdio.1 Standardne datoteke stdin. dodavanje.2..dat".13. Funkcija fclose vra´a nulu ako je datoteka uspjeˇno zatvorena te EOF u c s sluˇaju greˇke. "wb". Na primjer. stderr U datoteci <stdio.h> ....

fprintf i fscanf koje rade s datotekama. Razlika izmedu getc c c i fgetc je u tome da getc moˇe biti implementirana kao makro naredba dok z fgetc ne smije. #include <stdio."r"))==NULL){ printf("Ne mogu otvoriti datoteku %s\n". } else if((fpt=fopen(argv[1]. c 13. Program u sljede´em primjeru broji znakove u datoteci koja je navedena c kao argument komandne linije.1 ˇ Citanje i pisanje znak po znak int getc(FILE *fp) int fgetc(FILE *fp) Funkcije vra´aju sljede´i znak iz datoteke na koju pokazuje fp. printf i scanf imaju analogne verzije getc. .3. exit(-1).count=0. putchar. gets. exit(-1).argv[0]). FILE *fpt. fclose(fpt). Sve te funkcije kao prvi argument uzimaju pokazivaˇ na spremnik c (pokazivaˇ na FILE) povezan s datotekom. } while((ch=fgetc(fpt))!=EOF) count++. fputs. Funkcija getchar() koju s c smo uveli u Poglavlju 5 implementira se kao getc(stdin). char *argv[]) { int ch.218 POGLAVLJE 13.h> int main(int argc. DATOTEKE 13.h> #include <stdlib. U sluˇaju greˇke ili kraja datoteke vra´a se EOF. printf("\nBroj znakova = %d\n". To je razlog c s c ˇto je tip vra´ene vrijednosti int umjesto char. putc.3 Funkcije za ˇitanje i pisanje c Funkcije getchar. fgets. puts.argv[1]).count). if(argc==1){ /* ime datoteke nedostaje */ printf("\nUporaba: %s ime_datoteke\n".

c s Znak moˇe biti upisan u datoteku pomo´u funkcija z c int putc(int c. if(argc==1) /* ime datoteke nedostaje */ cpy(stdin. while((c=getc(fpulaz))!=EOF) putc(c.stdout).3. Za viˇe detalja vidi [7]. } 219 Znak proˇitan sa getc ili fgetc moˇe biti vra´en u datoteku pomo´u c z c c funkcije int ungetc(int c. onda se standardni ulaz kopira na standardni izlaz. else while(--argc>0) . #include <stdio. FILE *fpizlaz) { int c.fpizlaz).ˇ 13. int main(int argc. char *argv[]) { FILE *fpt. FILE *). Prvi argument je znak koji vra´amo. Ukoliko se ne navede niti jedno ime. Funkcija putchar(c) iz s s Poglavlja 5 definirana je kao putc(c. FILE *fp) s z Ponovo. FILE *fp) int fputc(int c. FUNKCIJE ZA CITANJE I PISANJE return 0.stdout). razlika izmedu putc i fputc je u tome ˇto putc moˇe biti implementirana kao makro naredba dok fputc ne smije.h> void cpy(FILE *. Funkcije vra´aju ispisani c znak ili EOF ako je doˇlo do greˇke prilikom ispisa. FILE *fp). Funkcija koja kopira jednu datoteku u drugu moˇe biti napisana na z sljede´i naˇin: c c void cpy(FILE *fpulaz. } Pomo´u te funkcije moˇemo napisati program cat koji uzima imena datoteka c z i ispisuje datoteke na standardnom izlazu u poretku u kojem su njihova imena navedena.

dok gets znak za prijelaz u novi red uˇitava. 13.220 POGLAVLJE 13. } Uoˇimo da smo u programu posve izbjegli upotrebu brojaˇa time ˇto inkrec c s mentiramo i dekrementiramo argc i argv."r"))==NULL){ printf("cat: ne mogu otvoriti datoteku %s\n". Ukoliko je ulazna linija dulja od toga.*argv). c Funkcija vra´a buf ako je sve u redu ili NULL ako se doˇlo do kraja datoteke c s ili se pojavila greˇka.stdin). FILE *fp). exit(1). int n. ostatak ´e biti proˇitan c s c c pri sljede´em pozivu funkcije fgets. Funkcija za ispis podataka u datoteku. Ona ne uzima veliˇinu c c buffera kao argument i stoga se moˇe desiti da je ulazna linija ve´a od mez c morije koja je za nju rezervirana.2 ˇ Citanje i pisanje liniju po liniju c Funkcija koja uˇitava podatke iz datoteke liniju-po-liniju je char *fgets(char *buf. ali na njegovo c mjesto stavlja ’\0’. s Funkcija char *gets(char *buf). Pri tome ´e biti uˇitano najviˇe n-1 znakova. Prvi argument je pokazivaˇ na dio memorije (eng. fclose(fpt). Tre´i argument je pokazivaˇ na datoteku iz koje se uˇitava.stdout). liniju-po-liniju je . } return 0.n. uvedena u Poglavlju 5 ˇita uvjek sa standardnog ulaza. DATOTEKE if((fpt=fopen(*++argv. buffer) u koji ´e ulazna c c linija biti spremljena. c c c Funkcija ´e proˇitati liniju ukljuˇuju´i i znak za prijelaz u novi red i na c c c c kraj ´e dodati nul znak ’\0’. Pri tome treba uzeti u obzir razliku da fgets uˇitava i znak ’\n’. } else { cpy(fpt. a drugi je veliˇina memorije na koju prvi argument c pokazuje.3. c c c s ukljuˇivˇi ’\n’. Stoga je bolje umjesto gets(buf) koristiti c fgetf(buf.

h> #define MAXLINE 128 int main(void) { char buf[MAXLINE].ˇ 13. koje sluˇe za razlikovanje izmedu pojedinih situacija. s fputs ispisuje znakovni niz na koji pokazuje str u datoteku na koju pokazuje fp.3. a c c s nulu (laˇ) u suprotnom. s 13. a nulu (laˇ) ako nije. ferror vra´a broj z c razliˇit od nule (istina) ako je doˇlo do greˇke. int feof(FILE *fp).3 Prepoznavanje greˇke s Budu´i da ulazne funkcije vra´aju EOF i u sluˇaju kada je doˇlo do greˇke c c c s s i u sluˇaju kada se naide na kraj datoteke postoje funkcije c int ferror(FILE *fp).stdout)==EOF) { fprintf(stderr. FUNKCIJE ZA CITANJE I PISANJE 221 int fputs(const char *str. Na kraj niza ona dodaje znak za prijelaz u novi red ˇto ju razlikuje od fputs(str. Zadnji nul znak ne´e biti ispisan i znak za prijelaz u novi red c ne´e biti dodan. z Sljede´i program kopira standardni ulaz na standardni izlaz i detektira c ulaznu greˇku pomo´u funkcije ferror.MAXLINE."\nIzlazna greska. s c #include <stdio.3.stdout). FILE *fp). exit(1).stdin) != NULL) if(fputs(buf. Funkcija vra´a nenegativnu vrijednost ako je ispis uspio ili EOF u sluˇaju c c greˇke. Funkcija c s s z feof vra´a broj razliˇit od nule (istina) ako smo doˇli do kraja datoteke.\n"). while(fgets(buf. c Funkcija int puts(const char *str). ispisuje znakovni niz na koji pokazuje str na standardni izlaz. } if(ferror(stdin)) { .

} 13..). Funkcije fwrite upisuje u datoteke na koju pokazuje fp nobj objekata dimenzije size iz polja na koje pokazuje ptr.. u znakovni c zapis koji se moˇe ˇitati. iz koje se ˇita. s .) je ekvivalentno s fprintf(stdout. u kojem se ono nalaze u raˇunalu..). int fscanf(FILE *fp. Vrijednost koju s funkcija vra´a je broj uˇitanih objekata. fscanf vra´a broj uˇitanih objekata ili EOF ako je doˇlo do greˇke ili kraja c c s s datoteke. size t fwrite(const void *ptr.222 POGLAVLJE 13. size t nobj."\nUlazna greska.3. const char *format. size t size.)..) je ekvivalentno s fscanf(stdin..... Konaˇno. z c Funkcije fread ˇita iz datoteke na koju pokazuje fp nobj objekata dic menzije size i smjeˇta ih u polje na koje pokazuje ptr. FILE *fp)... Da bi se ispitao uzrok treba koristiti s s funkcije ferror i feof.3. printf(. size t nobj. Taj broj moˇe biti manji od nobj c c z ako je doˇlo do greˇke ili kraja datoteke. Ove funkcije vrˇe binarno ˇitanje i pisanje. Taj broj moˇe biti manji od nobj ako je doˇlo do z s greˇke.. exit(2). size t size. To znaˇi da ne dolazi do konverzije s c c podataka iz binarnog zapisa. } return 0. DATOTEKE fprintf(stderr.. Vrijednost koju funkcija vra´a c je broj upisanih objekata.5 Binarni ulaz/izlaz Za zapisivanje struktura mogu se pokazati pogodnije sljede´e funkcije: c size t fread(void *ptr. 13..).. fprintf vra´a broj upisanih znakova ili negativan broj u sluˇaju c c greˇke. const char *format. One su identiˇne funkcijama printf i scanf s jedinom razlikom da im je prvi c c s c argument pokazivaˇ na datoteku u koju se vrˇi upis odn.4 Formatirani ulaz/izlaz Za formatirani ispis u datoteku i upis iz datoteke moˇemo koristiti funkcije z int fprintf(FILE *fp.\n"). a s c scanf(. FILE *fp)...

dat\n")."Greska pri upisu u 1. exit(-1). }.i<SIZE. #include <stdio.\n").h> #include <stdlib. 1.dat".1000L.sizeof(lista[i])."wb"))==NULL){ fprintf(stderr. char name[64]. return EXIT_SUCCESS. long total.h> #define SIZE 2 struct account{ short count."Marko"}}. int i. int n) { . Sadrˇaj datoteke moˇe biti izlistan sljede´om funkcijom. } Program u jednoj for petlji upisuje listu struktura account u datoteku."Error: nije moguce otvoriti" " datoteku 1.3. int main(void) { struct account lista[SIZE]={{3.2000L.++i){ if(fwrite(&lista[i]. {7.dat. exit(1).ˇ 13. if((fp=fopen("1. } } fclose(fp). FUNKCIJE ZA CITANJE I PISANJE 223 Primjer upotrebe je zapis strukture u datoteku dan je u sljede´em proc gramu. fp) != 1){ fprintf(stderr."Ivo"}. z z c void Print(const char *file. FILE *fp. } for(i=0.

U svakom sluˇaju izlaz iz c c . Osnovni nedostatak funkcija za binarni ulaz/izlaz je zavisnost o arhitekturi raˇunala i prevodiocu."Read.sizeof(elm). } Poziv funkcije bi bio npr.224 FILE *fp. DATOTEKE elm. Naime. error: nije moguce otvoriti" " datoteku %s. } printf("%d : count = %d. total = %ld. 1. if((fp=fopen(file.3. long offset. } fclose(fp). name = %s\n". exit(1). struct account int i. } for(i=0.file). oblika Print("1."Greska pri citanju iz %s\n". dok je kod teksz tualne datoteke ta veliˇina ovisna o implementaciji. c c int fseek(FILE *fp. exit(-1). POGLAVLJE 13. fp) != 1){ fprintf(stderr.count.name). na razliˇitim raˇunalima binarni zapis iste c c c strukture podataka moˇe biti razliˇit ˇto onemogu´ava da podaci zapisani na z c s c jednom stroju budu ispravno proˇitani na drugom. Sljede´e dvije funkcije nam omogu´avaju direktan pristup. i. Kod binarnih datoteka to je broj c z znakova koji prethode trenutnom poloˇaju unutar datoteke. elm.6 Direktan pristup podacima Pomo´u do sada uvedenih funkcija podacima moˇemo pristupiti samo c z sekvencijalno. Prednosti su brzina i c kompaktnost zapisa. long ftell(FILE *fp)."rb"))==NULL){ fprintf(stderr.\n".2). int pos).total. 13.++i){ if(fread(&elm. elm. return. Funkcija ftell uzima kao argument pokazivaˇ na otvorenu datoteku i c vra´a trenutni poloˇaj unutar datoteke.i<n.file). elm.dat".

3. Trce´i parametar pos indicira od z c kog mjesta se mjeri offset (odmak). exit(-1). if((fp=fopen(file. pos. U datoteci stdio. fseek(fp. pos. U sluˇaju c greˇke. FUNKCIJE ZA CITANJE I PISANJE 225 funkcije ftell mora dati dobar drugi argument za funkciju fseek. SEEK SET) Znaˇenje c Idi na poˇetak datoteke c Idi na kraj datoteke Ostani na teku´oj poziciji c Idi na poziciju je dao prethodni poziv ftell() funkcije Nova pozicija u datoteci offset znakova od poˇetka datoteke c offset znakova od trenutne pozicije u datoteci offset znakova od kraja datoteke Standard postavlja neka ograniˇenja na funkciju fseek. ftell ´e vratiti vrijednost -1L. SEEK_SET).ˇ 13."Povecaj. Poziv fseek(fp. te dva paramec tra koji specificiraju poloˇaj u datoteci. int n) tmp. fseek(fp. 0L. fseek(fp. c void Povecaj(const { FILE *fp. error: nije moguce otvoriti" " datoteku %s. struct account long int char *file. Tako za binarne c datoteke SEEK END nije dobro definiran. SEEK CUR) ftell pos. } pos = n*size.file). size=sizeof(struct account). . SEEK END) 0L. fseek(fp. koje imaju sljede´e c c znaˇenje: c pos SEEK SET SEEK CUR SEEK END Na primjer. SEEK CUR i SEEK END. SEEK SET) 0L.\n". Napiˇimo sada funkciju koja ´e u datoteci potraˇiti odredeni account i s c z pove´ati njegovo polje total za 100."r+b"))==NULL){ fprintf(stderr.h su definirane tri simboliˇke konstante: SEEK SET. s c Funkcija fseek uzima pokazivaˇ na otvorenu datoteku. ako je ono manje od 5000. dok za tekstualne datoreke jedino su pozivi iz prethodne tabele dobro definirani.

exit(1). a zatim se pokazivaˇ c c pozicionira na poˇetak n+1-og zapisa (fseek(fp. fp) != 1){ fprintf(stderr. fp) != 1){ fprintf(stderr. 1. exit(EXIT_FAILURE). if(fwrite(&tmp.\n"). SEEK CUR)."Greska pri upisu. size.) i onda c c vrˇimo upis. return. DATOTEKE if(fread(&tmp. } Funkcija mijenja sadrˇaj n+1-og zapisa (brojanje zapisa poˇinje od nule). } } fclose(fp). 1.\n"). -size.total += 100. } if(tmp. z c Prvo se otvara datoteka za ˇitanje i upisivanje (r+b). SEEK_CUR).total < 5000L) { tmp. pos. Nakon ˇto je c c s zapis promijenjen. U tu svrhu se prvo vra´amo na poˇetak n+1-og zapisa (fseek(fp. s . fseek(fp. SEEK SET).226 POGLAVLJE 13. size. -size. Zapis c se proˇita i ako je total manji od 5000 poveˇava se za 100."Greska pri citanju.). treba ga upisati nazad u datoteke.

| i ^ uzimaju dva operanda i vrˇe operacije na s bitovima koji se nalaze na odgovaraju´im mjestima. Definicije operacija dane su u sljede´oj tabeli: c c b1 1 1 0 0 b2 1 0 1 0 b1 & b2 1 0 0 0 b1 ^ b2 0 1 1 0 b1 | b2 1 1 1 0 Operacije ilustriramo s nekoliko promjera.1 Operatori Programski jezik C ima jedan broj operatora ˇije je djelovanje definic rano na bitovima.Poglavlje 14 Operacije nad bitovima 14. short. zatim na sljede´em najmanje c c znaˇajnom mjestu itd. int i long. To su sljede´i operatori c Operator & | ^ << >> ∼ Znaˇenje c logiˇko I bit-po-bit c logiˇko ILI bit-po-bit c ekskluzivno logiˇko ILI bit-po-bit c lijevi pomak desni pomak 1-komplement Prve tri operacije &. Logiˇko I: c a = 0100 0111 0101 0111 b = 1101 0100 1010 1001 a & b = 0100 0100 0000 0001 227 . Takvi se operatori mogu primijeniti na cjelobrojne tipove podataka char. Usporeduju se bitovi na c najmanje znaˇajnom mjestu u oba operanda.

a sve ostale bitove varijable b staviti na nulu. dovoljno je napraviti z logiˇko I s konstantom koja na traˇenom mjestu ima nulu. a sve z ostale postaviti na jedan. Na primjer. Pretpostavimo da su variz c c c jable tipa int i da je int kodiran u 16 bitova. a ostale postavili na nulu morali c s bismo izvrˇiti operaciju b=a & 0xfc00 (provjerite). pretpostavimo da ˇelimo ˇest najmanje znaˇajnih bitova iz variz s c jable a kopirati u varijablu b. Tada prvo definiramo masku. konstantu koja ima sljede´i raspored bitova: c mask = 0000 0000 0011 1111 Odmah se vidi da je mask=0x3f. ali taj naˇin je zavisan o s c ˇirini tipa int. Njega ´emo c c c z z c iskoristiti kada ˇelimo iz jedne varijable u drugu kopirati dio bitova. Ako c z u nekoj varijabli a ˇelimo postaviti na nulu neki bit. a = 0100 0111 0101 0111 mask = 1111 1101 1111 1111 a & mask = 0100 0101 0101 0111 Ovdje smo deseti bit postavili na nulu. da smo htjeli izdvojiti dio najznaˇjnijih c bitova (lijevo pozicioniranih) morali bismo precizno znati duljinu tipa int. To moˇemo posti´i pomo´u logiˇkog I operatora. Na primjer. Logiˇko ILI na sliˇan naˇin moˇe posluˇiti maskiranju bitova.228 POGLAVLJE 14. OPERACIJE NAD BITOVIMA Logiˇko ILI: c a = 0100 0111 0101 0111 b = 1101 0100 1010 1001 a | b = 1101 0111 1111 1111 Ekskluzivno logiˇko ILI: c a = 0100 0111 0101 0111 b = 1101 0100 1010 1001 a ^ b = 1001 0011 1111 1110 Logiˇki operatori najˇeˇ´e sluˇe maskiranju pojedinih bitova u operandu. c Na primjer. Uoˇimo da je maska neovisna o broju bitova c koji se koristi za tip int. c c sc z Maskiranje je transformacija binarnog zapisa u varijabli na odreden naˇin. ako ˇelimo izdvojiti ˇest najmanje z s znaˇajnih bitova trebamo napraviti c a = 0100 0111 0101 0111 mask = 1111 1111 1100 0000 a | mask = 1111 1111 1101 0111 . a na svim ostalim c z jedinice. s Logiˇko I takoder sluˇi postavljanjem na nulu odredenih bitova. Naravno. Da bismo izdvojili najznaˇajnijih ˇest bitova. Operacija koju trebamo napraviti je a = 0100 0111 0101 0111 mask = 0000 0000 0011 1111 a & mask = 0000 0000 0001 0111 i stoga imamo b=a & 0x3f.

Na primjer a = 0110 0000 1010 1100 a >> 6 = 0000 0001 1000 0010 . a = 0100 0111 0101 0111 mask = 0000 0000 0011 0000 a ^ mask = 0100 0111 0110 0111 Vidimo da je rezultat da su bitovi 5 i 6 promijenili svoju vrijednost. onog tipa koji se koristi) no to se moˇe izbje´i pomo´u 1-komplementa. Pomak nadesno >> takoder djeluje nad prvim operandom dok je drugi operand broj bitova za koji treba izvrˇiti pomak. Opec rator pomaka nalijevo << djeluje na sljede´i naˇin: prvi operand mora biti c c cjelobrojni tip nad kojim se operacija vrˇi. Bitovi na desnoj strani s (najmanje znaˇajni) pri tome se gube. Ova operacija ovisi o duljini tipa int (odn. Ako ponovimo operaciju a = 0100 0111 0110 0111 mask = 0000 0000 0011 0000 a ^ mask = 0100 0111 0101 0111 dolazimo do polazne vrijednosti.1. a drugi operand je broj bitova za s koji treba izvrˇiti pomak (unsigned int). a sa desne strane se mjesta popunjavaju nulama. Pri tome. Na primjer. Operatori pomaka pomi´u binarni zapis broja nalijevo ili nadesno. z c c Unarni operator 1-komplement (∼) djeluje tako da jedinice pretvara u nule u nule u jedinice: ~0101 1110 0001 0101 = 1010 0001 1110 1010 odnosno. dok se na desnoj strani uvode novi c bitovi. U prethodnom primjeru bismo stoga pisali b=a | ~0x3f. Ekskluzivno ILI moˇemo koristiti za postavljanje odredenih bitova na 1 z ako su bili 0 i obratno. Pri tome se 6 najznaˇajnijih bitova c c gubi. ∼0x5e15=0xa1ea. onda s se na lijevoj strani uvode nule. OPERATORI 229 i stoga imamo b=a | 0xffa. ako je varijabla na kojoj se pomak vrˇi tipa unsigned. Logiˇko ILI evidentno moˇemo koristiti za stavljanje pojedinih bitova na c z jedan. Na primjer.14. Drugi operand ne smije premaˇiti s s broj bitova u lijevom operandu. b=a<<6 ima sljede´i efekt: c a = 0110 0000 1010 1100 a << 6 = 0010 1011 0000 0000 Sve se bitovi pomi´u za 6 mjesta ulijevo.

/* duljina tipa int */ mask=0x1 << (nbits-1). a za pozitivan broj nulama.i<=nbits.&a). /* 1 na najznacajnijem mjestu*/ printf("\nUnesite cijeli broj: "). if(i % 4 == 0) printf(" "). scanf("%d". S druge strane. } Program prvo odredi duljinu tipa int pomo´u sizeof operatora i zatim c inicijalizira unsigned varijablu mask tako da bit 1 stavi na najznaˇajnije c mjesto. unsigned mask. return 0.++i) { b=(a & mask) ? 1 : 0.230 POGLAVLJE 14.b.b). for(i=1. neki prevodioci ´e uvijek popunjavati ispraˇnjena mjesta nulama. To znaˇi da ´e za negativan broj ispraˇnjeni bitovi na lijevoj strani c c z biti popunjavani jedinicama. .i. } printf("\n"). nbits=8*sizeof(int).nbits. OPERACIJE NAD BITOVIMA Ako je varjabla a cjelobrojni tip s predznakom onda rezultat moˇe ovisiti o z implementaciji. printf("%d". #include <stdio. Tada je izraz a &= 0x7f a ^= 0x7f a |= 0x7f a <<= 5 a >>= 5 ekvivalentan izraz a = a & 0x7f a = a ^ 0x7f a = a | 0x7f a = a << 5 a = a >> 5 vrijednost 0x37 0x6dc8 0x6dff 0xb6e0 0x36d Na kraju pokaˇimo program koji ispisuje binarni zapis cijelog broja tipa z int. mask >>= 1.h> int main(void) { int a. ve´ina prevodioca ´e na lijevoj strani uvesti bit predznaka c c broja. c z Logiˇki operatori uvedeni u ovom poglavlju formiraju operatore pridruˇivanaja c z &= ^= |= <<= >>= Neka je a=0x6db7. a sve ostale bitove stavi na nulu.

2. . c c i d. U naredbi mask >>= 1. 2 i 1 bit. clan 2.. clan n. Prema tome zauzimaju 7 bitova. c Pojedine ˇlanove polja bitova moˇemo dohvatiti istom sintaksom kao i c z kod struktura..a.b itd. Prva deklaracija definira strukturu razbijenu u ˇetiri polja bitova.. c c c s c Jednostavan program koji upotrebljava polje bitova je sljede´i: c #include <stdio.h> .. POLJA BITOVA 231 Operacija (a & mask) ? 1 : 0 dat ´e 1 ako je na maskiranom mjestu c bit jednak 1. Poredak tih bitova unutar jedne kompjutorske rijeˇi ovisi o implementaciji. 14. }. Na primjer. 3. dakle v.14. struct primjer v. Deklaracija polja bitova posve je sliˇna deklaraciji c c strukture: struct ime { clan 1. Ako broj bitova deklariran u polju bitova nadmaˇuje jednu kompjutorsku s rijeˇ. s tom razlikom da ˇlanovi strukture imaju drugaˇije znaˇenje nego u obiˇnoj c c c c strukturi. b. a b c d : : : : 1. Sintaksa je takva da iz imena varijable dolazi dvotoˇka i c c broj bitova koji ˇlan zauzima. 2. maskirni bit pomi´emo s najznaˇajnijeg mjesta prema najmanje znaˇajnom c c c i tako ispitujemo sve bitove u varijabli. za pam´enje polja bit ´e upotrebljeno viˇe kompjutorskih rijeˇi. a. Svaki ˇlan polja bitova predstavlja jedno polje bitova unutar komc pjutorske rijeˇi. odnosno nulu ako je maskirani bit 0. 1. v.2 Polja bitova Polja bitova nam omogu´uju rad s pojedinim bitovima unutar jedne c kompljutorske rijeˇi. Ta polja imaju duljinu 1. 3. c struct primjer { unsigned unsigned unsigned unsigned }.

v. } v={1.c.d=%d\n". return 0.3.b=%d.v. unsigned c : 5. printf("v treba %d okteta\n". 0. v. unsigned : 5. c c c c #include <stdio.d). return 0. } c z c Poredak polja unutar rijeˇi moˇe se kotrolirati pomo´u neimenovanih ˇlanova unutar polja kao u primjeru c struct { unsigned a : 5. printf("v treba %d okteta\n".a=%d.v.b=%d.a. 5. sizeof(v)).b.c).v.4}.c=%d.2.2. v. v. struct primjer v.v.c=%d\n". OPERACIJE NAD BITOVIMA int main(void) { static struct{ unsigned a : 5. unsigned b : 5. v. printf("v. unsigned d : 5. Na primjer.v. unsigned b : 5.a=%d.v. printf("v.h> int main(void) { static struct{ unsigned a : unsigned b : : unsigned unsigned c : } v={1.3}.a. Neimenovani ˇlan ˇija je ˇirina deklarirana kao 0 bitova tjera prevodilac c c s da sljede´e polje smjesti u sljede´u raˇunalnu rijeˇ. sizeof(v)). } .b.232 POGLAVLJE 14.v. unsigned c : 5. 5. }. 5.

Adresni operator se c ne moˇe primijeniti na ˇlana polja bitova niti se ˇlan moˇe dose´i pomo´u z c c z c c pokazivaˇa. Clanovi mogu biti tipa unsigned i tipa int. c .14. POLJA BITOVA 233 Polja bitova mogu se koristiti kao i druge strukture s odredenim ograniˇ ˇenjima.2.

h> <complex.h> <fenv.h> <stdarg.h> <iso646.h> <string.h> <ctype.h> <stdio.h> <stdlib.h> <inttypes.1 ANSI zaglavlja <assert.Dodatak A A.h> <limits.h> <stddef.h> 234 .h> <signal.h> <wchar.h> <float.h> <setjmp.h> <stdint.h> <math.h> <locale.h> <tgmath.h> <time.h> <stdbool.h> <errno.h> <wctype.

59 | [ 91 | { 123 (fs) 28 | < 60 | \ 92 | | 124 (gs) 29 | = 61 | ] 93 | } 125 (rs) 30 | > 62 | ^ 94 | ~ 126 (us) 31 | ? 63 | _ 95 | (del) 127 . 44 | L 76 | l 108 (cr) 13 | 45 | M 77 | m 109 (so) 14 | .2 ASCII znakovi Char Dec | Char Dec | Char Hex | Char Dec -----------------------------------------------(nul) 0 | (sp) 32 | @ 64 | ‘ 96 (soh) 1 | ! 33 | A 65 | a 97 (stx) 2 | " 34 | B 66 | b 98 (etx) 3 | # 35 | C 67 | c 99 (eot) 4 | $ 36 | D 68 | d 100 (enq) 5 | % 37 | E 69 | e 101 (ack) 6 | & 38 | F 70 | f 102 (bel) 7 | ’ 39 | G 71 | g 103 (bs) 8 | ( 40 | H 72 | h 104 (ht) 9 | ) 41 | I 73 | i 105 (nl) 10 | * 42 | J 74 | j 106 (vt) 11 | + 43 | K 75 | k 107 (np) 12 | .A.2. ASCII ZNAKOVI 235 A. 46 | N 78 | n 110 (si) 15 | / 47 | O 79 | o 111 (dle) 16 | 0 48 | P 80 | p 112 (dc1) 17 | 1 49 | Q 81 | q 113 (dc2) 18 | 2 50 | R 82 | r 114 (dc3) 19 | 3 51 | S 83 | s 115 (dc4) 20 | 4 52 | T 84 | t 116 (nak) 21 | 5 53 | U 85 | u 117 (syn) 22 | 6 54 | V 86 | v 118 (etb) 23 | 7 55 | W 87 | w 119 (can) 24 | 8 56 | X 88 | x 120 (em) 25 | 9 57 | Y 89 | y 121 (sub) 26 | : 58 | Z 90 | z 122 (esc) 27 | .

Najmanji cijeli broj veci od x double floor(double x) -.arcsin(x) atan(double x) -.tg(x) double double double double acos(double x) -.3 Matematiˇke funkcije c double cos(double x) -.x^y.ln(x).th(x) double exp(double x) -.arccos(x) asin(double x) -.e^x double pow (double x.|x| labs(long n) -.Najveci cijeli broj manji od x .ch(x) double sinh(double x) -.236 DODATAK A.|n| double sqrt(double x) -.sh(x) double tanh(double x) -.korijen double ceil(double x) -.sin(x) double tan(double x) -. double y) -. double log10 (double x ) -. double log(double x) -.arctg(y/x) double cosh(double x) -.log(x) double fabs (double x ) -. A. double x) -.arctg(x) atan2(double y.cos(x) double sin(double x) -.

The C Programming Language. 8036/8026 Assembly Language Programming. [4] B.Bibliografija [1] C-faq. SAMS. Berkeley.com/ scs/C-faq/top. III. W. Advanced Programming in the UNIX Enviroment. 2002. Prata. 2002. S. A Reference Manual. [3] S. 1992. [6] S. R. 1988. 2nd Edition. Murray. D. Schaum’s outline series. McGraw-Hill. 1986. G. Steele Jr. L. Fifth Edition.html. Harbison III. C Primer Plus.. C. 1996. [2] B. http://www. Kernighan. 237 . Prentice Hall. Theory and Problems of Programming with C. Addison Wesley. Gottfried. P. [7] W. 4th edition. M. H. H. C.eskimo. Osborne McGraw-Hill. [5] W. Pappas. Ritchie. Stevens.

Sign up to vote on this title
UsefulNot useful