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

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

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

. . . . . 234 A. . . . . . . . . . . . . . . . .2 ASCII znakovi . . . . . . . . . . .ˇ SADRZAJ A 7 234 A. . . . . . . . . . .3 Matematiˇke funkcije . . . . . . 236 c . . . . . 235 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 s 1. To su tekstualne datoteke koje programer o kreira zajedno s . U ovoj skripti bit ´e opisan dio najˇeˇ´e koriˇtenih funkcija iz standardne bic c sc s blioteke. 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. exit. c 1 Koristi se joˇ i naziv punjaˇ. Pri tome nam pomaˇe programski alat koji se naziva make. Dobiveni se kˆd naziva objektni kˆd ili o o 1 objektni program.c sadrˇe dijelove C programa odnosno izvorni kˆd z o (eng.c datoteke zajedno.c datoteku prevesti u objektni kˆd.5 Linker Sloˇeniji C programi sastoje se od viˇe datoteka s ekstenzijama .2.2. koje su pohranjene u standardnim bibliotekama funkcija. UVOD 1.c datoteku generira strojni kˆd i smjeˇta ga u datoo s teku istog imena ali s ekstenzijom .c datoteku prevodilac prevodi u strojni jezik no time se joˇ ne dobiva izvrˇni program jer njega ˇine tek sve . eventualno i s vanjskim bibliotekama. s s c Prevodilac za svaku . 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.6 make Pri prevodenju u izvrˇni kˆd programa koji je razbijen u viˇe datoteka s o s potrebno je svaku pojedinu .12 POGLAVLJE 1.h nazivaju se datoteke zaglavlja (eng.2. Prevodilac ´e nakon generiranja c objektnog kˆda automatski pozvati linker tako da njegovo neposredno pozio vanje nije potrebno. source code). z s fgets itd.c datotekama. Zadatak je linkera kreirati izvrˇni program povezivanjem s svih objektnih programa u jednu cjelinu.7 Programske biblioteke Svaki sloˇeniji C-program koristi niz funkcija kao ˇto su printf.c i . header files) i sadrˇe informacije potrebne prevodiocu za prevodenje z izvornog kˆda u strojni jezik.o. a zatim sve o objektne datoteke povezati. z s Datoteke s ekstenzijom . Datoteke s ekstenzijom .h. s c . Ukoliko povezivanje objektnog kˆda nije bilo uspjeˇno o s linker ´e dati poruke o greˇkama. Svaku pojedinu . 1.

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

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

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

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

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

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

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

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

