programiranje 2 - racunarstvo550.xyz. semestar/programiranje 2... · od rješavanja zadataka, do...
Post on 12-Sep-2019
33 Views
Preview:
TRANSCRIPT
Izradili: Duje Senta Antonio Prcela 9. ožujka 2015.
Sveučilište u Splitu Fakultet elektrotehnike, strojarstva i brodogradnje
Programiranje 2
Priručnik za laboratorijske
vježbe
Sadržaj
I. Općenito o C-u ............................................................................................................................................ 1
II. Uvod u leksički pretprocesor ...................................................................................................................... 1
III. Usporedba sintaksi C-a i VB.Net-a .......................................................................................................... 3
Vježba 1. ............................................................................................................................................................. 5
Vježba 2. ............................................................................................................................................................. 8
Vježba 3. ...........................................................................................................................................................11
Vježba 4. ...........................................................................................................................................................13
Vježba 5. ...........................................................................................................................................................15
Vježba 6. ...........................................................................................................................................................20
Vježba 7. ...........................................................................................................................................................21
Vježba 8. ...........................................................................................................................................................24
Vježba 9. ...........................................................................................................................................................26
Vježba 10. .........................................................................................................................................................28
Vježba 11. .........................................................................................................................................................30
Vježba 12. .........................................................................................................................................................33
1
I. Općenito o C-u
Programski jezik C spada u proceduralne programske jezike kojeg su razvili Dennis Ritchie i Ken Thompson u
Bellovim laboratorijima 1976.g. C je jezik opće namjene, što znači da se u njemu može napraviti apsolutno sve:
od rješavanja zadataka, do pisanja pogonskih programa (engl. driver), operacijskih sustava, tekst procesora ili
igara. C, kao jezik, ni u čemu ne ograničava. Omogućuje i uključivanje naredbi pisanih strojnim jezikom (engl.
assembler), zbog čega je zajedno s mogućnošću direktnog pristupa pojedinim bitovima, bajtovima ili cijelim
blokovima memorije, pogodan za pisanje sistemskih programa. Zbog tih karakteristika C je među popularnijim
programskim jezicima i rabe ga mnogi programeri. Kao jedan od najvažnijih jezika u povijesti komercijalne
računalne industrije, C je do danas ostao jedini programski jezik prilagođen za sve računalne platforme, od malih
sustava pa do mrežnih superračunala. Programi napisani u njemu vrlo su bliski načinu rada hardvera te u načelu
zahtijevaju od programera dobro razumijevanje rada procesora, memorije te ulazno-izlaznih sklopova. Korištenje
C-a je određeno standardom, a trenutni standard koji se koristi je C11.
II. Uvod u leksički pretprocesor
Pojam "pretprocesor" se koristi za oznaku prve faze obrade izvornog koda. On vrši leksičku obradu izvornog koda
na tri načina:
1. Umeće u izvorni kod datoteke koje su navedene u direktivi #include
2. Vrši supstituciju teksta prema direktivi #define
3. Vrši selekciju koda, koji će se prevesti, pomoću direktiva #if, #elif, #else i #endif
Koriste se dvije varijante direktive #include:
#include <ime_datoteke> //primjer 1
U gornjem primjeru (primjer 1) je prikazano kada se datoteka traži u direktoriju u kojem su smještene datoteke s
deklaracijama standardnih funkcija. Dok se u donjem primjeru (primjer 2) uključuje datoteka koja se nalazi u
direktoriju gdje je izvorni kod (u direktoriju programa gdje se nalazi datoteka *.c).
#include "ime_datoteke" //primjer 2
Leksička supstitucija teksta, makro (engl. macro), se definira pomoću direktive #define na dva načina:
#define identifikator tekst_za_supstituciju
Neki primjeri supstitucije:
#define GODINA 20
#define ANTE "Ante"
#define NEW_LINE printf("\n");
#define ISPIS printf
#define ZBROJ(arg1, arg2) arg1 + arg2
2
Primjer koda sa supstitucijom: Ispis:
#include <stdio.h>
#define GOD 20 #define ANTE "Ante" #define NEW_LINE printf("\n"); #define ISPISI printf #define POST(a,b) (float)((a)*(100))/(b)
int main(int argc, char *argv[]) { ISPISI("%s ima %d godina.\n", ANTE, GOD); NEW_LINE ISPISI("Imao je na ispitu %f posto.", POST(3,5)); return 0;
}
Ante ima 20 godina.
Imao je na ispitu 60 posto.
Tablica T1 Primjer supstitucije
Leksička definicija Izvorni kod Supstitucija
#define MNOZI(a,b) ((a)*(b)) y = MNOZI(2+5,broj); y = ((2+5)*(broj));
#define MNOZI(a,b) a*b y = MNOZI(2+5,broj); y = 2+5*broj;
Tablica T2 Primjer makroa
Razlog zagrada u prvoj definiciji (Tablica T2) je zbog prioriteta operatora, jer da je makro napisan na način kao u
drugoj definiciji zbog prioriteta operatora * nad +, makro ne bi vratio očekivani rezultat.
3
III. Usporedba sintaksi C-a i VB.Net-a
Osnovna razlika koda napisanog u C-u s onim napisanim u VB.NET-u je taj što je C vrlo osjetljiv na velika i mala
slova (case-sensitive). Nije isto je li napisano pozivFunkcije(); ili PozivFunkcije();, u ovom slučaju bi se
pozvale dvije različite funkcije.
C programski jezik: Visual Basic .NET
#include <stdio.h>
int main(int argc, char *argv[]) { int cijeli_broj = 0; char znak = 0; printf("Hello world!"); printf("\n");
/*komentar u C-u*/ return 0; }
Module Module1
Sub Main()
Dim cijeli_broj As Integer Dim znak as Char Console.Write("Hello world!") Console.WriteLine()
'komentar u VB.Net-u End Sub End Module
Tablica T3 Usporedba sintakse C i VB.net
Kao što se može vidjeti iz priložene tablice (Tablica T3), postoje značajne razlike sintaksi. Prvo se može uočiti da
se u C-u imena ključnih riječi pišu malim slovima. Sljedeće je to što se svaka naredba mora zatvoriti s točka-
zarezom ; inače se javlja greška pri prevođenju koda. Varijable prilikom deklaracije moraju imati pridruženu
inicijalnu vrijednost, inače sadrži nedefiniranu vrijednost. Pristup sadržaju neinicijalizirane varijable je strogo
zabranjen te može dovesti do greške u prevođenju ili izvršavanju programa.
U C-u ne postoje procedure, samo funkcije. U gornjem primjeru se može vidjeti funkcija main. Kao i kod VB.NET-
a, main je početna funkcija svakog programa, od nje program započinje s radom. Funkcija main() po
standardima ANSI C te ISO/IEC C11 ima povratnu vrijednost isključivo tipa int, dok ostale funkcije mogu imati
povratnu vrijednost bilo kojeg tipa. Poželjno je da main() vraća vrijednost nula (return 0;) ako je program
uspješno završio s radom, te bilo koju drugu vrijednost u slučaju da je došlo do greške prilikom izvršavanja
programa.
int main(int argc, char *argv[])
int main(void)
int prije main-a, a i bilo koje druge funkcije, govori da funkcija vraća cijelobrojnu vrijednost, dok void unutar
zagrada govori da funkcija ne prima nikakve argumente. U varijabli argc je zapisano broj argumenata koji se
nalaze u nizu stringova u argv. Program može imati minimalno jedan argument, što reprezentira naziv programa.
4
Funkcije vraćaju vrijednost na sljedeći način:
int inkrement(int x) { x++; return x; //x je varijabla tipa int }
Funkcija printf()
- funkcija koja se koristi za formatirani ispis teksta i/ili varijabli na ekranu.
printf("Pozdrav! "); // ispis teksta
printf("\n"); // prelazak u novi red
printf("%d", x); // ispis vrijednosti varijable x tipa int
postoji još:
%s – ispis stringa (niza znakova)
%c – ispis varijable tipa char
%e – ispis tipa double
%f – ispis varijable tipa float
Adresni operator &
- Služi da bi se pristupilo adresi neke varijable. Navodi se ispred imena varijable.
Funkcija scanf() & scanf_s()
- koristi se za formatirani unos podataka s standardnog ulaza (stdin), te se koriste isti specifikatori formata
kao kod printf-a (%d, %e, %c…). Pošto se radi o unosu, mora se koristiti adresni operator & da bi se
vrijednost spremila na memorijsku adresu koja je dodijeljena varijabli.
Primjer korištenja:
scanf(" %d", &x); //spremi unos s tastature na adresu varijable x
Za unos stringa preko scanf() se ne koristi adresni operator:
scanf(" %s", txt); //’txt’ je adresa početnog elementa niza znakova
Gore navedena funkcija ima nedostatak što na standardnom ulazu može biti unesena veća količina teksta nego
što varijabla može spremiti pa se program sruši (prelijevanje međuspreminka). Da bi se spriječilo rušenje
programa u takvim uvjetima uvedena je funkcija scanf_s(), koja nije dio ANSI C-a i podržana je samo u
Windows OS-u. Primjer korištenja:
char txt[N] = {0}; // deklariran je niz znakova „txt“ od N elemenata scanf_s(" %s", txt, N); /* unos se sprema u „txt“ ako je unos kraći od N
znakova */
Komentari u C-u se mogu pisati na dva načina:
// komentar koda… - korištenjem dvostruke kose crte, za samo jedan red komentara.
/* tekst se nalazi
u dvije linije */ - unutar oznaka za komentar koji se želi pisati u više redaka.
5
Vježba 1.
U programskom jeziku C postoje četiri vrste osnovnih primitivnih varijabli (tablica 1.1). Osnovni operatori koji
postoje u C-u su pretežno isti kao i kod ostalih programskih jezika (tablica 1.2).
Razred varijabli Oznaka Veličina Raspon vrijednosti
Cjelobrojni tip podataka
int
short
long
4 byte
2 byte
8 byte
<-2147483648, 2147483647>
<-32768, 32767>
<-9,223e+15, 9,223e+15>
Znakovni tip podataka char 1 byte <-128, 127>
Realni tip podataka
float
double
4 byte
8 byte
<+-1.175494351e-38 , +-3.402823466e+38>
<+-2.2250738585072014e-308,
+-1.7976931348623158e+308>
Kardinalni tip podataka (mogu biti cjelobrojni i znakovni
tipovi)
unsigned char
unsigned int
unsigned long
1 byte
4 byte
8 byte
<0,255>
<0,65535>
<0, 18,466e+15>
Tablica 1.1 – Primitivne varijable u C-u
Razredi operatora Oznaka Naziv Napomena
Matematički operatori
+ - * / = % ++ --
zbrajanje oduzimanje množenje dijeljenje operator pridruživanja modulus inkrement dekrement
- s desna na lijevo pridružuje vrijednost - ostatak cjelobrojnog dijeljenja
Operatori za rad s bitovima
& | ^ !
logičko AND logičko OR logičko XOR logičko NOT
Operatori indirekcije & *
adresni operator pokazivački operator
Uvjetni operatori
> <
== != >= <=
veće od manje od jednako različito veće ili jednako manje ili jednako
- ukoliko se koristi = onda će doći do pridruživanja
vrijednosti, a ne do usporedbe.
Logički operatori && || !
uvjetno AND uvjetno OR logičko NOT
Tablica 1.2 – Osnovni operatori C-a
6
Ime varijable ujedno označava i adresu varijable. Koja je to adresa? Brigu o tome vodi prevoditelj. Adresa
varijable se može odrediti pomoću posebnog operatora & koji se naziva adresni operator. Koristi se kao unarni
operator koji se zapisuje ispred imena varijable.
Primjer koda: Ispis:
#include <stdio.h>
int main(void) { int cijeli_broj = 5; printf("Adresa varijable je: %d\n", &cijeli_broj); printf("Vrijednost varijable je: %d", cijeli_broj);
return 0; }
Adresa varijable je: 24883681
Vrijednost varijable je: 5
Tablica 1.3 Primjer ispisa vrijednosti i adrese varijable
Varijable je moguće prebaciti u drugi tip pomoću eksplicitne pretvorbe (poznato kao „cast“ operator).
Primjenjuje se tako da se ispred varijable koja se pretvara napiše novi tip. Sljedeći primjer (tablica 1.4.) će
pretvoriti cijeli broj y u broj s pomičnim zarezom i spremiti u varijablu x:
Primjer koda: Ispis:
int y = 12; float x = (float)y; printf("%f", x);
12.000000
Tablica 1.4 Primjer eksplicitne pretvorbe varijable
for petlja
For petlja se upotrebljava kada se kod unutar te petlje treba ponoviti određeni broj puta. Prva trećina zagrade
(do prvog točka-zareza „;“) govori koju varijablu uzeti i odakle krenuti. Druga trećina zagrade je uvjet koji treba
bit zadovoljen da bi krenulo u iduću iteraciju petlje. Zadnji dio zagrade je operacija kojom se mijenja varijabla,
najčešće korak kojim se mijenja varijabla. Napomena: nije potrebno pisati sva tri dijela zagrade. Može se npr.
korak povećavati unutar petlje.
Primjer koda: Ispis:
for(int i=0; i<5; i++) { printf("%d,", i); }
0,1,2,3,4
Tablica 1.5 Primjer for petlje
7
ZADACI
1. Napisati program koji definira četiri varijable (char, int, float, double). Ispisati sve četiri varijable, zajedno s
njihovim adresama i izračunati koliko su bajtova u memoriji sve te varijable zajedno zauzele.
2. Napisati program koji definira tri varijable tipa znak. Prvoj dodijeliti znak 0, drugoj dodijeliti znak 9, a treću
izračunati kao sumu prve dvije. Ispisati sva tri znaka kao znak i kao ASCII vrijednosti.
3. Napisati program koji računa sumu, produkt, razliku i kvocijent dva cijela broja. Ispisati sve rezultate na ekran.
4. Napisati program koji računa sumu prvih 50 brojeva.
5. Napisati program koji na ekran ispisuje:
E E E E E
D D D D
C C C
B B
A
U programu je dozvoljeno koristiti najviše dvije printf() naredbe.
8
Vježba 2.
Cilj ove vježbe je upoznavanje s naredbom selekcije "if" koja služi za uvjetno izvršavanje dijela koda. Provjera
uvjeta se skoro pa uvijek koristi u programima (gotovo je nemoguće zamislit program bez ijedne provjere nekog
uvjeta).
Razlikuje se:
1. uvjetna naredba: if
2. uvjetno grananje: if->else
3. višestruko grananje: if -> else if -> else if ->… -> else
switch -> case
Sintaksa if naredbe s primjerima je prikazana u tablici 2.1.
Sintaksa za if->else...if->else:
if (/*uvjet*/) { /* naredbe koje se izvršavaju ukoliko je uvjet zadovoljen */
}
else if (/*drugi uvjet*/) { /* naredbe ukoliko NE prođe prvi uvjet, a drugi bude zadovoljen */
} else { /* naredbe ukoliko nije zadovoljen ni prvi ni drugi uvjet */ }
Sintaksa za switch->case->..->default:
switch (X) //varijabla koja će se provjeravati { case const: /* umjesto const se piše konstanta s kojom će se usporediti varijabla X */ /* dio koda koji se izvodi ako je uvjet zadovoljen */ break; /* nakon što se odradi kod, prekida se switch. Ako nema break, program će nastaviti provjeriti ostale case-eve */ case... ... default: /* ako nije nijedan case ispunjen, odradit će se naredbe unutar default-a */ break; }
Tablica 2.1 Sintaksa if-a i switch-case
U tablici 2.2 su prikazana dva primjera za zadatka koji provjerava varijablu I. Lijevi primjer je s if a desni s
switch-case. Ako je I jednak 1 tada će nova vrijednost od I biti 20, ako I nije jednak 1 tada provjeri je li
9
jednak 2 te ako je promijeni mu vrijednost u 20. U slučaju da nijedan uvjet nije ispunjen ne mijenja se vrijednost
od varijable I.
if -> else if -> else switch -> case
if (broj == 1) { broj = 20; } else if (broj == 2) { broj = 10; } else break;
switch (broj) { case 1: broj = 20; break; case 2: broj = 10; break; default: break; }
Tablica 2.2 Primjer zadatka s if...else-if & switch-case
Opis za if:
1. Ako je broj jednak 1 onda varijabli broj pridruži broj 20,
2. Ako je broj jednak 2 onda varijabli broj pridruži broj 10,
3. Ako ni to nije ispunjeno, odlazi na else. Zadnji else nema nikakav uvjet, pa će se izvršiti ako nisu
ispunjeni uvjeti prethodnih blokova.
Opis za switch: Provjerava se uvjet za varijablu I. Ukoliko nije udovoljen nijedan uvjet prelazi se na default
(koji je jednak kao i else).
1. Ako je broj jednak 1 onda pridruži varijabli broj broj 20,
2. Ako je broj jednak 2 onda pridruži varijabli broj broj 10,
3. Još ostaje default koji govori ono isto kao i else: ako broj nije bio ispunio prethodne uvjete onda
prekini s radom programa (break).
Kod switch-a nema provjere s logičkim operandima, niti je moguća usporedba dviju ili više varijabli. Moguća je
jedino usporedba varijable s konstantom. Unutar svakog case-a ili default-a, iza naredbi se nalazi break koji
prekida daljnje provjere. Taj break, koji određuje da se završi s bilo kakvom daljnjom provjerom, preskače sve
preostale case-eve pa i tako preskoči default. Ako ne postoji break na kraju case-a, tada će switch nastavit
s provjerom sljedećeg uvjeta, u ovom primjeru case 2.
10
Ugniježđeni uvjeti:
Moguće je postavljati nove uvjete unutar uvjeta, tako da unutar jednog if naredbenog bloka može bit drugi if
naredbeni blok.
Primjer:
if(broj > 0 && broj < 50)
{
if (broj%2 == 0)
printf("Broj je izmedju 1 i 49, te je paran.\n");
else
printf("Broj je izmedju 1 i 49, te nije paran.\n");
}
ZADACI
1. Napisati program koji pretvara uneseni realni broj u cijeli na način da ako je prva znamenka iza
decimalne točke >= 5 zaokružuje se na prvi veći. Program mora raditi i za pozitivne i za negativne
brojeve.
2. Napisati program koji unosi 10 brojeva ali na ekran ispisuje samo neparne. Broj se ispisuje čim je unesen.
3. Napisati program koji unosi 10 brojeva i računa koliko ih je djeljivo s 3.
4. Napisati program koji unosi 10 znakova i računa koliko ih je uneseno:
a) malim slovima;
b) koliko ih je = H.
5. Napisati program koji unosi bodove kolokvija za 5 učenika (između 0 i 100) te računa koliko je učenika
prošlo (više od 50) i koliki je prosječni broj bodova.
11
Vježba 3.
Kod programiranja se često javlja potreba da se određeni dio koda ponavlja više puta, s time da taj broj
ponavljanja nije unaprijed poznat. U takvom slučaju se koristi while ili do->while petlja. Razlika između ove
dvije petlje je u tome što while najprije provjerava je li uvjet zadovoljen, te onda kreće s izvršavanjem koda i
tako se vrti sve dok nije uvjet zadovoljen. Dok do->while petlja radi na način da se uvijek uđe jednom u petlju,
izvrši kod unutar petlje, pa tek nakon toga provjeri je li uvjet zadovoljen i ovisno o tome se ulazi u sljedeću
iteraciju ili se završava rad petlje. Kod obje vrste while petlji mora se obratiti pozornost da se negdje unutar
while petlje mijenja vrijednost varijable koja se provjerava u uvjetu, inače doći do realizacije beskonačne petlje
(primjeri obiju vrsta petlji se nalaze u tablici 3.1).
while primjeri: do->while primjeri:
int broj = 5; while (broj != 0) { printf("%d, ", broj); broj--; } //ispisuje: 5, 4, 3, 2, 1,
int broj = 5; do { printf("%d, ", broj); broj--; } while(broj!= 0); //ispisuje: 5, 4, 3, 2, 1,
int broj = -5; while(broj != 0) { printf("%d, ", broj); broj++; } //ispisuje: -5, -4, -3, -2, -1,
int broj = -5; do { printf("%d, ", broj); broj++; }while(I != 0); //ispisuje: -5, -4, -3, -2, -1,
Tablica 3.1 Primjeri while i do while petlji
Sljedeća while petlja prikazana u tablici 3.1 ne ispisuje ništa, jer uvjet mora biti različit od nule (true).
int broj = 0; while(broj) { printf("%d, ", broj); broj--; }
Tablica 3.2 Uvjet while petlje
Da se na početku inicijalizira broj = 3, petlja bi se izvršila tri puta jer sve što je različito od nule, pa i negativni
brojevi, u uvjetu se tretira kao istina.
Naredbe za upravljanje petljama
Postoje dvije naredbe koje se mogu koristiti unutar programa, a služe za prekid petlje (break) ili za direktan skok
u sljedeću iteraciju petlje (continue).
12
Generiranje slučajnih brojeva
Da bi računalo nasumično generiralo brojeve, koristi se rand()funkcija definirana u zaglavlju <stdlib.h>.
Međutim ako se koristi samo funkcija rand() ona će uvijek generirati iste slučajne brojeve zbog čega se
najčešće koristi u kombinaciji s srand(). Funkcija srand() mijenja brojevnu bazu iz koje se generiraju brojevi,
pa su brojevi generirani pomoću funkcije rand() zbilja slučajni. Primjer generiranja slučajnih brojeva je prikazan
u tablici 3.3.
int broj = 5; srand((unsigned)time(NULL)); broj = rand();
U ovom primjeru, srand() uzima trenutno vrijeme te na osnovu njega generira bazu od koje će krenuti generiranje slučajnih brojeva. Funkcija time(NULL) vraća trenutno vrijeme u UNIX/POSIX obliku, koje računa ukupan broj sekundi koje su prošle od 1.1.1970 (Koordinirano svjetsko vrijeme – UTC).
int i = 0, randBr = 0, min = 11, max = 100; srand((unsigned)time(NULL)); for(i=0; i<25; i++) { randBr = rand() % (max - min) + min; printf("%d, ", randBr ); }
Ovakav princip se koristi ako se želi generirat 25 brojeva koji se nalaze u nekom rasponu, od 11 do 100 (uključujući 11 ali ne i 100). U gornjoj for petlji će se ispisati sve brojeve od 11 do 100, uključujući 11 ali ne uključujući broj 100.
Jednadžba koja ispisuje sve u rasponu od 11 do 100, uključujući 11 i 100:
randBr = (min + rand() / (RAND_MAX / (max - min + 1) + 1) );
Tablica 3.3 Generiranje slučajnih brojeva
Za one koji žele znati više: RAND_MAX je definiran u <stdlib.h> te mu je vrijednost ovisna o standardnim
bibliotekama različitih platformi. Najmanja garantirana vrijednost može biti 32767.
ZADACI
1. Napisati program koji računa aritmetičku sumu brojeva. Nije unaprijed poznato koliko će se brojeva unijeti,
program prestaje s radom kad se unese 0, ali nju ne treba računati za prosjek.
2. Napisati program koji uneseni cijeli broj razbija na dekadske znamenke. Potrebno je ispisati svaku znamenku
posebno i to u obrnutom redoslijedu. Npr. broj 4893 je potrebno ispisati kao 3 9 8 4
3. Napisati program koji generira slučajni broj i korisnik pogađa o kojem se broju radi. Program svaki put treba
javiti je li uneseni broj > ili < od generiranog. Pogađanje traje sve dok se broj ne pogodi. Potrebno je ispisati iz
kojeg je puta broj pogođen.
4. Napisati program koji ispisuje sve primitivne brojeve od 0 – 100.
13
Vježba 4.
Niz je imenovana i numerirana kolekcija istovrsnih objekata koji se nazivaju elementi niza. Elementi niza mogu
biti prosti skalarni tipovi i korisnički definirani tipovi podataka. Označavaju se imenom niza i cjelobrojnom
izrazom – indeksom – koji označava poziciju elementa u nizu. Indeks niza se zapisuje u uglatim zagradama iza
imena niza. Primjerice, x[3] označava element niza x indeksa 3.
Sintaksa deklaracije elementa jednodimenzionalnog niza je:
Tip_podatka ime_Niza [ brojElemenata ];
Prvi element niza ima indeks 0, a n-ti element ima indeks n-1. Prema tome, x[3] označava četvrti element niza. S
elementima niza se manipulira kao s običnim skalarnim varijablama, uz uvjet da je prethodno deklariran tip
elemenata niza. Primjerice, deklaracijom:
int A[10];
definira se A kao niz od 10 elementa tipa int.
Inicijalizacija nizova
Za globalno i statički deklarirane nizove automatski se svi elementi postavljaju na vrijednost nula. Kod lokalno
deklariranih nizova ne vrši se inicijalizacija početnih vrijednosti elemenata niza. To mora obaviti programer. Za
inicijalizaciju elemenata niza na neku vrijednost često se koristi for petlja, primjerice kod:
for (int i = 0; i < 10; i++)
A[i] = 1;
sve elemente niza A postavlja na vrijednost 1.
Niz se može inicijalizirati listom konstanti, napisanom unutar vitičastih zagrada, koje redom određuje početnu
vrijednost elemenata niza.
int A[10]= {1,2,23,4,32,5,7,9,6};
Ako se inicijaliziraju svi potrebni elementi niza, tada nije nužno u deklaraciji navesti dimenziju niza već to obavlja
sam prevodilac.
int A[]= {1,2,23,4,32,5,7,9,6,3};
Ovaj izraz je potpuno ekvivalentan prethodnoj deklaraciji.
Niz se može i parcijalno inicijalizirati. U deklaraciji int A[10]= {1,2,23}; prva tri elementa imaju vrijednost
1, 2 i 23, a ostale elemente prevodilac postavlja na vrijednost nula.
14
ZADACI
1. Napisati program koji s tastature unosi dva cijela broja i matematičku operaciju (+, -, * ili /). Ovisno o operaciji
je potrebno izračunati i ispisati rezultat. 2. Napisati program koji unosi 20 rezultata ocjena s kolokvija (sprema ih u niz). Ocjene mogu biti od 1-5 i ako se
unese krivi broj unos se ponavlja. Kada su ocjene unesene program računa histogram ocjena u 5 grupa (koliko je
bilo kojih ocjena) i ispisuje ga na ekran.
3. Napisati program koji za uneseni niz od 10 cijelih brojeva:
a) traži najmanji element niza;
b) traži najveći element niza;
c) sortira niz od najmanjeg prema najvećem elementu.
Napomena: najmanji i najveći element niza se ne smiju samo ispisati nakon sortiranja. Potrebno ih je pronaći
prije sortiranja.
15
Vježba 5.
U C-u ne postoji varijabla tipa string koja sprema tekst, nego se koristi niz znakova char varijable. Deklaracija i
inicijalizacija je moguća na više načina, a specifikator formata za ispis i unos je %s. Pošto se radi o nizu znakova,
potrebno je na kraju svakog stringa imati '\0' koji označava kraj stringa, inače će najvjerojatnije doći do rušenja
programa ili ispisa nepoželjnih znakova.
char znakovi1[] = "test" ;
char znakovi2[] = {'t', 'e', 's', 't', '\0'};
char znakovi3[5] = {0};
scanf(" %s", znakovi3);
printf("%s\n", znakovi1);
printf("%s\n", znakovi2);
printf("%s\n", znakovi3);
Prva deklaracija i inicijalizacija automatski određuje veličinu niza i na kraj niza dodaje '\0', dok je kod drugog
načina potrebno ručno dodati '\0'. Kod trećeg načina se kreira niz s 5 elemenata, kod kojeg svaki element ima
ASCII vrijednost -52, što je znak s brojem 204 iz proširene ASCII tablice. Tek nakon scanf() mijenja se vrijednost
var. znakovi3 s onim što se unese s tastature te se na kraju unesenog stringa automatski doda '\0'. Pri tome se
unos u niz znakova radi bez korištenja adresnog operatora &. Razlog tome je što varijabla znakovi3 pokazuje na
adresu početka niza.
Napomena: U ostatku teksta koristiti će se naziv "string" umjesto "niz znakova".
Unos stringa s tastature
Stringove je moguće unijeti na sljedeće načine:
scanf(" %s", tekst);
gets(tekst);
Obratite pozornost na razliku između korištenja razmaka u scanf i bez korištenja razmaka:
/*prvi slucaj s razmakom*/ int broj = 0; char tekst[50] = {0}; scanf("%d ", &broj); //ima razmak! gets(tekst); printf("%d\n", broj); printf("%s\n", tekst);
/*drugi slucaj bez razmaka*/ int broj = 0; char tekst[50] = {0}; scanf("%d", &broj); //nema razmak! gets(tekst); printf("%d\n", broj); printf("%s\n", tekst);
Nakon unosa „123“ pa „abc“ se ispisuje:
123 abc
Nakon unosa „123“ odmah se ispisuje:
123
Tablica 5.1 Primjeri unosa s razmakom i bez razmaka
16
Ako se u oba slučaja unese „123“ pa pritisne enter, u prvom slučaju će biti moguće unijeti neku vrijednost u
varijablu tekst, dok će u drugom slučaju „preskočiti“ unos u tekst te će odmah ispisati vrijednost varijable tekst
koja je samo nova linija (enter).
U ostatku teksta će se spomenuti samo nekoliko važnijih funkcija za rad za stringovima. One su ugrađene u
datoteci zaglavlja <string.h>, za detalje kako koja radi mogu se pogledi primjeri s prezentacija, potražit primjere
na internetu ili u Pomoći (CTRL+F1) Visual Studia (ili nekog drugog razvojnog okruženja).
Neke važnije funkcije unutar <string.h>
1) strlen(znakovi1);
Funkcija strlen() vraća dužinu stringa znakovi1. Dužinu računa od prvog elementa do '\0' (koji se ne računa).
Budući da funkcija vraća neku vrijednost, poželjno je da se ta vrijednost ispiše ili spremi u neku varijablu jer je
puno efikasnije pristupati varijabli nego pozivati funkciju.
char neki_string[] = "abcdefg987" ;
x = strlen(neki_string);
printf("Duzina stringa neki_string je: %d \n" , x);
ili:
printf("Duzina stringa neki_string je: %d \n" , strlen(neki_string));
ISPIS:
Duzina stringa neki_string je: 10
2) strcpy(destinacija, izvor);
Funkcija strcpy() uzima sve znakove iz jednog stringa te ih sprema u novi (uključujući i '\0'). Lijevi argument je
odredišna varijabla u koju se kopira string, a desna je izvorišna iz koje će se uzimati svi elementi.
3) strcmp(prvi_string, drugi_string);
Funkcija strcmp() uspoređuje dva stringa te vraća:
- 0 ako su oba stringa identična
- pozitivni broj ako je prvi_string „veći“ od drugi_string („veći“ – ASCII vrijednost prvog različitog slova je
veća u prvi_string nego u drugi_string)
- negativni broj ako je prvi_string „manji“ od drugi_string („manji“ – ASCII vrijednost prvog različitog slova
je manja u prvi_string nego u drugi_string)
Funkcija strcmpi() radi na sličan način, ali ne razlikuje velika i mala slova, što znači da će vratiti 0 za usporedbu
stringova „abcd“ i „ABCD“.
17
Još neke funkcije iz <string.h>.
strlwr Pretvara sva slova iz stringa u mala slova
strupr Pretvara sva slova iz stringa u velika slova
strrev Obrće znakove u stringu (npr.: “abcde“ pretvara u “edcba“)
strchr Pronalazi prvo pojavljivanje određenog znaka u stringu
strrchr Pronalazi zadnje pojavljivanje određenog znaka u stringu
strncat Dodaje određeni broj znakova iz jednog stringa u na kraj drugog stringa.
strncmp Uspoređuje n znakova od dva stringa, povratna vrijednost je ista kao kod strcmp.
Tablica 5.2 funkcije iz zaglavlja string.h
Funkcije za unos znaka s tastature
Deklarirane su u zaglavlju <conio.h>.
getch() – uzima jedan znak s tastature, ali ga ne prikazuje na ekranu (ako koristite putty, onda vam je ovo
poznato kod unosa šifre).
getche() – uzima jedan znak s tastature te ga odmah i prikaže na ekranu.
getchar() – uzima jedan znak s tastature - bez obzira koliko se unese, isto kao i gore navedena
funkcija getche() prikaže unos, ali "čeka" dok se pritisne enter nakon čega program nastavlja s radom. Obje
funkcije uzimaju znak odmah prilikom unosa.
Primjer:
char a, b, c;
a = getch();
b = getche();
c = getchar();
printf("%c %c %c \n", a,b,c);
Tijekom unosa “abcde“ na konzolnom prozoru će se prikazati “bcde“, a funkcija printf će ispisati: “a b c“.
Tablica 5.3. prikazuje još neke od funkcija za rad sa znakovima. Kod svake od ovih funkcija vrijedi pravilo da vraća
vrijednost 0 ako uvjet nije zadovoljen, a broj različit od 0 ako je uvjet zadovoljen. Primjeri s ovim funkcijama su
prikazani u tablici 5.4.
Funkcije za rad s znakovima: isalnum – testira je li neki znak dekadska znamenka ili slovo. isalpha – testira je li neki znak slovo. islower – testira je li znak malo slovo. isupper – testira je li znak veliko slovo. isdigit – testira je li neki znak dekadska znamenka ili slovo. isascii – testira je li znak definiran u ASCII tablici. toascii – vraća ASCII vrijednost nekog znaka tolower – vraća ASCII vrijednost malog slova za zadano slovo toupper – vraća ASCII vrijednost velikog slova za zadano slovo
Tablica 5.3 Funkcije iz zaglavlja ctype.h
18
Primjer: Ispisuje:
char a = 'a', b = 'B', c = '1', d = 'f';
printf("isalnum 'a': %d \n", isalnum(a)); printf("isalpha 'B': %d \n", isalpha(b)); printf("isnum '1': %d \n", isdigit(c)); printf("isupper 'f': %d \n", isupper(d));
isalnum 'a': 2
isalpha 'B': 1
isnum '1': 4
isupper 'f': 0
Tablica 5.4 Primjeri ispisa
Pretvaranje tipova podataka
Za pretvorbu jednog tipa podataka u drugi mogu se koristiti sljedeće funkcije:
atof() – pretvara niz znakova (ascii) u realan broj;
atoi() – pretvara niz znakova (ascii) u cijeli broj;
itoa() – pretvara cijeli broj u niz znakova.
Deklaracije funkcija:
double atof(const char *string);
int atoi(const char *string);
char *itoa(int x, char *string, int baza);
string - adresa niza znakova u koji se spremaju znamenke broja
x - broj koji se pretvara u string
baza - brojevna baza (heksadecimalno: 16; decimalno: 10;…) po kojoj se vrši pretvorba (u opsegu od 2-36) povratna vrijednost je pokazivač na string.
Primjer:
char tekst[] = "12ab34cd56";
int broj;
broj = atoi(tekst);
printf("Tekst pretvoren u broj: %d \n" , broj);
Ispisuje:
Tekst pretvoren u broj: 12
19
ZADACI
1. Napravi program koji uzima znakove s tastature (sa ili bez prikaza tih znakova na ekranu) sve dok se ne unese
znak ESC. Tada se izlazi iz programa.
2. Napravit program koji iz unesenog stringa stvara novi (u novoj varijabli), na način da iz prvog prebaci sve
znakove osim brojeva.
3. Napraviti program koji uneseni string mijenja u 3 koraka i rezultat svakog ispisati. Smije se koristiti samo jedan
string.
a) U stringu trebaju ostati samo slova.
b) Iz tog stringa treba izbaciti sve samoglasnike (velika i mala slova).
c) Sva velika slova pretvoriti u mala i obrnuto.
4. Napravi program koji unesenu dekadsku vrijednost pretvara u heksadecimalnu, s tim da je znamenke potrebno
ispisati svaku u svom redu i to u pravom rasporedu.
20
Vježba 6.
Višedimenzionalnim nizovima se pristupa preko dva ili više indeksa. Primjerice,deklaracijom int x[3][4]; definira
se dvodimenzionalni niz koji ima 3 x 4 = 12 elemenata. Deklaraciju se može čitati i ovako: definirana su 3 niza od
kojih svaki ima po 4 integer elementa. Dvodimenzionalni nizovi se često koriste za rad s matricama. U tom
slučaju nije potrebno razmišljati o tome kako je niz složen u memoriji, jer se elementima pristupa preko dva
indeksa: prvi je oznaka retka, a drugi je oznaka stupca matrice.
Matrični prikaz niza je:
x[0][0] x[0][1] x[0][2] x[0][3]
x[1][0] x[1][1] x[1][2] x[1][3]
x[2][0] x[2][1] x[2][2] x[2][3]
Memorijski raspored elemenata dvodimenzionalnog niza, koji opisuju neku matricu, je takvi da su elementi
složeni po redovima matrice; najprije prvi redak, zatim drugi, itd. Višedimenzionalni niz se može inicijalizirani već
u samoj deklaraciji, primjerom:
int x[3][4] = {{1, 21, 14, 8},{12, 7, 41, 2},{1, 2, 4, 3}};
Navođenje unutarnjih vitičastih zagrada je opcijski, pa se može pisati i sljedeća deklaracija:
int x[3][4] = {1, 21, 14, 8, 12, 7, 41, 2, 1, 2, 4, 3};
Ovaj drugi način inicijalizacije se ne preporučuje, jer je teže uočiti raspored elemenata.
ZADACI
1. Napisati program koji unosi jednu matricu (A dimenzija 3x3) i iz te matrice računa matricu B = 5*A. Matricu B
je potrebno ispisati.
2. Napisati program koji unosi jednu matricu (A dimenzija 3x3) i iz te matrice stvara matricu B = AT. Matricu B je
potrebno ispisati.
3. Napisati program koji unosi dvije matrice (A i B dimenzija 3x3), a zatim računa matricu C = A + B i matricu D =
A * B. Matrice C i D je potrebno ispisati.
4. Napisati program koji unosi 5 imena i zatim:
a) pronalazi indeks imena prvog po abecedi, te ispisuje indeks i ime;
b) pronalazi indeks imena zadnjeg po abecedi, te ispisuje indeks i ime;
c) sortira imena po abecedi i ispisuje ih.
Pri tome rezultati pretraživanja ne smiju razlikovati velika i mala slova, tj. bez obzira je li ime uneseno velikim ili
malim slovom treba biti na ispravnom mjestu po abecedi.
21
Vježba 7.
U C-u varijabla može biti deklarirana na globalnoj i lokalnoj razini. Razlika između ove dvije razine je to što je
globalna varijabla dostupna "cijelom programu" tj. svim funkcijama, a lokalna samo u funkciji ili petlji u kojem je
deklarirana. Primjer deklaracije varijabli na globalnoj i lokalnoj razini nalazi se u tablici 7.1.
GLOBALNA LOKALNA
#include <stdio.h>
int x = 11; //Globalna varijabla
int main(void) { printf("x je jednak: %d\n", x); return 0; }
#include <stdio.h>
int main(void) { int i = 5;
for(int j = i; j > 0; j--) printf("j je: %d\n", j );
printf("i je: %d \n", i); printf("konacni j je: %d", j); return 0; }
Tablica 7.1 Deklaracija varijabli na globalnoj i lokalnoj razini
U gornjem primjeru za deklariranje lokalne varijable, varijabla 'i' je lokalna i dostupna samo unutar main-a, dok
je varijabla 'j' vidljiva samo unutar for petlje pa će prevodilac javiti grešku "undeclared identifier" za zadnji printf.
Varijable unutar funkcije se brišu nakon što funkcija završi s radom, te se opet kreiraju pri ponovnom pozivanju
funkcije. Funkcije se najčešće koriste za određenu radnju koja se često koristi, pa se ne mora iznova pisati nego
samo pozvati.
Funkcija se kreira u tri koraka:
1. Deklaracija – ime funkcije, broj i tip argumenata koji se prosljeđuju u funkciju te povratna vrijednost
funkcije.
2. Definicija funkcije - uz ime, argumente i povratnu vrijednost sadrži kod s naredbama.
3. Poziv funkcije - mjesto u kodu gdje se poziva funkcija.
22
DIO KODA OPIS
#include <stdio.h>
int pravokut(int, int);
Deklaracija funkcije ‘pravokut’ iznad main-a.
int main(void) { int kv = 0, x = 2, y = 3; kv = pravokut(x, y); printf("x*y je: %d \n", kv); return 0; }
Varijabla ‘kv’ prima povratnu vrijednost funkcije.
Ispisuje se: x*y je: 6
int pravokut(int a, int b) { return a * b; }
Definicija funkcije povratnog tipa integer, prima argumente tipa integer i vraća njihov umnožak.
Takav pristup je posebno važan ukoliko funkcija poziva druge funkcije. Kad se ne bi kod pisao na ovaj način,
trebalo bi se voditi računa o rasporedu funkcija u kodu. Poželjno je da se najprije deklariraju funkcije, te se iza
main funkcije napišu njihove definicije . Postoje funkcije koje mogu vratiti vrijednost (int, double, char, itd.),
te one koje ne vraćaju vrijednost (void). Ukoliko funkcija vraća vrijednost, potrebno je tu vrijednost vratiti
naredbom return. Na mjestu gdje je return funkcija se prekida, a sav kod koji se eventualno nalazi iza
return neće izvršiti.
Ukoliko je funkcija void tada se poziva na sljedeći način:
ime_funkcije(arg1, arg2);
Ukoliko funkcija vraća vrijednost rada tada se vrijednost može ili spremiti u varijablu ili direktno koristiti:
x = ime_funkcije(arg1, arg2); //spremanje vrijednosti u varijablu
printf("%[neki format ispisa]", ime_funkcije(arg1, arg2)); /*direktno
korištenje vrijednosti za ispis */
Nakon što se izađe iz funkcije sve lokalne varijable se brišu. Tako da kada se ponovo uđe u funkciju, varijable
poprimaju početne vrijednosti. Ukoliko je potrebno da varijabla sadrži svoju vrijednost i nakon što izađe iz
funkcije, to se može napraviti korištenjem ključne riječi static. Ona omogućuje da kada se opet pozove
funkcija zadržava prethodnu vrijednost.
static int x = 0;
Osim static postoji i ključna riječ const koja se koristi za deklaraciju konstanti.
const broj = 123;
23
Neke funkcije iz zaglavlja <math.h> Datoteka zaglavlja <math.h> sadrži matematičke funkcije, kao npr.: potenciranje, korjenovanje, logaritmi, trigonometrija, itd. Neke od najčešće korištenih su definirane funkcije:
double exp(double X); //koristi se za računanje eX
double sqrt(double X); //koristi se za računanje √𝑋 double pow(double X, double Y); //koristi se za računanje XY
Kao što se može vidjeti, u zagradama se šalju vrijednosti koje su tipa različitog od int. Zbog toga, u slučaju da je X varijabla tipa int, potrebno je koristiti cast operator, npr.:(double)X, pa će poziv jednoj od gornjih funkcija izgledati ovako:
sqrt((double)varijabla); Također ne smije se zaboraviti da svaka od matematičkih funkcija ima povratnu vrijednost, pa tu vrijednost treba negdje spremiti ili ispisati. Pri tome treba voditi računa o tipu argumenta i povratnoj vrijednosti.
double rezultat = 0; int parametar = 25; rezultat = sqrt((double)parametar);
Povratna vrijednost je tipa double pa se mora spremiti u varijablu istog tipa.
ZADACI
1.Napisati program koji unese 3 broja i zatim po želji korisnika (ovisno o tome koje se slovo unese) računa:
a) 𝑟𝑒𝑧 = 𝑥 + 𝑦 + 𝑧 b) 𝑟𝑒𝑧 = 𝑥 ∗ 𝑦 ∗ 𝑧 c) 𝑟𝑒𝑧 = 𝑥 ÷ 𝑦 + 𝑧
d) 𝑟𝑒𝑧 = √𝑥 ÷ 𝑦 − 𝑧
Svi rezultati se računaju preko f-ija (jedna za svaki primjer), s tim da je za primjer pod “d” potrebno provjeriti je li izraz ispod korijena >= 0, i ako nije treba javiti grešku. 2. Napisati program koji sadrži dvije varijable: globalnu varijablu X i lokalnu varijablu Y(lokalna za f-ju main) i dodijeliti im neke vrijednosti. Iz glavnog programa treba pozvati f-ju koja ispisuje vrijednosti obije varijable.
3. Napraviti program koji u “običnoj” f-ji računa faktorijele. Rezultat je potrebno ispisati u glavnom programu i nije dozvoljeno korištenje globalnih varijabli.
4. Napisati f-ju kojom se približno određuje vrijednost ex (e = 2.718282), i rezultat usporediti s vrijednošću koja se dobije pomoću standardne funkcije exp() deklarirane u math.h. U rješavanju problema koristiti razvoj u red:
𝑒𝑥 = 1 + 𝑥
1!+
𝑥2
2!+
𝑥3
3!+
𝑥4
4!…
Npr. za x = 5, ispisati izračunate rezultate za 5, 10 i 50 ponavljanja. Zašto se program ponaša "čudno" za 50
ponavljanja?
24
Vježba 8.
U programiranju i matematici često se koriste rekurzivne funkcije. Direktna rekurzija nastaje kada se u definiciji
funkcije poziva ta ista funkcija, a indirektna rekurzija nastaje kada jedna funkcija poziva drugu funkciju, a ova
ponovo poziva funkciju iz koje je pozvana. Definicija rekurzivne funkcije u pravilu se sastoji od dva dijela:
temeljnog slučaja i pravila rekurzije. Jedan od najčešćih problema koji se koristi za opis rekurzivne funkcije su
faktorijeli. Pri rješavanju problema pomoću rekurzivne funkcije treba voditi računa o dvije stvari:
1. Prvo treba provjerit temeljni slučaj rekurzije.
2. Odradi pravilo rekurzivnog poziva.
unsigned int my_fact(unsigned int x)
{
if(x < 2) // Temeljni slučaj za brojeve manje od 2
return 1; // jer je: 0! == 1! == 1
else
return x * my_fact(x-1); /* Pravilo rekurzivnog poziva pr.: 3! =
3 * 2! */
}
1. Pozivanja rekurzije
my_fact(3); { return 3 * my_fact(2); { return 2 * my_fact(1); { return 1; //x je manji od 2 } } } 2. Vraćanje iznosa
//1. POVRAT
my_fact(3); { return 3 * my_fact(2); { return 2 * 1; //vracena jedinica i množi se s 2. } }
//2. POVRAT
my_fact(3); { return 3 * 2; //vraća iznos 6 u glavni program }
Tablica 8.1 Simulacija rada rekurzivne funkcije faktorijela za izračun 3!.
25
ZADACI
1. Napisati program koji traži je li neki broj element niza. U programu treba napraviti f-ju traži koja uzima tri
argumenta (niz, broj elemenata niza i vrijednost koja se traži), a treba vratiti 1 ako broj postoji ili 0 ako broj ne
postoji.
2. Napisati program koji računa produkt svih elemenata matrice dimenzija 3x3. Program ne smije sadržavati ni
jednu globalnu varijablu, unos i proračun napraviti u odvojenim funkcijama.
3. Napisati program koji sadrži f-je my_strlen i my_strcpy, koje rade isto što i f-je strlen i strcpy. Ispravnost
funkcija provjeriti usporedbom sa stvarnim f-jama strlen i strcpy.
4. Napisati program koji pomoću rekurzivne f-je računa faktorijele.
5. Napisati program koji korištenjem rekurzivnih funkcija traži minimalni, odnosno maksimalni element niza
brojeva.
6. Napraviti program koji uneseni dekadski broj pretvara u binarni, oktalni ili heksadecimalni. Dekadski broj, kao i
brojevna baza (2, 8, 16) u koju se radi pretvorba se unose u programu, a pretvorbu je potrebno realizirati pomoću
rekurzivne f-je. Rezultat se ispisuje u mainu.
26
Vježba 9.
Struktura je skup jedne ili više varijabli, grupiranih zajedno pod jednim imenom radi lakšeg rukovanja. Varijable
strukture su članovi ili polja strukture koje definira korisnik. Kao i ostale varijable, strukture mogu biti povratna
vrijednost neke funkcije. U daljnjem tekstu će se koristit prvo veliko slovo za ime strukture, dok se u programu
mogu, kao i kod običnih varijabli, koristiti drugi načini imenovanja.
struct Osoba {
int dan, mjesec, godina, niz[10];
char ime[15], prezime[15], spol;
};
Potrebno je naglasiti da se nakon zatvarajuće vitičaste zagrade stavlja točka-zarez. Definiranjem strukture se ne
kreira nova varijabla, niti se rezervira prostor u memoriji. Tek kada se deklariraju varijable tipa neke strukture
rezervira se prostor u memoriji. Primjer s gornjom strukturom:
struct Osoba Marko; /* U memoriji je rezervirano mjesto za varijablu tipa struct Osoba, a varijabla se zove Marko */
Pristup pojedinim elementima strukture se vrši pomoću točka operatora:
scanf(" %d", &Marko.dan);
printf("%d\n", Marko.dan);
Kod deklaracije strukture moguće je odmah deklarirati i varijable tipa te strukture, tako da se nakon zagrade
napišu varijable:
struct Osoba {
int dan, mjesec, godina;
char ime[15], prezime[15], spol;
} Marko, Ana, Petra, Josip; /* Može se deklarirati, ali nije preporučljivo.
Nije dozvoljena deklaracija globalnih varijabli i korištenje istih na kolegiju! */
Strukture se mogu deklarirati unutar neke funkcije (npr. main) ili pak izvan funkcija, razlika je u tome što u prvom
slučaju deklarirana struktura nije vidljiva ostalim funkcijama (sjetiti se dijela o lokalnim i globalnim varijablama!).
27
Niz struktura
struct Osoba nizOsoba[10];
Kreiran je niz od 10 varijabli tipa strukture Osoba. Pristup pojedinoj varijabli se vrši na sljedeći način npr.:
nizOsoba[2].dan //Pristupa se trećem elementu niza i to varijabli 'dan'.
Struktura unutar strukture
Definiranje strukture ‘Motor’ Def. strukture ‘Motor’ unutar strukture ‘Auto’.
struct Motor { int kubik, snagaKw; };
struct Auto { int godina; char proizvodac[15], model[15]; struct Motor motorizacija; };
//Deklaracija i pristupanje motoru
struct Auto Lancia; Lancia.Motor.snagaKw = 1750;
Tablica 9.1 Primjeri definicija struktura
ZADACI
1. Napisati program koji unosi dva proizvoljna vremena (sat, minute, sekunde) i zbraja ih. Za pohranu
vremena koristiti strukturu, a zbrajanje i ispis napraviti u odvojenim funkcijama.
2. Napisati program koji za uneseni datum računa koji je datum bio dan prije i koji će datum biti dan poslije.
Za pohranu datuma koristiti strukturu datum (dan, mjesec, godina). Proračun i ispis dana prije/poslije
napraviti u funkciji dan_prije / dan_poslije.
3. Napisati program koji unosi podatke o 5 osoba (ime, prezime, datum i sat rođenja). U programu napraviti
funkciju koja pronalazi najstariju osobu, te funkciju koja pronalazi osobu prvu po abecedi.
28
Vježba 10. Jedan od glavnih razloga zbog čega se C jezik smatra jezikom niske razine je taj što omogućuje indirektno
manipuliranje s podacima i izvršenjem programa. Kako bi se pristupilo adresi varijabli koriste se posebni
operatori - adresni operator & i operator indirekcije *, te specijalni tip varijabli koje se nazivaju pokazivačke
varijable ili pokazivači (engl. pointer).
Adresni operator &
Adresa varijable se može odrediti pomoću posebnog operatora & koji se naziva adresni operator. Koristi se kao
unarni operator koji se zapisuje ispred imena varijable.
#include <stdio.h> int main(void) { int y = 777; printf("\n Vrijednost y je %d", y); printf("\n Adresa y je %#p", &y); //# je za ispisivanje u heks. obliku return 0; }
Ispis programa može izgledati ovako:
Vrijednost y je 777
Adresa y je 0x0063FDF4
Tablica 10.1 Korištenje adresnog operatora
Ispis adrese je izvršen u heksadecimalnom obliku s osam znamenki, jer je korišteno 32-bitno računalo na kojem
je adresa određena 32-bitnim kardinalnim brojem. Napomena: pri sljedećem izvršavanju programa ispis adrese
ne mora biti isti jer operacijski sustav ne učitava program uvijek na isto mjesto u memoriji (time se mijenja i
adresa na kojoj se nalazi varijabla x).
Pokazivači
Varijable kojima je vrijednost adresa neke druge varijable ili funkcije nazivaju se pokazivači ili pointeri. Vrlo je
važno definirati na koji tip varijable pokazuje jer C prevoditelj mora znati kakav će tip podatka biti na adresi koju
oni sadrže.
Deklaracija pokazivača vrši se slično deklaraciji varijable, s razlikom što ispred imena varijable (imena pokazivača)
obvezno zapisuje znak indirekcije '*'. Primjerice,
int *p = NULL; /* p je pokazivac na objekt tipa int */
unsigned *q = NULL; /* q je pokazivac na objekt tipa unsigned */
Ovim deklaracijama definirane su dvije pokazivačke varijable. Njihova vrijednost je neodređena, jer im nije
pridijeljena adresa neke varijable. Važno je znati da pokazivače prije upotrebe treba inicijalizirati, odnosno mora
im se pridijeliti vrijednost adrese postojeće varijable. Pri tome, tip pokazivača mora biti jednak tipu varijable. To
se ostvaruje adresnim operatorom '&'.
int suma = 777; /* deklaracija i inicijalizacija varijable suma */
int *p = NULL; /* deklaracija pokazivaca na objekt tipa int */
p = &suma; /* p inicijaliziran na adresu varijable suma */
29
Pokazivač ‘p’ je inicijaliziran da pokazuje na varijablu ‘suma’. Daljnje korištenje
printf("%d", suma);
printf("%d", *p);
daje isti ispis, jer se indirekcijom pokazivača dobiva vrijednost na koju on pokazuje, a to je vrijednost varijable
suma.
ZADACI
1. Napisati program koji prvo unosi, a zatim ispisuje elemente nekog polja brojeva i polja znakova zajedno s
adresom svakog elementa. Sve unose i ispise je potrebno napraviti preko pokazivača i ne smiju se koristiti [].
2. Napisati program koji u jednoj funkciji računa sumu, produkt i razliku dva broja, a svi se rezultati ispisuju u
glavnom programu.
3. Zadatak s računanjem dana prije i poslije riješiti preko funkcija na način da se sva tri datuma deklariraju u
mainu, a unos, i proračun dana prije i poslije u f-ji. Rezultate je potrebno ispisati u mainu, a ne u funkciji.
4. Napisati program koji za zadani string traži koliko se puta podstring pojavljuje u stringu. Podstring je potrebno
unijeti u f-ji. F-ja za pretraživanje treba vratiti broj ponavljanja. U funkciji za pretraživanje se može koristiti f-ja
strncmp iz string.h.
30
Vježba 11. U standardnoj biblioteci <stdlib.h> implementiran je niz funkcija koje se koriste za sve ulazno izlazne
operacije: unos s tipkovnice, ispis na ekran te čitanje i pisanje informacija koje se pohranjuju na magnetskim i
optičkim medijima. Komuniciranje s uređajima koji obavljaju ove operacije vrši se sekvencijalno bajt po bajt, a
programski mehanizam kojim se vrši ovakvi prijenos informacije naziva se tok (engl. stream). U jednom se
programu može raditi s više tokova. Svakom toku se pridjeljuje jedna struktura podataka imena FILE, koja je
definirana u <stdio.h>. Temeljna namjena te strukture je da služi kao memorijski ulazno/izlazni
međuspremnik (engl. I/O buffer) pri prijenosu podataka.
Tokovi se dijele u četiri grupe:
• standardni ulaz (vrši unos znakova s tipkovnice),
• standardni izlaz (vrši ispis na ekran),
• standardna dojava greške (obično se vrši ispis na ekran),
• datotečni tok (vrši čitanje ili pisanje podataka u datoteku).
1. Funkcije fopen() i fopen_s()
Da bi se moglo koristiti neku datoteku potrebno je od operacijskog sustava zatražiti dozvolu pristupa toj
datoteci. Taj proces se zove otvaranje datoteke. Isto tako se za kreiranje nove datoteke mora zatražiti dozvola od
operacijskog sustava. Tu funkciju obavlja standardna funkcija fopen(). Ona pored komunikacije s operacijskim
sustavom kreira datotečni tok koji sadrži memorijski međuspremnik za efikasno čitanje ili spremanje podataka
na disk.
Prototip funkcije fopen() je:
FILE *fopen(const char *ime_datoteke, const char *mod);
Može se koristiti i ‘sigurna’ verzija fopen_s()
errno_t fopen_s(FILE **fp, const char *ime_datoteke, const char *mod);
koja vraća poruku o grešci u strukturu tipa errno_t umjesto vrijednosti NULL ukoliko dođe do greške otvaranja datoteke. Modovi otvaranja datoteke su opisani u tablici 11.1.
Mod Opis
"r" Otvori datoteku za čitanje, ako datoteka ne postoji fopen() vraća NULL.
"w" Otvori datoteku za pisanje, ako ne postoji datoteka zadanog imena, kreira se nova.
"a" Otvori datoteku za dopunu sadržaja, ako ne postoji datoteka zadanog imena, kreira se nova.
"r+" Otvori datoteku za čitanje i pisanje . Ako ne postoji datoteka zadanog imena, kreira se nova.
"w+" Isto kao "r+"
"a+" Otvori datoteku za čitanje i dopunu. Ako ne postoji datoteka zadanog imena, kreira se nova.
"b" Ako se iza slova w, r ili a još zapiše slovo 'b' to označava da se datoteku treba otvoriti u binarnom modu.
Tablica 11.1 Modovi za način otvaranja datoteke.
31
Dobra je praksa da se uvijek provjeri je li datoteka otvorena bez greške. Primjerice, za otvoriti datoteku imena "hello.txt" u koju će se nešto upisati koriste se sljedeće naredbe:
FILE *fp;
fp = fopen("hello.txt", "w");
if(fp == NULL)
printf("Greska pri otvaranju datoteke");
2. fclose() funkcija Zatvaranje datoteke se vrši funkcijom fclose() čiji je prototip:
int fclose(FILE *fp);
U koliko se ne pozove funkcija fclose() datoteka se automatski zatvara kada se dođe do kraja programskog bloka u kojem je otvorena.
3. feof() funkcija Za detektiranje kraja datoteke predviđena je posebna funkcija:
int feof(FILE *fp);
koja vraća vrijednost različitu od nule ako je dosegnut kraj datoteke. Nakon toga više nije moguće čitanje iz datoteke. 4. Funkcije za rad s datotekama
getc() , putc() – upravljaju sa znakovima fscanf(), fprintf() – upravljaju sa formatirano zapisanim znakovima fgets(), fputs() – upravljaju sa nizom znakova na razini retka
5. fprintf() funkcija
Kada je datoteka otvorena, koristi je se kao tok. Primjerice, naredbama
fprintf(fp, "Hello World!\n"); fprintf(fp, "Hello World drugi put!");
u prethodno otvorenoj datoteci "hello.txt" biti će zapisane dvije linije teksta:
Hello World! Hello World drugi put!
32
6. fscanf() funkcija
Za formatirano čitanje sadržaja datoteke koristi se fscanf() funkcija, koja je poopćeni oblik scanf() funkcije za dobavu podataka iz ulaznih tokova. Prototip fscanf() funkcije je:
int fscanf(FILE *fp, const char *fmt, ...);
Parametar fp je pokazivač ulaznog toka, koji može biti standardni ili datotečni tok koji se dobije kada se datoteka otvori s atributom "r", "r+" ili "w+". String 'fmt' služi za specifikaciju formata po kojem se učitava vrijednost varijabli, čije adrese se koriste kao argumenti funkcije. Tri točke označavaju proizvoljan broj argumenata, uz uvjet da svakom argumentu mora pripadati po jedan specifikator formata u stringu 'fmt'.
7. fscanf_s() funkcija
int fscanf_s(FILE *fp, const char *fmt, ...);
Funkcija slična fscanf(), samo sa dodatnom kontrolom sigurnosti.
ZADACI
1. Napisati program koji kopira jednu datoteku u drugu. Nazivi obije datoteke se unose u glavnom programu, a
kopiranje se radi u funkciji.
2. Napisati program koji iz datoteke znak, po znak čita znamenke sve dok ne pročita nešto što nije broj ili se
dostigne kraj datoteke. Te znamenke je potrebno pretvoriti u broj koji je bio zapisan u datoteci i ispisati ga na
ekran.
3. Napisati program koji iz datoteke čita maks. 100 podataka o osobama (ime, prezime i godinu rođenja) i traži
koliko se osoba zove Ana. Čitanje datoteke i brojenje je potrebno napraviti u odvojenim f-jama.
4. Napisati program koji iz datoteke pročita 1 cijeli redak i onda ga riječ po riječ ispisuje na ekran, a svaka je riječ
u svom retku.
- Datoteke potrebne za ove zadatke je potrebno napraviti u nekom tekst. editoru (npr. Notepad, …).
33
Vježba 12.
Programski jezik C omogućuje da se pri potrebi za vrijeme izvršavanja programa rezervira dodatna memorija za
pohranu varijabli. Taj postupak se naziva alokacija, a koristan je npr. ukoliko prije pokretanja programa nije
poznata veličina niza. Dinamičko alociranje memorije omogućuje da se tijekom rada programa rezervira
određeni dio memorije za određenu varijablu.
Dinamička alokacija se radi sa sljedećim naredbama koje su definirane u <stdlib.h>:
malloc() - alocira određeni broj bajtova u memoriji.
calloc() - alocira određeni broj bajtova u memoriji, te svaki bajt postavi na nulu.
realloc() - koristi se za promjenu veličine ranije alociranog prostora u memoriji.
free() - oslobodi rezervirani dio memorije.
Za alociranje je potrebno koristiti pokazivače. Din. alociranje memorije za niz brojeva s 10 elemenata:
int *nizBrojeva = (int *)malloc(10 * sizeof(int));
Ako pokazivač ima vrijednost NULL znači da alokacija nije uspjela, pa je poželjno da se nakon svake alokacije
provjeri je li uspjela ili ne. Isto ne smije se zaboraviti koristiti funkciju free() na kraju programa, inače će nakon
zatvaranja programa ostati alociran blok memorije kojem operacijski sustav, a ni program, više ne mogu
pristupiti, te jedino preostaje ponovno pokretanje računala da bi se taj blok memorije oslobodio.
int *nizBrojeva = (int *)malloc(x * sizeof(int));
if(nizBrojeva == NULL)
{
printf("Alokacija memorije nije uspjela!\n");
return -1;
}
Pristup elementima je isto kao i kod nizova!
for(int i = 0; i<x; i++)
scanf(" %d", &niz[i]); //Unos elemenata u alociran niz
Dinamička alokacija niza struktura:
struct Auto *niz_Auta = (struct Auto *)malloc(n * sizeof(struct Auto));
if(niz_Auta == NULL)
{
printf("Alokacija memorije nije uspjela!\n");
return -1;
}
34
ZADACI
1. Napisati program koji unosi bodove kolokvija svih studenata i računa prosječni broj bodova, nakon što su
ocjene unesene, uz uvjet da unaprijed nije poznato koliko je bilo studenata.
2. Napisati program koji neki dekadski broj pretvara u string koji se sastoji od znamenki tog broja. String se
dinamički alocira ovisno o tome koliko znamenki ima broj.
3. Napisati program koji čita imena N (unosi se u programu) studenata iz datoteke, a zatim za svakog studenta
pita koliko je bodova dobio iz svakog od kolokvija(ukupno 3) i zatim računa ukupni broj bodova i ocjenu, prema
tablici:
Bodovi Ocjena
50 - 60% => 2
61 – 74% => 3
75 – 87% => 4
88 -100% => 5
4.Napisati program koji pomoću nizova simulira rad reda (podaci se skidaju spočetka, a stižu na kraj). Red mora
biti cirkularan, a dimenzija se unosi s tastature.
35
Reference:
[1] Ivo Mateljan: “Programiranje C jezikom“, Sveučilište u Splitu, 2005.
[2] Predavanja i prezentacije kolegija Programiranje 2:
- doc. dr. sc. Linda Vicković
- dipl. ing. Ivica Crnjac
top related