i kontrola programa se vra´a na testiranje istinitosti izraza i<=n. 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++..i=i+1) { . c Stvarni argument funkcije scanf je memorijska adresa varijable n.i<=n. n--. Nakon toga se izvrˇava naredba c s i=i+1. Stoga svi argumenti funkcije scanf. Zatim se testira izraz i<=n. Inicijalizacija se izvrˇi samo s z s jednom. } Naredbe oblika i=i+1 i i=i-1 kojima se brojaˇ pove´ava.0/(i*(i+1)). while(i<=n) { suma=suma+1.2. Prvo se varijabla i inicijalic c zira tako ˇto joj se pridruˇi vrijednost 1 (i=1). Naredba for(i=1. i=i+1. FOR PETLJA 21 Njen prvi argument je niz znakova koji sadrˇi znakove konverzije. for-petlja iz naˇeg primjera moˇe se zapisati pomo´u while-petlje na s z c ovaj naˇin: c i=1. osim prvog. Program se tada nastavlja s z prvom naredbom iza tijela petlje. Adresa varijable dobiva se pomo´u adresnog operatora & c (&n je adresa varijable n). 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). a ne sama varijabla n.3. Petlja c zavrˇava kad izraz koji se testira postane laˇan. . moraju imati znak & ispred sebe. Ako je rezultat testa istinit izvrˇavaju se naredbe iz tijela petlje (naredbe u s vitiˇastim zagradama).. } je for petlja.. Ona djeluje na sljede´i naˇin.

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

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

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

} else{ x1=-b/(2 * a). x2 y1=sqrt(-d)/(2 * a). Na primjer. } else if (uvjet2){ naredba2. y2 } a). = x1. Proizvoljan broj if naredbi moˇemo ugnijezditi z tako da dobijemo viˇestruku if naredbu. izvrsava se dio koda d=b*b-4*a*c. . pomo´u dvije if nas c redbe dobivamo naredbu oblika if(uvjet1){ naredba1. s Ako je uvjet a != 0. Prvo se raˇuna diskriminanta jednadˇbe d.b . } naredba4. pozitivna. 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. x2=x1. 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. a).b/(2 * a).0 provjeravamo je li jednaˇba kvadratna ili z nije.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. jednaka nuli ili negativna.4. dok se u protivnom c s prijelazi na sljede´u instrukciju.b + sqrt (d))/(2 * x2=(.2.0 ispunjen. IF NAREDBA if(uvjet) naredba1.y1. = . if (d > 0) { x1=(. } else{ naredba3. c Testiranjem uvjeta a != 0. a onda se ulazi u jednu viˇestruku c z s naredbu uvjetnog granjnja u kojoj se ispituje je li diskriminanta. 25 pri ˇemu se naredba1 izvrˇava ako je uvjet ispunjen.

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

1) Funkcija treba uzeti dva cjelobrojna parametra n i k te vratiti broj n .5. Pascalov trokut prikazan niˇe. } . 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. int i. return rezult. */ long binom(int n.i--) rezult=rezult*i. 1 · 2···k k 27 (2. int k) { long rezult=1. if(n == k) return 1. U k glavnom programu treba zatim ispisati tzv. if(k == 0) return 1.++i) rezult=rezult/i. for(i=n.h> /* Funkcija binom() racuna binomni koeficijent. c #include <stdio. for(i=1. z U liniji s indeksom n ispisani su brojevi n .i<=k.2. FUNKCIJE Treba napisati funkciju koja raˇuna binomni koeficijent c n n · (n − 1) · · · (n − k + 1) = . za sve vrijednosti k od 0 do n.i>n-k.

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

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

c s s 2. Nakon ˇto funkcija binom izvrˇi return naredbu. } deklarirane su varijable bnm (tipa long). a svaka vrijednost s s razliˇita od nule signalizira zavrˇetak uslijed greˇke.n<10. n i k (tipa int).k++){ bnm=binom(n. Argumenti koji su deklarirani u definiciji funkcije binom nazivaju se formalni argumenti.30 POGLAVLJE 2.k). printf("%ld ". Nakon zavrˇetka main vra´a s c cjelobrojnu vrijednost operacijskom sustavu koja ima znaˇenje izlaznog stac tusa.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. Prilis kom poziva funkcije vrijednosti stvarnih argumenata se kopiraju u formalne argumente. } printf("\n").n++){ for(k=0. vrijednost koja s s stoji uz return se kopira u varijablu bnm koja stoji na lijevoj strani jednakosti. Nula se interpretira kao uspjeˇni zavrˇetak programa.5. Stoga c . ali to nije nuˇno budu´i da se radi o posve razliˇitim varijablama. } return 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.5. OSNOVE PROGRAMSKOG JEZIKA C for(n=0. Program se zatim nastavlja ispisivanjem izraˇunate vrijednosti bnm. 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. Varijable n i k deklarirane u main su stvarni argumenti funkcije binom. Nju poziva operac s cijski sustav kad korisnik pokrene program.k<=n. c Kako se radi o varijabli tipa long. za ispis u printf naredbi koristimo znak konverzije %ld. k kao i stvarnim argumentima.k). Njima smo dali ista imena n. Funkcija binom se poziva unutar dvostruke for petlje u naredbi bnm=binom(n.bnm).

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

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

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

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

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

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

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

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

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

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

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

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

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

c=niz_znakova[i]. } 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. i++. i++. a 0 laˇ. 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. To je razlog zbog c z kojeg bi u prethodnom primjeru varijabla c dobila vrijednost 0 ili 1. c c c Funkcija inv sastoji se od jedne for petlje for(i=0. i<j. while(c !=’\0’) { if(c>=’0’ && c<=’9’) ++brojac. oba ova oblika pove´avaju varijablu za jedan. c=niz_znakova[0]. Uoˇimo da smo pisali ++brojac c c umjesto brojac++. z Gornja while petlja mogla je biti napisana i na sljede´i naˇin: c c i=0. OSNOVE PROGRAMSKOG JEZIKA C je istina ako varijabla c ne sadrˇi nul-znak ’\0’.44 c!=’\0’ POGLAVLJE 2.j=strlen(s)-1.j--) . 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. 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’. Pri tome su bitne zagrade je operatori pridruˇivanja imaju z najniˇi prioritet. Pri s tome koristimo ˇinjenicu da su brojevi u skupu znakova poredani u prirodnom c poretku (isto vrijedi i za slova engleske abecede). Naredbom if(c>=’0’ && c<=’9’) ++brojac. Operatore inkrementiranja i dekrementiranja moˇemo z pisati prije i poslije varijable.

a j se smanjuje. . Pri tome posljednji nul-znak nismo pomicali s njegovog mjesta. s[i]=s[j]. 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. Petlja se izvrˇava sve dok je izraz i<j istinit.8.2. Pri toj zamjeni morali smo koristiti pomo´nu c varijablu c. Unutar s tijela petlje vrˇi se zamjena znakova s c=s[i]. Viˇetruke inicijalizacije odvajaju s se zarezom. Sliˇno. s[j]=c. koji su na pozicijama i i j. Ove se dvije naredbe ponovo c c odvajaju zarezom. u dijelu u kome se mijenja brojaˇ petlje mijenjaju je c c oba brojaˇa: i se pove´ava. Evidentno je da ´e na izlazu iz for petlje poredak znakova u c znakovnom nizu biti invertiran.

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

kˆd o /* x=72. 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.0.0. s Jednako tako ne moˇemo komentirati dio programa koji ve´ sadrˇi komentar. x=72. // brojac znakova Prevodilac ignorira tekst od znaka // do kraja linije. Sastoje se od slova i brojeva u bilo kojem c poretku s ograniˇenjem da prvi znak mora biti slovo. */ prvi komentar smo zaboravili zatvoriti. /* Inicijalizacija */ Prevodilac ´e javiti sintaktiˇku greˇku jer zadnji */ graniˇnik nema odgovac c s c raju´eg /* graniˇnika. U primjeru s c /* Ovo je prvi komentar.3. ˇto rezultira gubitkom naredbe x=72. ˇto znaˇi u liniji c s c x=72. int n. z c z Na primjer. odnosno 31 (C99). /* 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). c Na primjer.. /* Inicijalizacija */ 3. najbolje z je koristiti preprocesor (vidi Poglavlje 8). */ je neispravan jer komentar zapoˇet prvim /* graniˇnikom zavrˇava s prvim c c s */ graniˇnikom. Stariji prevodioci ne prihva´aju ovaj naˇin komentiranja.0. Prevodilac ´e ignorirati text sve c do prvog znaka */ na koji naide.ˇ ˇ 3. y=31. c c Standard C99 omogu´ava koriˇtenje drugog tipa komentara koji dolazi iz c s Jave i C++a. .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. U standardu C99 ta je granica dignuta na 63 znaka.0.0. Komentar zapoˇinje znakom // i prostire se do kraja linije. Duljina imena je proizvoljna premda je prema standardu C90 prevodilac duˇan prez poznati samo prvih 31 znakova. Velika i mala slova se razlikuju i znak (underscore) smatra se slovom. c c Ako treba komentirati veliki dio programa koji u sebi sadrˇi brojne komentare.

kljuˇne rijeˇi. 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. KONSTANTE I VARIJABLE Primjer 3.1 Primjeri pravilno napisanih identifikatora: x names y13 Pov1 sum_1 table _temp TABLE Primjer 3. far fortran huge near pascal treba izbjegavati jer su rezervirana za varijable i . tzv.48 POGLAVLJE 3. 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.

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

long). ˇto je u danaˇnje vrijeme uglavnom c c c s s 32 bita.50 POGLAVLJE 3. • LONG MIN. Oni pokrivaju stoga pribliˇno dvostruko ve´i raspon pozitivnih cijelih z c brojeva od osnovnih tipova. 1 2 Na Unix sustavima ta se datoteka najˇeˇ´e nalazi u /usr/include/ direktoriju. s s . • UINT MAX: Maksimalna vrijednost za tip unsigned.h> u kojoj su graniˇne vrijednosti definirane. s Koje su minimalne i maksimalne vrijednosti cjelobrojnih varijabli pojedinih tipova? Odgovor je zavisan o raˇunalu.U i UL su objaˇnjene u sekciji 3. Na nekom c raˇunalu dio sadrˇaja datoteke <limits. Tip int je prirodan cjelobrojni podatak za dano raˇunalo.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. a mogu reprezentirati samo pozitivne cijele brojeve (ukljuˇivˇi c s nulu). Koriˇtenje ˇirih cjelobrojnih tipova moˇe stoga imati za posljedicu s s z sporije izvrˇavanje programa. SHRT MAX: Minimalna i maksimalna vrijednost za tip short. • ULONG MAX: Maksimalna vrijednost za tip unsigned long.6. Stoga postoji sistemska datoteka c 1 zaglavlja <limits. INT MAX: Minimalna i maksimalna vrijednost za tip int. • USHRT MAX: Maksimalna vrijednost za tip unsigned short. LONG MAX: Minimalna i maksimalna vrijednost za tip long. c sc Zaˇto su negativne vrijednosti u zagradama objaˇnjeno je u sekciji 8.) Ovdje su s • SHRT MIN. • INT MIN. 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. short. Za njegovo c pam´enje koristi se jedna raˇunalna rijeˇ.

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

x_max+1). slovo A (veliko) ima ASCII kˆd 65. printf("LONG_MAX+1 = %ld\n".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.s_max+1).s_min-1). . Znac kovi se u raˇunalu kodiraju i sve manipulacije sa znakovima obavljaju se c c putem njihovih numeriˇkih kodova.i_min-1). Zbog toga je tip char ustvari cjelobrojni tip podataka vrlo malog raspona.us_max+1). B ima kˆd 66 itd.2 Znakovni podaci Podatak tipa char namijenjen je pam´enju individualnih znakova. 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).l_min-1). c c ASCII kˆd.i_max+1). printf("ULONG_MAX+1 = %lu\n". kad se doda dva dobiva se jedan itd. I ovdje su c c brojevi “poredani u krug”. gdje je n ˇirina tipa (u bitovima). 3. printf("USHRT_MAX+1 = %hu\n". printf("INT_MAX+1 = %d\n". s Kod brojeva s predznakom imamo sliˇno ponaˇanje. Npr. Jedan od najstarijih je tzv. odnosno implementirana je aritmetika modulo 2n .52 POGLAVLJE 3. printf("INT_MIN-1 = %d\n". Brojevi bez predznaka su dakle “poredani u krug”. s c Standard C99 uvodi dodatne tipove cijelih brojeva long long i unsigned long long. KONSTANTE I VARIJABLE Uoˇite kontrolne znakove s kojima se ispisuju pojedini integralni tipovi. Postoje razliˇiti naˇini kodiranja znakova.4. 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". Dodavanje jedinice c s najve´em pozitivnom broju daje najve´i negativni broj. ISO Latin 2 (za srednjoeuropske jezike) itd. 3 ASCII je kratica od “American Standard Code for Information Interchange”.l_max+1). a oduzimanje jedic c nice od najve´eg negativnog broja daje najve´i pozitivni broj. printf("SHRT_MIN-1 = %hd\n".ui_max+1). printf("UINT_MAX+1 = %u\n". printf("LONG_MIN-1 = %ld\n". Rezultat koji se dobiva je sljede´i: kada se najve´em broju bez predznaka c c doda jedan dobiva se nula. Tip long long mora biti implementiran s minimalno 64 bita. ali ponaˇanje ovisi o naˇinu kodiranja negativnih s c brojeva (2-komplement u naˇem sluˇaju).

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

17549 · 10−38 ≈ 2−126 ≤ |x| ≤ (1 − 2−24 ) · 2128 ≈ 3.225 · 10−308 ≈ 2−1022 ≤ |x| ≤ (1 − 2−53 ) · 21024 ≈ 1. Strojni epsilon je udaljenost izmedu broja 1. On je stoga dobra mjera preciznosti brojevnog sustava.402823466E+38F (+38) 2.h>.798 · 10308 . KONSTANTE I VARIJABLE se danas pridrˇava ve´ina proizvodaˇa hardwarea.40282 · 1038 .175494351E-38F (-37) 3. Ovdje redom imamo: strojni epsilon. Na nekom raˇunalu dio c c sadrˇaja datoteke <float. c . 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.2204460492503131E-16 2. 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. Kao i u sluˇaju cijelih brojeva vrijednosti ovisne o hardwareu stavljene c su u standardnu datoteku zaglavlja.192092896E-07F 1.2250738585072014E-308 (-307) 1. najmanji dekadski eksponent.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.7976931348623157E+308 (+308) Konstante koje poˇinju s FLT odnose se na tip float. c Ovdje ne´emo ulaziti u diskusiji brojeva 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).54 POGLAVLJE 3. Navec dimo samo granice u kojima se ti brojevi kre´u (prema IEEE standardu): U c jednostrukoj preciznosti (float) imamo 1. Ona sadrˇi niz simboliˇkih konstanti koje daju z c razliˇite informacije o realnim tipovima podataka. dok u dvostrukoj (double) vrijedi 2. dok se one s DBL odnose c na tip double.0 i prvog ve´eg broja s pokretnim zarec zom. najmanji (normalizirani) broj s pokretnim zarezom. U sluˇaju brojeva s pokretnim zarezom c to je datoteka <float. najve´i (norc c malizirani) broj s pokretnim zarezom i najve´i dekadski eksponent.

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

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

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

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

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

%Ld\n".60 Konstanta 500000U 123456789L 123456789ul 0123456l 0X50000U -5000ULL 50000ULL POGLAVLJE 3. 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. e=2000000000ULL.c).a).e). printf("unsigned printf("long printf("unsigned long printf("unsigned long printf("long long printf("unsigned long long Znakovne konstante. Znakovne konstante imaju svoje cjelobrojne c vrijednosti koje su odredene naˇinom kodiranja znakova. Cesto koriˇteni posebni znakovi su s . Koriˇtenjem konsc s tanti poput ’a’. %lo\n". Znakovna konstanta je jedan znak u jednostrukim navodnicima. %lu\n". a drugi je neko od slova ˇ ili znakova. d=20000000LL. %lld\n". Npr. = = = = = = %u\n". %llu\n". ’3’ postiˇe se neovisnost o naˇinu kodiranja znakova. c=200000LU. backslash). Znakovi konverzije za printf funkciju vide se u sljede´em primjeru: c unsigned long unsigned long long long unsigned long long a=20U.c).b). ’A’ ’x’ ’5’ ’?’ ’ ’ (uoˇimo da je i bjelina znak). 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. b=2000L.d).

2 5000.2e3l 5000.345E+8F -0. ’\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.0L Kontrolni znakovi u printf funkciji mogu se vidjeti u sljede´em primjeru: c . c 0. Na primjer ’\170’ je znak koji ima kod 170 oktalno.0e+5 . Npr.0 e+5 c nije ispravno napisana konstanta). Realne se konstante mogu pisati i s eksponentom i tada decimalna toˇka nije c potrebna.6. broj 3 × 105 moˇe biti napisan na sljede´e naˇine: z c c 300000. odnosno 120 decimalno (x u ASCII znakovima). KONSTANTE Kod \b \f \n \r \t \v \0 \? \” \’ \\ Na primjer.3. Sliˇno. Eksponent mora biti cijeli broj kome prethodi slove e (malo ili veliko). 3. gdje je ooo troznamenkasti oktalni broj.00fe-3f 1. realna konstanta koja nakraju ima l ili c L je tipa long double: 0. 1. Tip svih realnih konstanti je double. -0. Na primjer. kodovi mogu biti zadani i heksadecimalno u obliku \xoo.3e6 30E4 Uoˇimo da ne smije biti razmaka izmedu mantise i eksponenta (npr. gdje oo c dvoznamenkasti heksadecimalni broj. Ukoliko se ˇeli konstanta tipa float z na kraj joj se dodaje f ili F. Realna konstanta je broj zapisan u dekadskom sustavu s decimalnom toˇkom. 3e5 3E+5 3. Sliˇno.

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

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

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

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..zuto.zeleno=0...clan_n} var_1..bijelo}.. var_2.8. c c ... tabulator=’\t’}.... ili enum boje {plavo=-1.3...var_m... clan_2..var_m..crveno.. var_2.ljubicasto. clan_n=n-1 Varijabla tipa enumeracije deklarira se naredbom enum ime var_1..... 65 Vrijednosti koje se dodijeljuju identifikatorima mogu se modificirati kao u sljede´em primjeru c enum ESC {novi_red=’\n’. ENUMERACIJE clan_1=0 clan_2=1 clan_3=2 . Deklaracije enumeracije i varijabli tog tipa mogu se spojiti: enum ime {clan_1.

Rezultatu dijeljenja odsjecaju se decimalne znamenke kako bi se dobio cjelobrojni rezultat. pokazivaˇe (Poglavlje 11) te operatore koji djeluju na bitovima c (Poglavlje 14). c 66 .Poglavlje 4 Operatori i izrazi U ovom se poglavlju bavimo operatorima i izrazima koji se formiraju pomo´u c operatora i operanada. tj. z c c c 4. Standard C99 je propisao da se i u sluˇaju negativnih c brojeva vrˇi odsjecanje.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. cjelobrojc c nim. realnim i znakovnim operandima (znakovna konstanta ima cjelobrojni tip). • Operacije dijeljenja u sluˇaju cjelobrojnih operanada ima znaˇenje cjec c lobrojnog dijeljenje. Najˇeˇ´e je to odsjecanje. Paˇnju ´emo posvetiti naˇinu izraˇunavanja izraza. Ako bar jedan s z od operanada nije cjelobrojan. tj. zaokruˇivanje c sc z prema nuli. onda je rezultat dijeljenja realan broj. Ukoliko je rezultat dijeljenja negativan. onda prema standardu C90 naˇin zaokruˇivanja rezulc z tata ovisi o implementaciji. Izostavit ´emo operatore vezane uz strukture (Poc glavlje 12). zaokruˇivanje prema nuli. Nazivnik pri dijeljenju mora biti razliˇit od nule.

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

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,

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

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

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

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

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

a pod DOSom Ctr-Z. Funkciju getchar ˇesto koristimo za ˇitanje jednog znaka (npr. ovdje dajemo funkciju koja ˇita samo prvo c slovo s ulaza: int get_first(void) { int ch. c Kada na ulazu nema podataka funkcija getchar ˇeka da se podaci utipkaju. 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.eskimo. c Pod unix-om to je znak Ctr-D. kako ne bi ostali za slijede´i poziv c s c funkcije getchar. z zbog niskog prioriteta operatora pridruˇivanja. Ponaˇanje funkcije getchar ovisi o operacijskom sustavu na kojem se pros gram izvrˇava.com/ scs/C-faq/top. U tim sluˇajevima moramo paziti da proˇitamo sve znakove u liniji. while(getchar() != ’\n’) . 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. 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.80 POGLAVLJE 5. c c ukljuˇivˇi znak za prijelaz u novi red. Da bismo zaustavili ˇitanje moramo utipkati znak za “kraj datoteke”.2 Funkcije iz datoteke <ctype. Posljedica je toga da prvi poziv funkciji getchar blokira program sve dok se ne ukuca ˇitava ulazna linija i pritisne tipka RETURN (ENTER). Operacijski sustav prikuplja podatke s tastature i ˇalje ih aplis s kacijskom programu najˇeˇ´e liniju po liniju. bilo interpretirano kao test z c=(getchar()!=EOF). // prazna naredba return ch.h> sadrˇi deklaracija niza funkcija koje sluˇe z z testiranju znakova. } Zagrade u testu (c=getchar())!=EOF su nuˇne jer bi c=getchar()!=EOF. ch=getchar(). Na primjer. Varijabla c bi tada dobila vrijednost 0 ili 1. Tada su svi c ˇ uˇitani znakovi na raspolaganju funkciji getchar.h> Datoteka zaglavlja <ctype. Neke od funkcija su sljede´e: c . } 5. odgovora c c y ili n). ULAZ I IZLAZ PODATAKA putchar(toupper(c)).html).

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

Stoga je op´enito bolje umjesto gets koristiti funkciju fgets (vidi s c sekciju 13. 5.arg n. while(gets(red)!=NULL) puts(red). . ULAZ I IZLAZ PODATAKA /* kopiranje ulaza na izlaz */ int main(void) { char red[128].3). . Npr. Osnovni nedostatak funkcije gets je u tome ˇto nije mogu´e odrediti s c maksimalni broj znakova koji ´e biti uˇitan. ... . 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. } 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.h> POGLAVLJE 5. a povratna vris c jednost funkcije puts se zanemaruje..82 #include <stdio. c Najˇeˇ´e koriˇteni znakovi konverzije navedeni su u sljede´oj tablici: c sc s c . 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. %c ili %d itd. .4 Funkcija scanf int scanf(const char *format. 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.). .. arg_1. Funkcija scanf sluˇi uˇitavanju podataka sa standardnog ulaza. U tom sluˇaju c s s c izvrˇava se komanda puts(red) koja ispisuje uˇitanu liniju.arg_n) gdje je kontrolni string konstantni znakovni niz koji sadrˇi informacije o z vrijednostima koje se uˇitavaju u argumente arg 1. Op´a z c c forma funkcije je scanf(kontrolni_string. arg_2..

. Numeriˇki c c c c podaci na ulazu moraju imati isti oblik kao i numeriˇke konstante. scanf("%d".. Znak konverzije (%i) interpretira ulazni podatak kao oktalan broj ako mu prethodi nula ili kao heksadecimalan broj ako mu prethodi 0x ili 0X.%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. To znaˇi da pri pozivu funkcije c scanf ispred imena varijable u koju scanf treba uˇitati vrijednost moramo c staviti adresni operator &. 5.4. FUNKCIJA SCANF znak konverzije %c %d %e. c Argumenti funkcije scanf mogu biti samo pokazivaˇi na varijable.4..5.. onda scanf uzima kao argument c adresu te varijable. 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.. uˇitati cijeli broj s ulaza u varijablu x. Tako ´e naredba c int x... /* pogresno */ s predstavlja greˇku... Na primjer.&x). a ne samu varijablu. uzmimo da kˆd o . Podaci koje scanf ˇita dolaze sa standardnog ulaza ˇto je tipiˇno tastac s c tura. Ukoc liko podatak treba uˇitati u neku varijablu. dok naredba c int x. scanf("%d".x). .1 Uˇitavanje cijelih brojeva c Cijeli brojevi mogu biti uneseni kao decimalni (%d) ili kao oktalni i heksadecimalni (%i). .. 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).%f.

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

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

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

. Naime scanf z uvijek ostavlja zavrˇni znak prijelaza u novi red u ulaznom nizu. Na primjer. c To je nuˇno ukoliko smo imali prethodni poziv scanf funkcije. uˇitati cijelu liniju bez znaka za prijelaz u novi red moˇemo pomo´u c z c naredbe scanf(" %[^\n]".. 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.. 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. .. linija). linija). Stoga.. Na primjer . To se radi tako da se znaku konverzije doda prefiks *. Uoˇimo da smo prije %[ ostavili jedan razmak koji s c govori funkciji scanf da preskoˇi sve bjeline koje prethode znakovnom nizu. Ako se ˇeli ˇitati samo znakove bez z c bjelina treba koristiti " %c %c %c" ili %c zamijeniti s %1s. 5. linija). a ispred %[ mora biti ostavc c ljeno prazno mjesto kako bi bile preskoˇene sve prethodne bjeline. 87 uˇitava najve´i niz znakova sastavljen od velikih slova i razmaka. 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 Druga mogu´nost s uglatim zagradama je koristiti sintaksu c scanf(" %[^niz znakova]".5.. 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. kontrolni niz " %c%c%c" ˇita tri znaka.4. Na kraj uˇitanog niza znakova bit ´e dodan \0. linija).. tako da bi s naredba scanf("%[ ABCDEFGHIJKLMNOPRSTUVWXYZ]". scanf(" %[ ABCDEFGHIJKLMNOPRSTUVWXYZ]". c Znak konverzije c uˇitava jedan znak u varijablu bez obzira je li on bjelina c ili ne.4. Arguc c ment linija mora naravno imati dovoljnu dimenziju da primi sve znakove i zavrˇni nul-znak \0. Nadalje. FUNKCIJA SCANF char linija[128].5 Prefiks * Mogu´e je preskoˇiti neki podatak u listi i ne pridruˇiti ga odgovaraju´oj c c z c varijabli.

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

char ch. Nova verzija programa bi glasila int n.&n) == s c z c 1. To moˇemo uˇiniti pomo´u funkcije z c c getchar. FUNKCIJA SCANF 89 5. 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.4.5. while(scanf("%d".&input) != 1) . c Kˆd bi mogao izgledati ovako: o int n. c c int get_int(void) { int input. To moˇemo uˇiniti testom scanf("%d". upiˇe slovo). No program bi trebao kod neuspjeˇnog upisa zatraˇiti od korisnika da s z ponovi upis. scanf("%d". while(n >= 0) { // radi nesto s brojem scanf("%d".4. Stoga je potrebno prije novog poziva funkciji scanf isprazniti spremnik. Tu ˇinjenicu c s c c moˇemo iskoristiti za provjeru jesu li svi traˇeni podaci uˇitani.7 Povratna vrijednost Funkcija scanf vra´a broj uspjeˇno uˇitanih podataka ili EOF. while(scanf("%d". ˇtoviˇe.&n) == 1 && n >= 0) { // radi nesto s brojem } Uoˇimo da prvo ispitujemo je li broj dobro uˇitan. Uzmimo jez z c dan primjer u kojem uˇitavamo i procesiramo samo pozitivne cijele brojeve. 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. Ako nije. // 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. n >= 0 ne´e c c c biti ispitivano.&n).&n). iskoristiti sadrˇaj spremnika da obavijestimo koc s s z risnika o greˇci koju je napravio. s s Ponaˇanje programa moˇemo popraviti ako ispitujemo da li je funkcija s z scanf uspjeˇno uˇitala broj. Mi ´emo.

). Najˇeˇ´e koriˇteni znakovi konverzije dani su u sljede´oj tabeli: c sc s c znak konverzije %d. 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 ´e ispisani onako kako c su uneseni.arg n.5 Funkcija printf int printf(const char *format.arg_n) z gdje je kontrolni string konstantan znakovni niz koji sadrˇi informaciju o formatiranju ispisa argumenata arg 1.. .%f.. Kontrolni znakovni niz ima posve istu formu i funkciju kao kod funkcije scanf. arg_1.\nMolim unesite "). Funkcija printf sluˇi za ispis podataka na standardnom izlazu.%i %u %o %x %e. .. .%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 . Op´a z c forma funkcije je printf(kontrolni_string. . ULAZ I IZLAZ PODATAKA while((ch = getchar())!=’\n’) putchar(ch). c 5. } return input..90 { POGLAVLJE 5. arg_2.. neproˇitani ulazni podatak ´e biti proˇitan c c c pomo´u druge while petlje. printf(" nije cijeli broj. printf("cijeli broj: "). Sadrˇaj ´e biti ispisan funkcijom putchar i c z c korisnik ´e biti zamoljen da ponovi upis. } Kada korisnik ne unese cijeli broj.

. onda unutar kontrolnog znakovnog niza na tom mjestu treba staviti %%. naredba double x=2. c short i=64.. Na primjer. c 5.5.c. Sliˇno pomo´u %d moˇemo ispic c z sati i varijablu tipa char i tipa short.414214 Svi znakovi koji nisu znakovi konverzije ispisani su onako kako su uneseni u kontrolnom znakovnom nizu "x=%d.. char c=’w’. printf("i(okt)=%o: i(hex)=%x: i(dec)=%d\n".i).. FUNKCIJA PRINTF 91 Funkcija vra´a broj ispisanih znakova ili EOF ako je doˇlo do greˇke.0. 0X.5. . Ako treba ispisati znak %. izrazi ili polja... ´e ispisati c x=2. c(char)=%c\n".x.i. 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. y=1.. y=%f\n".sqrt(x)).... printf("x=%d.000000... Na primjer. . c s s Argumenti funkcije printf mogu biti konstante. printf("c(int)=%d.c). 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. y=%f\n". a argumenti tipa float u double... jer ´e u oba sluˇaja z c c printf vidjeti samo varijablu tipa double. Na primjer.i. varijable.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. .5. ispisat ´e c c(int)=119. ispisuje i(okt)=100: i(hex)=40: i(dec)=64 .. c(char)=w ukoliko raˇunalo koristi ASCII skup znakova (119 je ASCII kod znaka “w“).

5.92 POGLAVLJE 5.. U konverziji tipa f broj se ispisuje bez eksponenta. ULAZ I IZLAZ PODATAKA Izrazi tipa long ispisuju se pomo´u prefiksa l..%f.x). int main(void){ printf("i(okt)=%lo: i(hex)=%lx: i(dec)=%ld\n". double x=12345.678. znak konverzije %e.%f. program c #include <stdio...678000: x(e)=1.%Lf. Na primjer. g dobivaju prefiks L ako se ispisuje varijabla tipa long double.h> #include <limits.7 Znakovi konverzije e.i.. printf("x(f)=%f: x(e)=%e: x(g)=%g\n".x. } (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.%g %e.i).234568e+004: x(g)=12345. ..h> long i=LONG_MAX.i.h> c i predstavlja najve´i broj tipa long.%g %Le. f.x. %g i %e. U konverziji tipa g naˇin ispisa (s ekspoc nentom ili bez) ovisi o vrijednosti koja se ispisuje.. a u konverziji tipa e s eksponentom. ´e ispisati c x(f)=12345.2 Ispis realnih brojeva Brojeve tipa float i double moˇemo ispisivati pomo´u znakova konverz c zije %f. Naredba.. c 5.%Lg tip podatka koji se ispisuje broj tipa float broj tipa double broj tipa long double .

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

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

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

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

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

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

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

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

*string2.. Oba c uvjeta zadovoljavaju samo mala slova (engleske) abecede koja u naredbi c=’A’+c-’a’.. u dijelu kˆda o int x. 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.2.h> char *string1...2 if-else naredba if-else naredba ima oblik .\n"). Uoˇimo op´enito da uvjet if(x!=0) moˇemo pisati kao if(x). . c c c c 6.... te isto c c z tako if(x==0) moˇemo zamijeniti s if(!x).. Funkcija strcmp ´e vratiti nulu ako su stringovi string1 i string2 jednaki.... 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. ... if(strcmp(string1. NAREDBE UVJETNOG GRANANJA char c.6... c Istu bismo if naredbu mogli napisati i u obliku if(!strcmp(string1.. Na primjer. izraˇunavanje izraza x != 0 && (20/x) < 5 u sluˇaju x=0 prestat ´e nakon c c c x != 0.. pretvaramo u odgovaraju´a velika slova. . if(x != 0 && (20/x) < 5) .string2)) printf("Stringovi su jednaki. jer je vrijednost cijelog izraza u tom trenutku poznata (=laˇ). if(c>=’a’ && c<=’z’) c=’A’+c-’a’.\n"). 101 Ovdje koristimo logiˇko I kako bismo povezali uvjete c>=’a’ i c<=’z’..2.. Stoga z podizraz (20/x) < 5 ne´e biti izraˇunat i do dijeljenja s nulom ne´e do´i.string2)==0) printf("Stringovi su jednaki..

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

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

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

0). Za drugu if-else naredbu kaˇemo da je z ugnijeˇdena u prvu.0*(1.6. imamo funkciju #include <math.0) y=3. Budu´i da je if-else jedna naredba nisu nam bile potrebne zagrade poslije c else dijela prve if-else naredbe.0)*exp(x).0)).0+exp(5. else y=3. else y=3. else if(uvjet2) naredba2. NAREDBE UVJETNOG GRANANJA naredba1.0*(1.0+(x-2.h> double f(double x) { double y. } 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. else if(x<5.0) y=3.0+exp(5.0)).2.0)*exp(x).0+(x-2. return y. a zatim s se prelazi na naredbu nareba4. naredba4. 105 u kojoj se prvo ispituje uvjet1 te ako je istinit izvrˇava se naredba1. s Program se zatim nastavlja naredbom naredba4. else if(x<5. else naredba3.0) y=3.0) y=3.0+(x-2. Ako uvjet1 nije istinit izraˇunava se uvjet2 c te se izvrˇava ili naredba2 ili naredba3 ovisno o istinitosti izraza uvjet2. z .0+(x-2. if(x<2. Na primjer.0).

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

Takav stil z pisanja treba izbjegavati. . 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...3 switch naredba Naredba switch sliˇna je nizu ugnjeˇdenih if-else naredbi. dok je ona ustvari pridruˇena unutarnjoj if naredbi (if(a>b)).6... 6. case konstanta_2: . else z=b. else z=b. break.3. 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. 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. } else z=b. c o c s Naredba ima sljede´i oblik: c switch(izraz) { case konstanta_1: naredba_1.

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

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

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

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

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

z #include <string. Izrazi separirani zarezom izraˇunavaju se slijeva c na desno i rezultat ˇitavog izraza je vrijednost desnog izraza. 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. Operator zarez koristi se uglavnom u for naredbi.1 Operator zarez U jednoj for naredbi mogu´e je izvrˇiti viˇe inicijalizacija i promjena c s s brojaˇa. i dobili bismo rezultat i=7. Zarez (.5. 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. . c mogli bismo pisati i=(i=3.i++.5.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. 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. prevodi s u long .h> i daje duljinu znakovnog niza (bez nul-znaka). c 6.h>. Na primjer.i.j=strlen(s)-1.h> /* invertiraj : invertiraj znakovni niz */ void invertiraj(char s[]) { int c.2 Datoteka zaglavlja <stdlib.) koji separira dva c z izraza je ustvari operator. for(i=0.j--) { c=s[i]. } } Funkcija strlen deklarirana je u datoteci zaglavlja <string. Tada s[i]-’0’ c daje numeriˇku vrijednost znamenke s[i]. prevodi s u int .5. s[i]=s[j].i+4). Uoˇimo da smo za pomo´nu varijablu c c c mogli koristiti varijablu tipa int (umjesto char). s[j]=c.j.i<j. Pokaˇimo to na funkciji invertiraj koja invertira niz znakova. 6. U tome nam pomaˇe operator zarez.

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

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

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

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

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

izvrˇava s odreden niz naredbi i vra´a rezultat svog izvrˇavanja pozivnom programu. Time postiˇemo ve´u jasno´u programa i olakˇavamo budu´e z c c s c modifikacije.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. Unutar vitiˇastih zagrada c pojavljuje se tijelo funkcije koje se sastoji od deklaracija varijabli i izvrˇnih s naredbi.. Prvi argument arg 1 je varijabla tipa tip 1 itd. Op´i c s c c oblik te naredbe je return izraz. Unutar zagrada nalazi se deklaracija formalnih argumenata s funkcije.tip_n arg_n) { tijelo funkcije } c gdje je tip podatka tip podatka koji ´e funkcija vratiti kao rezultat svog izvrˇavanja.. 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. s c 7. . Funkcija vra´a rezultat svog izvrˇavanja pomo´u naredbe return. Deklaracije pojedinih argumenata medusobno se odvajaju zarezom. Tada je funkciju mogu´e koristiti u razliˇitim c c programima. 119 . . kao ˇto je to sluˇaj s funkcijama iz standardne biblioteke.1 Definicija funkcije Definicija funkcija ima oblik tip_podatka ime_funkcije(tip_1 arg_1.

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

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

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

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

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

printf("Unesite dva cijela broja: "). 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. Prevodiocu su c potrebni samo broj i tipovi varijabli koje funkcija uzima. int main(void) { int x. DEKLARACIJA FUNKCIJE 125 Ovdje je funkcija maximum() deklarirana kao i druge varijable u glavnom programu. 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. z=(x>=y) ? x : y. int y). } void maximum(int x.h> void maximum(int. } Uoˇimo da smo ovdje u prototipu ispustili nazive varijabli. Prototipovi su uvedeni u C sa standardom C90 (ANSI standardom).y. return. scanf("%d %d". c z ˇ Cinjenica da prevodilac ne pravi nikakve pretpostavke o broju i tipu argu- . &x. 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. imaju samo formalni znaˇaj.7. return 0. c #include <stdio.y).3). je deklarirati funkciju izvan main() c c sc funkcije kao u ovom sluˇaju.z). U tom sluˇaju kaˇemo da je funkcija definirana implicitno pravilima prevodioca.&y). Radi kompatibilnosti sa starijim programima dozvoljeno je koristiti i funkcije koje nisu prethodno deklarirane. Primijetimo da varijable x i y deklarirane u deklaraciji void maximum(int x. printf("\nMaksimalna vrijednost =%d\n". koja se ˇeˇ´e koristi. int). Druga mogu´nost. int y) { int z. i mogu se ispustiti. maximum(x.

} int main(void) { int x=5. Spomenimo na ovom mjestu joˇ i to da u C++ gornja deklaracija znaˇi s c double f(void). Sljede´i primjer ilustrira s c z c naˇin prijenosa argumenata. Prilikom poziva funkcije stvarni argumenti se izraˇunavaju (ako su izrazi) c i kopiraju u formalne argumente. ali ne i o argumentima. Takva deklaracija informira prevodilac o tipu funkcije (double). printf("\nIzvan funkcije x=%d". FUNKCIJE menata znaˇi samo da nije u stanju provjeriti je li poziv funkcije korektan ili c nije. 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. c U starijim programima mogu´e je na´i deklaraciju funkcije tipa c c double f(). . Ukoliko nije to ´e biti otkriveno tek prilikom izvrˇavanja programa. f(x).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. return. 7.126 POGLAVLJE 7. Funkcija prima kopije stvarnih argumenata ˇto znaˇi da ne moˇe izmijeniti stvarne argumente. Napomena. c #include <stdio.x). 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). U C++ sve funkcije moraju imati prototip. printf("\nNakon poziva funkcije x=%d". Prevodilac ponovo ne radi nikakve pretpostavke o broju i tipu argumenta. printf("\nUnutar funkcije x=%d". U svim novim programima imperativ je koristiti prototipove.x).x). Takve deklaracije treba izbjegavati.h> void f(int x) { x+=1.

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

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

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

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

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

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

3. va_end(ap). i<n. return 0.1. Pri tome je n zadan kao prvi argument.). Funkcije printf i scanf to rade putem kontrolnog stringa. . mada broj deklariranih argumenata moˇe biti bili koji broj ve´i ili jednak 1.t).3..) { va_list ap.2.0).0).h> double suma(int. t=suma(5.0.0. return total. Tri toˇke iza posz c c ljednjeg deklariranog argumenta oznaˇava da funkcija uzima varijabilan broj c argumenata. s c . double total=0. Napisat ´emo funkciju z c koja sumira n brojeva tipa double koji su joj dani kao argumenti. ++i) total += va_arg(ap. int i.0. } 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.. FUNKCIJE S VARIJABILNIM BROJEM ARGUMENATA 133 Ovdje je dat primjer s dva deklarirana argumenata.1.0.s.t. Nakon ˇto su svi parametri proˇitani potrebno je pozvati funkciju va end s c koja zavrˇava proces ˇitanja argumenta.h> #include <stdarg. } double suma(int n.2.. #include <stdio.n).7..double). Pokaˇimo jedan jednostavan primjer. int main(void) { double s.0.0.5.. printf("%g %g\n".4. for(i=0.6. s=suma(3.0. va_start(ap.

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

8. • Deklaracije funkcija — U string. 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. x=2*r*PI. • Deklaracije struktura — U stdio..14 . c • Makro funkcija — Na primjer getchar() koji je obiˇno definiran kao c getc(stdin) gdje je getc() makro funkcija. onda preprocesor datoteku traˇi u direktoriju u kojem se izvorni program nalazi. . Do zamjene ne´e do´i unutar znakovnih nizova.. • Definicije tipova — Na primjer size t...2. Datoteka ukljuˇena u izvorni kˆd pomo´u #include naredbe moˇe i sama c o c z z sadrˇavati #include naredbe.h je deklariran niz funkcija za rad sa stringovima. 8. prije prevodenja biti zamijenjen s .. itd. NAREDBA #DEFINE 135 Ako je ime datoteke navedeno unutar navodnika. unutar dvostrukih navodnika. tj.. NULL itd.h se definira struktura FILE. 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.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. x=2*r*3. time t.. Ime datoteke z navedeno izmedu oˇtrih zagrada signalizira da se radi o sistemskoj datoteci s (kao npr..h tako imamo EOF..14..h). stdio. c c Tako ´e na primjer dio izvornog kˆda c o #define PI 3. pa ´e preprocesor datoteku traˇiti na mjestu odredenom c z operacijskim sustavom.

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

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

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

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

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

c.8. #include <stdio. %s.5 Predefinirani makroi C standard specificira nekoliko makroa koje moraju biti definirane. datoteka %s\n". z 141 8.__TIME__). } Ovaj kˆd na jednom sustavu ispisuje o Ime datoteke: makro_2. Vrijeme: 19:28:06.\n".__LINE__).\n".\n".\n".__FILE__).__DATE__). __FILE__). . PREDEFINIRANI MAKROI #if 0 dio programa koji iskljucujemo #endif Time se izbjegava problem ugnijeˇdenih komentara. Zadnji makro je uveden standardom C99.5. printf("Linija koda: %d.h> int main(void) { printf("Ime datoteke: %s.\n".__LINE__. 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. printf("Ime funkcije: return 0. printf("Datum: %s. 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. Ime funkcije: main.__func__). Na primjer. Datum: Feb 12 2003. Linija koda: 9.

h> #include <math. PREPROCESORSKE NAREDBE 8. #include <stdio. Tu nam ponovo moˇe pomo´i tehnika s #ifdef c o z c DEBUG. Na primjer. file ime_datoteke. Predaja negativne vrijednosti vrlo ˇesti c c signalizira da se desila greˇka u izvrˇavanju programa. 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. 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.f(x)).h> #include <assert. } int main(void) { int x=-1. Na primjer. return sqrt(2*x-1). printf("x=%d\n".h>. no ANSI-C nam nudi bolju opciju. } . funkcija koja prima jedan argument tipa double moˇe z oˇekivati samo pozitivne vrijednosti. line br_linije gdje je ime datoteke ime datoteke u kojoj se naredba nalazi i br linije broj linije u kojoj se naredba nalazi. 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). Da bismo iskoristili makro assert trebamo ukljuˇiti standardnu datoteku c zaglavlja <assert. Nakon toga assert zaustavlja izvrˇavanje s programa. assert ´e ispisati poruku c Assertion failed: izraz.6 assert Mnoge funkcije oˇekuju da ´e dobiti argumente koji zadovoljavaju odredene c c uvjete. return 0. a to je makro assert.h> int f(int x) { assert(2*x-1 >= 0).142 POGLAVLJE 8.

.8... Svaki assert makro ´e sada biti reduciran na nul-naredbu. c . 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. ASSERT U ovom sluˇaju program na jednom sustavu ispisuje sljede´e: c c Assertion failed: 2*x-1 > 0. 143 file C:\prjC\test\testA\a1..cpp.h> #define NDEBUG #include <assert..6...h> .

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

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

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

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

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

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

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

Vijek trajanja statiˇke s s c varijable je cijelo vrijeme izvrˇavanja programa. 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..) F1 = F2 = 1. s Svaka varijabla definirana izvan svih funkcija je statiˇka. Prevodilac nije duˇan c z poˇtovati deklaraciju register. z 9. } 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.2. (i = 3. Varijabla dekc larirana u nekom bloku (npr. c Ukoliko statiˇka varijabla nije inicijalizirana eksplicitno prevodilac ´e je c c inicijalizirati nulom. tako da je ona samo sugestija prevodiocu. .... a uniˇtavaju se tek na zavrˇetku programa. . ... To su brojevi definirani rekurzijom Fi = Fi−1 + Fi−2 . 4.6 Statiˇke lokalne varijable c Statiˇka lokalna varijabla je lokalna varijabla deklarirana s identifikatoc rom memorijske klase static. funkciji) s identifikatorom memorijske klase static je takoder statiˇka varijabla. To najˇeˇ´e c sc znaˇi smjestiti varijablu u registar mikroprocesora.2..5 Statiˇke varijable c Statiˇke varijable alociraju se i inicijaliziraju na poˇetku izvrˇavanja proc c s grama. .. .9.2... VIJEK TRAJANJA VARIJABLE ... 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).. ˇ Zelimo napisati program koji ispisuje prvih 20 Fibonaccijevih brojeva. Pogledajmo kako se to svojstvo koristi u jednom primjeru. s Na varijablu tipa register ne moˇe se primijeniti adresni operator. 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. } // neispravno 9. Ona postoji za cijelo vrijeme izvrˇavanja s programa ali se moˇe dohvatiti samo iz bloka u kojem je definirana..

i++) printf("\n i= %d. 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 . Pri tome je nuˇno da se funkcije i globalne varijable definirane u z jednoj datoteci mogu koristiti i u svim ostalima. return 0. f2=1. STRUKTURA PROGRAMA Glavni program imat ´e sljede´i oblik: c c #include <stdio. long f. Takvo z s razbijanje izvornog kˆda ve´ih programa olakˇava njihovo odrˇavanje i nao c s z dogradnju. } Dakle.152 POGLAVLJE 9. svaka funkcija z s s definirana u programu moˇe biti smjeˇtena u zasebnu . int main(void) { int i. Na primjer. 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). f1=f. Upozorenje.3 Vanjski simboli C program moˇe biti smjeˇten u viˇe datoteka. f=(i<3) ? 1 : f1+f2. F= %ld". u drugoj linker povezuje viˇe datoteka o s .o). Tu ´e nam pomo´i statiˇke varijable: c c c c long fibonacci(int i) { static long f1=1. U prvoj prevodilac prevodi izvorni kˆd svake pojedine datoteke u objeko tni kˆd (datoteku s ekstenzijom .h> long fibonacci(int). 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 . return f.fibonacci(i)). Proces prevodenja izvornog kˆda smjeˇtenog u viˇe datoteka ima dvije o s s faze.c datoteku.i<=20. for(i=1. f2=f1.i. } Statiˇke varijable f1 i f2 bit ´e inicijalizirane jedinicama samo pri prvom c c pozivu funkcije fibonacci.

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

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

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

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

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

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

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

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

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

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

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

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

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

a izraz 1. 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. a MAXY=3 je broj stupaca matrice.2. .j) matrice m je m[i][j].0. Na primjer.3.0.166 POGLAVLJE 10.0. m[0][1]=2.1. Elementi viˇedimenzionalnog polja pamte se u memoriji raˇunala kao s c jedno jednodimenzionalno polje. m[0][2]=3. ime je ime polja. 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.5. Poznavanje te ˇinjenice vaˇno je pri konsc z trukciji numeriˇkih algoritama koji rade s matricama te za razumijevanje c inicijalizacije polja. m[1][0]=4. 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.6 Viˇedimenzionalna polja s Deklaracija viˇedimenzionalnog polja ima oblik s mem_klasa tip ime[izraz_1][izraz_2]. tip je tip podatka.1.0. drugi od 0 do izraz 2 . POLJA 10.0. . m[1][2]=6.1 itd.0. . Na primjer. . Tako se prvi indeks kre´e c od 0 do izraz 1 .2.0. Element na mjestu (i.6. 1.. m[1][1]=5. a u drugom m[1][i] za i = 0. ..0.[izraz_n].4.0. gdje je mem klasa memorijska klasa. gdje je c l=i*MAXY+j. izraz n su konstantni cjelobrojni pozitivni izrazi koji odreduju broj elementa polja vezanih uz pojedine indekse. polje m deklarirano sa static float m[2][3]. predstavlja matricu s dva retka i tri stupca. Inicijalne vrijednosti ´e biti pridruˇene elementima matrice po recima: c z m[0][0]=1.0.0}. polje m moˇe biti inicijalizirano na sljede´i z c naˇin: c static float m[2][3]={1.0.

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

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

++j) printf("%f ".1.++i){ for(j=0.i<2.000000 4. 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.0}}.k<2.6. Budu´i da poredak petlji moˇe z s c z biti proizvoljan imamo 3! = 6 verzija tog algoritma.j<3. double C[2][3]. printf("Matrica C:\n"). #include <stdio. printf("\n").0.j.0}.k.ˇ 10.000000 3.000000 2. to funkciji koja uzima dvodimenzionalno polje (char a[2][2]) kao argument moˇemo predati A[0] i A[1]. Rezultat izvrˇavanja programa je s Matrica C: 2. for(i=0. double B[2][3]={{0.C[i][j]).0}. } return 0.000000 4. VISEDIMENZIONALNA POLJA 169 Budu´i da je trodimenzionalno polje zapravo (jednodimenzionalno) poc lje dvodimenzionalnih polja.0}}. Produkt je matrica C = AB dimenzije 2 × 3.000000 1.2. } Mnoˇenje matrica vrˇi se u trostrukoj petlji.h> double A[2][2]={{1.{3.{1.1.++j) for(k=0.0. Uoˇimo da smo u c programu iskoristili ˇinjenicu da se statiˇka varijabla (polje C) automatski c c inicijalizirana nulama.0. a c z matrica B ima dimenziju 2 × 3.0.j<3.4. Matrica A je dimenzije 2 × 2.000000 .i<2.0. int main(void) { int i. for(i=0.0.0.++k) C[i][j]+=A[i][k]*B[k][j].++i) for(j=0.0.

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

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

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

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

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)

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

.. Takva operacija ima smisla ako pokazivaˇi pokazuju na isto polje. Cesto se koristi u ovu svrhu simboliˇka konsc tanta NULL #define NULL 0 . Na primjer. tako da je c c mogu´e pisati c if(px != 0) .. c Ako su px i py dva pokazivaˇa istog tipa. Legalno je pisati z double *p=0.3. 11. tada je c py-px+1 broj elemenata izmedu px i py. Naime. OPERACIJE NAD POKAZIVACIMA 179 11.3.3.h>). To je naroˇito korisno kod automatskih varijabli koje pri pozivu funkcije c ˇ imaju nedefiniranu vrijednost. // POGRESNO 11. (simboliˇka konstanta NULL definirana je u <stdio.2 Pokazivaˇi i cijeli brojevi c Pokazivaˇu nije mogu´e pridruˇiti vrijednost cjelobrojnog tipa. ukljuˇuju´i krajeve..3... Iznimku c c z jedino predstavlja nula.3 Usporedivanje pokazivaˇa c Pokazivaˇe istog tipa moˇemo medusobno usporedivati pomo´u relacijskih c z c operatora. funkcija c koja daje broj znakova u stringu moˇe biti napisana na sljede´i naˇin: z c c .. Usporedivanje s drugim cijelim brojevima nije dozvoljeno: if(px == 0xBFFFF986) . 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.4 Oduzimanje pokazivaˇa c Jedan pokazivaˇ moˇe se oduzeti od drugoga ukoliko oni pokazuju na isto c z polje.. Uoˇimo da je c c c py-px vrijednost cjelobrojnog tipa (a ne pokazivaˇkog). Ako su px i py dva pokazivaˇa na isto polje te ako je py > px. 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..ˇ 11. Pokazivaˇe je osim c c s drugim istovrsnim pokazivaˇem mogu´e usporedivati i s nulom... double *p=NULL.

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

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

. ako se ˇeli da funkcija primi upravo pokazivaˇ na double... void *p. upotrebu cast operatora: char *pc. c z c bez promjene pokazivaˇa.*pd1. c Generiˇki pokazivaˇ se ne moˇe dereferencirati.. z c onda formalni argument treba deklarirati kao pokazivaˇ na double.K. void f(void *). f(pd0). nije garantirano s c da se vrijednost pokazivaˇa ne´e promijeniti. c c #include <stdio. 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 double *pd0. z c Ipak. 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.h> #include <stdlib. pi=(int *) pc. /* O.182 ˇ POGLAVLJE 11. z tj. . ... Argument se zatim ispisuje na odgovaraju´i naˇin. c double *pd0. */ S druge strane. svako takvo pridruˇivanje je dozvoljeno uz eksplicitnu promjenu tipa.... Pokazivaˇ na bilo koji tip moˇe se konvertirati u pokazivaˇ na void i obratno. c c Pokazivaˇ moˇe biti deklariran kao pokazivaˇ na void i tada govorimo o c z c generiˇkom pokazivaˇu. /* ISPRAVNO */ Osnovna uloga generiˇkog pokazivaˇa je da omogu´i funkciji da uzme pokac c c zivaˇ na bilo koji tip podatka. c c void *p. int *pi. /* ISPRAVNO */ Ako ponovna izvrˇimo konverziju u polazni tip pokazivaˇa.. /* ISPRAVNO */ pd1=p.h> . . pove´avati i smanjivati.

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

U skladu s time. x-. Ali. ako imamo pokazivaˇ px. Isto tako. nisu dozvoljene. char *pporuka="Dolazim odmah.". POKAZIVACI ˇ Stoviˇe. u izrazu x+1 bit ´e iskoriˇtena pokac c s zivaˇka aritmetika. onda moˇemo koristiti c z notaciju px[i] umjesto *(px+i). ime polja je konstantan pokazivaˇ pa nije doc zvoljeno pisati x++.184 ˇ POGLAVLJE 11..". 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. ali modific z c ciranje niza putem pokazivaˇa je nedefinirana operacija. Polje x nije l-vrijednost.. 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.i sliˇno. Pokazivaˇi i polja su stoga gotovo ekvivalentni. imamo da je *(px+i)==x[i] i dvije forme moˇemo koristiti ravnos z pravno. Sve ˇlanove niza moˇemo dohvatiti putem pokazivaˇa. U prvoj se deklarira i inicijalizira polje od 15 znakova koje je mogu´e dohvatiti c i mijenjati putem indeksa u uglatim zagradama. U drugoj deklaraciji deklarira se pokazivaˇ na char i inicijalizira adresom znakovnog niza "Dolazim c odmah. 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. ili f(x+5). 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. c z . Naime. Da bismo naglasili razliku izmedu polja i pokazivaˇa pogledajmo sljede´e c c dvije deklaracije: char poruka[]="Dolazim odmah.".. naredbe tipa x= . tj.

1. Treba c c samo pomaknuti const u definiciji. /* DOZVOLJENO */ .1.5}. Uzmimo sljede´i primjer z c c double polje[5]={0.14159.ˇ 11.2. // nije dozvoljeno polje[3]=1. Pokazivaˇ pp smo c inicijalizirali s nekonstantnim poljem polje. const double *pp=polje. z Na primjer const double pi=3.5 Pokazivaˇi i const c Vidjeli smo da modifikator const moˇemo koristiti za definiciju konstanti. // O.5}. POKAZIVACI I CONST 185 11.3. ˇto je dozvoljeno. // nije dozvoljeno pp[3]=1.4.3. Na primjer. double polje[5]={0. /* ISPRAVNO */ double *pt=polje.K.0.0. /* NIJE DOZVOLJENO */ *pp=56. Takva ´e funkcija prihvatiti konstantno i nekonstantno polje kao argument.0.2. Funkcija koja samo ispisuje elemente polja mogla bi biti deklarirana na ovaj naˇin: c void print_array(const double *array. z const double polje[5]={0.4.0.0.0.0.9. pp = &polje[1]. Ako definiramo konstantnu varijablu. double * const pp=polje.5}. Pokazivaˇ pp deklariran je kao pokazivaˇ na konstantan double. Prevodilac s nam jedino ne´e dozvoliti mijenjanje elemenata polja putem pokazivaˇa pp. onda samo pokazivaˇ na konstantan c tip moˇe pokazivati na tu varijablu.4. const double *pp=polje. int n).1. /* 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.K.2. Sam pokazivaˇ moˇemo slobodno mijenjati c z pp++.0. To znaˇi c c c da on pokazuje na konstantnu varijablu tipa double.3. c Mogu´e je definirati konstantan pokazivaˇ na nekonstantan tip.0.0.0. // O.2. c c *pp=3. Na primjer.5.0. Jednako tako moˇemo ga primijeniti na pokazivaˇe.0.0.

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

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

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

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

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

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

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

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

int p(char *a). Ti prioriteti mogu se promijeniti upotrebom zagrada. /* p je funkcija koja uzima pokazivac na char i vraca int */ int *p(char *a). /* 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). onda *f() mora biti tipa int. /* p je funkcija koja uzima pokazivac na char i vraca pokazivac na polje od 10 elemenata tipa int int */ int p(char (*a)[]). Tako je c int (*f)(void). 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 funkcija koja uzima pokazivac na char i vraca pokazivac na 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. int (*p)[10]. ˇto komplis cira ˇitanje deklaracija. c c Prilikom interpretacije deklaracije uzimaju se u obzir prioriteti pojedinih operatora. int *p[10]. /* p je funkcija koja uzima pokazivac na polje znakova i vraca int */ .194 ˇ POGLAVLJE 11. Ako deklariramo c s int *f(void). deklaracija pokazivaˇa na funkciju koja ne uzima argumente i vra´a int. POKAZIVACI 11. /* p je pokazivac na funkciju koja uzima pokazivac na char i vraca int */ int (*p(char *a))[10].

ˇ 11. /* p je pokazivac na funkciju koja uzima pokazivac na polje znakova i vraca pokazivac na int */ int *(*p[10])(char *a). /* 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 */ .

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

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

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

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

STRUKTURE 12. Isto tako funkcija moˇe vratiti strukturu u pozivni program. U primjeru struct tocka { int x.x).x.3. 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. Pokazivaˇ moˇemo inicijalizirati adresom varijable: c z pp1= &p1.x je ekvivalenta s *(pp1. p1. a pp1 je pokazivaˇ na strukturu tipa c tocka. a clan jedan ˇlan strukc c c ture.2.3 Strukture i pokazivaˇi c Pokazivaˇ na strukturu definira se kao i pokazivaˇ na osnovne tipove c c varijabli. } p1. 12.200 POGLAVLJE 12. } 12. struct tocka p2) { p1. return p1. onda je izraz ptvar->clan .y. Pojedini ˇlan strukture moˇe se putem pokazivaˇa dose´i izrazom (*pp1).y += p2. Uoˇimo da su zagrade nuˇne jer operator toˇka ima viˇi prioritet c z c s od operatora dereferenciranja. z Na primjer. Struktura moˇe z c z biti argument funkcije koja tada dobiva kopiju strukture koja je stvarni argument funkcije.y.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.x i c z c c (*pp1). *pp1.x += p2. int y.2 Struktura i funkcije Jedine operacije koje se mogu provoditi na strukturi kao cjelini su pridruˇivanje i uzimanje adrese pomo´u adresnog operatora. p1 je varijabla tipa struct tocka. ˇto s nije korektno formiran izraz. Izraz *pp1.

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

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

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

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

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

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

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

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

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

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

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

printf("0.234375. .i).212 POGLAVLJE 12.u. 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 binarno = %x\n".x=0. STRUKTURE a onda vrijednost unije proˇitamo kao varijablu drugog tipa rezultat ´e ovisiti c c o raˇunalu i najvjerojatnije ´e biti besmislen. 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.

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

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

1. z s no takav kˆd nije prenosiv na druge operacijske sustave. Standardni ulaz je povezan s s tastaturom. Nasuprot s tome funkcije operacijskog sustava pruˇaju programeru viˇe funkcionalnosti. VRSTE DATOTEKA slika 13.) mogu´e je standardni ulaz. a standarni izlaz i izlaz za greˇke s ekranom. To su standardni ulaz..3 Standardne datoteke C automatski otvara tri datoteke za korisnika. izlaz i izlaz za greˇke c s preusmjeriti. \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.1.. \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”. 13. Mi ´emo u ovom o c poglavlju opisati samo neke funkcije iz standardne biblioteke. standardni izlaz i standardni izlaz za greˇke.13. DOS.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”. . \n koristi standardnu biblioteku lakˇe je prenosiv na druge sustave. Na nekim operacijskim sustavima (Unix. Istaknimo da C s tastaturu i ekran tretira kao datoteke budu´i da datoteka predstavlja konc ceptualno naprosto ulazni ili izlazni niz oktetat.

Datoteka mora biti otvorena pomo´u funkcije fopen prije prve operacije pic sanja ili ˇitanja. U datoteci <stdio. Svrha je s spremnika smanjiti komunikaciju s vanjskom memorijom (diskom) i tako pove´ati efikasnost ulazno-izlaznih funkcija.. Tipiˇno se fopen koristi na sljede´i naˇin: c c c c ptvar=fopen(ime. if(ptvar==NULL) { printf("Poruka o gresci"): . } gdje je ime ime datoteke koja se otvara.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). 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. c Otvaranje postoje´e datoteke za ˇitanje i pisanje. Otvaranje postoje´e datoteke za dodavanje teksta.tip).. c Otvaranje postoje´e datoteke za ˇitanje i dodavanje teksta. 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. .2 Otvaranje i zatvaranje datoteke FILE *fopen(const char *ime..216 POGLAVLJE 13. c c Kreiranje nove datoteke za ˇitanje i pisanje. DATOTEKE 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... Program koji ˇeli c c c z z otvariti datoteku mora prvo deklarirati pokazivaˇ na FILE kao u ovom pric mjeru: FILE *ptvar. c c c • Ako datoteka koju otvaramo s tipom ”a” ili ”a+” ne postoji bit ´e c kreirana.

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

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

FUNKCIJE ZA CITANJE I PISANJE return 0. FILE *fpizlaz) { int c. FILE *fp) int fputc(int c. FILE *fp). char *argv[]) { FILE *fpt. Prvi argument je znak koji vra´amo. FILE *fp) s z Ponovo.fpizlaz). } 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. Za viˇe detalja vidi [7]. if(argc==1) /* ime datoteke nedostaje */ cpy(stdin.stdout). while((c=getc(fpulaz))!=EOF) putc(c. onda se standardni ulaz kopira na standardni izlaz. Funkcija koja kopira jednu datoteku u drugu moˇe biti napisana na z sljede´i naˇin: c c void cpy(FILE *fpulaz. 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 *. } 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. Ukoliko se ne navede niti jedno ime. #include <stdio.stdout).ˇ 13. int main(int argc. Funkcije vra´aju ispisani c znak ili EOF ako je doˇlo do greˇke prilikom ispisa.3. Funkcija putchar(c) iz s s Poglavlja 5 definirana je kao putc(c. else while(--argc>0) . c s Znak moˇe biti upisan u datoteku pomo´u funkcija z c int putc(int c. FILE *).

c c c s ukljuˇivˇi ’\n’. 13. ali na njegovo c mjesto stavlja ’\0’. } return 0. } else { cpy(fpt. buffer) u koji ´e ulazna c c linija biti spremljena. ostatak ´e biti proˇitan c s c c pri sljede´em pozivu funkcije fgets. dok gets znak za prijelaz u novi red uˇitava.220 POGLAVLJE 13. Ukoliko je ulazna linija dulja od toga. fclose(fpt). Prvi argument je pokazivaˇ na dio memorije (eng. DATOTEKE if((fpt=fopen(*++argv. Tre´i argument je pokazivaˇ na datoteku iz koje se uˇitava.stdin). } Uoˇimo da smo u programu posve izbjegli upotrebu brojaˇa time ˇto inkrec c s mentiramo i dekrementiramo argc i argv.n. liniju-po-liniju je . Pri tome treba uzeti u obzir razliku da fgets uˇitava i znak ’\n’. s Funkcija char *gets(char *buf).stdout). 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. 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’. a drugi je veliˇina memorije na koju prvi argument c pokazuje.*argv). 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. Stoga je bolje umjesto gets(buf) koristiti c fgetf(buf. int n.3.2 ˇ Citanje i pisanje liniju po liniju c Funkcija koja uˇitava podatke iz datoteke liniju-po-liniju je char *fgets(char *buf. Funkcija za ispis podataka u datoteku. uvedena u Poglavlju 5 ˇita uvjek sa standardnog ulaza."r"))==NULL){ printf("cat: ne mogu otvoriti datoteku %s\n". FILE *fp). exit(1). Pri tome ´e biti uˇitano najviˇe n-1 znakova.

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

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

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

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

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

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

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 &. 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.Poglavlje 14 Operacije nad bitovima 14. short. zatim na sljede´em najmanje c c znaˇajnom mjestu itd.1 Operatori Programski jezik C ima jedan broj operatora ˇije je djelovanje definic rano na bitovima. int i long. Logiˇko I: c a = 0100 0111 0101 0111 b = 1101 0100 1010 1001 a & b = 0100 0100 0000 0001 227 . Usporeduju se bitovi na c najmanje znaˇajnom mjestu u oba operanda. | i ^ uzimaju dva operanda i vrˇe operacije na s bitovima koji se nalaze na odgovaraju´im mjestima. Takvi se operatori mogu primijeniti na cjelobrojne tipove podataka char.

Tada prvo definiramo masku. Uoˇimo da je maska neovisna o broju bitova c koji se koristi za tip int. Na primjer. s Logiˇko I takoder sluˇi postavljanjem na nulu odredenih bitova. konstantu koja ima sljede´i raspored bitova: c mask = 0000 0000 0011 1111 Odmah se vidi da je mask=0x3f.228 POGLAVLJE 14. c 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. a sve z ostale postaviti na jedan. da smo htjeli izdvojiti dio najznaˇjnijih c bitova (lijevo pozicioniranih) morali bismo precizno znati duljinu tipa int. pretpostavimo da ˇelimo ˇest najmanje znaˇajnih bitova iz variz s c jable a kopirati u varijablu b. To moˇemo posti´i pomo´u logiˇkog I operatora. Pretpostavimo da su variz c c c jable tipa int i da je int kodiran u 16 bitova. Na primjer. ali taj naˇin je zavisan o s c ˇirini tipa int. a ostale postavili na nulu morali c s bismo izvrˇiti operaciju b=a & 0xfc00 (provjerite). 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. a na svim ostalim c z jedinice. 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. Naravno. Logiˇko ILI na sliˇan naˇin moˇe posluˇiti maskiranju bitova. Ako c z u nekoj varijabli a ˇelimo postaviti na nulu neki bit. c c sc z Maskiranje je transformacija binarnog zapisa u varijabli na odreden naˇin. Da bismo izdvojili najznaˇajnijih ˇest bitova. 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. Njega ´emo c c c z z c iskoristiti kada ˇelimo iz jedne varijable u drugu kopirati dio bitova. 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 .

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

. scanf("%d". To znaˇi da ´e za negativan broj ispraˇnjeni bitovi na lijevoj strani c c z biti popunjavani jedinicama. return 0.i<=nbits. /* duljina tipa int */ mask=0x1 << (nbits-1).230 POGLAVLJE 14.&a). a za pozitivan broj nulama. ve´ina prevodioca ´e na lijevoj strani uvesti bit predznaka c c broja. 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.h> int main(void) { int a. OPERACIJE NAD BITOVIMA Ako je varjabla a cjelobrojni tip s predznakom onda rezultat moˇe ovisiti o z implementaciji. } 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. printf("%d".++i) { b=(a & mask) ? 1 : 0.b. neki prevodioci ´e uvijek popunjavati ispraˇnjena mjesta nulama. for(i=1. #include <stdio.b). a sve ostale bitove stavi na nulu. /* 1 na najznacajnijem mjestu*/ printf("\nUnesite cijeli broj: "). } printf("\n"). unsigned mask. if(i % 4 == 0) printf(" "). nbits=8*sizeof(int).nbits. c z Logiˇki operatori uvedeni u ovom poglavlju formiraju operatore pridruˇivanaja c z &= ^= |= <<= >>= Neka je a=0x6db7. S druge strane.i. mask >>= 1.

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

v.2.b=%d.c=%d\n". 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ˇ. v. unsigned b : 5. v. v. return 0.c.d).b. }. unsigned : 5.a.3}.c=%d. } v={1.v.v.232 POGLAVLJE 14. v. 5. unsigned c : 5. } 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. unsigned d : 5.b.b=%d. struct primjer v. OPERACIJE NAD BITOVIMA int main(void) { static struct{ unsigned a : 5. 5.a=%d. c c c c #include <stdio.v. printf("v treba %d okteta\n". Na primjer.v.h> int main(void) { static struct{ unsigned a : unsigned b : : unsigned unsigned c : } v={1. } . 5.2. 0. unsigned c : 5. return 0.a=%d.v. sizeof(v)).v. printf("v. v.3. sizeof(v)).4}. unsigned b : 5.a. printf("v.d=%d\n". printf("v treba %d okteta\n".c).

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. POLJA BITOVA 233 Polja bitova mogu se koristiti kao i druge strukture s odredenim ograniˇ ˇenjima.14. Clanovi mogu biti tipa unsigned i tipa int.2. c .

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

ASCII ZNAKOVI 235 A.2. 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 | .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 | . 59 | [ 91 | { 123 (fs) 28 | < 60 | \ 92 | | 124 (gs) 29 | = 61 | ] 93 | } 125 (rs) 30 | > 62 | ^ 94 | ~ 126 (us) 31 | ? 63 | _ 95 | (del) 127 .

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

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

Sign up to vote on this title
UsefulNot useful