Moj C vodic.
Pisao sam ovo dok sam ucio C u srednjoj skoli. Pomoglo mi je da slozim sve u glavi, mozda ti pomogne isto. Svaka linija koda objasnjenja od pocetka, nema preskakanja.
Tvoj prvi program
Ovo je Hello World. Svi pocinju s ovim, bez iznimke. Pet linija koda koje ces znati napamet do kraja tjedna jer ces ih pisati sto puta. Kopiraj, pokreni, vidi da radi — pa cemo objasniti svaku liniju ispod.
#include <stdio.h>
int main() {
printf("Zdravo, svijete!\n");
return 0;
}
stdio.h bez koje printf ne radi. Stavljaj ovo na vrh gotovo svakog programa.main — od tuda pocinje izvrsavanje. int ispred znaci da funkcija vraca broj. Sve unutar { } je tijelo funkcije.printf ispisuje tekst. Tekst mora biti u navodnicima. \n je novi red. Na kraju obavezno ; — vrijedi za svaku naredbu u C-u.return 0; govori OS-u da je program zavrsio bez gresaka. 0 = sve ok.printf — vise primjera ispisa
#include <stdio.h>
int main() {
// Ispis teksta
printf("Ime: Ana\n");
printf("Dob: %d godina\n", 17);
printf("Visina: %.2f m\n", 1.68);
printf("Slovo: %c\n", 'A');
// Vise vrijednosti u jednom printf
printf("%s ima %d godina i visoka je %.1f m\n", "Ana", 17, 1.68);
// Escape sekvence
printf("Red 1\nRed 2\nRed 3\n");
printf("Stupac1\tStupac2\tStupac3\n");
return 0;
}
printf("%d", 42) ispisuje 42.%.2f ispisuje s tocno 2 decimale.printf("%c", 'A') ispisuje A.printf("%s", "Ana") ispisuje Ana.\n — novi red | \t — tab |
\\ — obrnuta kosa crta | \" — navodnik unutar stringa
Varijable i tipovi podataka
Varijabla je kutija s imenom — u njoj spremasˇ podatak. C zahtijeva da unaprijed kazes kakav tip podatka stavljas (broj, decimalni, slovo...) i to se ne moze mijenjati nakon deklaracije.
Osnovni tipovi podataka
| Tip | Velicina | Raspon vrijednosti | Primjer |
|---|---|---|---|
| int | 4 bajta | -2,147,483,648 do 2,147,483,647 | 42, -7, 1000 |
| float | 4 bajta | ~7 decimala preciznosti | 3.14, -1.5 |
| double | 8 bajta | ~15 decimala preciznosti | 3.14159265358 |
| char | 1 bajt | jedan znak | 'A', 'z', '5' |
| long | 8 bajta | veci raspon od int | 9000000000 |
#include <stdio.h>
int main() {
int godine = 20;
float visina = 1.85;
double pi = 3.14159265358979;
char slovo = 'A';
printf("Imam %d godina.\n", godine);
printf("Visok sam %.2f metara.\n", visina);
printf("Pi je otprilike %.10f\n", pi);
printf("Prvo slovo: %c\n", slovo);
return 0;
}
int s imenom godine i odmah joj das vrijednost 20. = je dodjela, nije usporedba. Usporedba se pise ==.float je decimalni broj. Za vecinu stvari sasvim ok.char cuva jedan znak, uvijek u jednostrukim navodnicima. 'A' je drugacije od "A" — prvo je char, drugo je string.printf: %d za int, %f za float/double, %c za char, %s za string. %.2f znaci ispiši s 2 decimale.snake_case — sve malo, podvlaka između.
Npr. broj_studenata, ukupna_cijena. Ne mozes poceti s brojem i nema razmaka.
Operatori
Operatori su ono sto poznajes iz matematike plus jos neke. +, -, *, / rade kao sto mislis. Ali ima i onih koji te mogu zbuniti — posebno razlika između = i ==.
#include <stdio.h>
int main() {
int a = 10, b = 3;
printf("%d + %d = %d\n", a, b, a + b);
printf("%d - %d = %d\n", a, b, a - b);
printf("%d * %d = %d\n", a, b, a * b);
printf("%d / %d = %d\n", a, b, a / b); // rezultat: 3, ne 3.33!
printf("%d %% %d = %d\n", a, b, a % b); // ostatak: 1
int x = 5;
x++;
printf("Nakon x++: %d\n", x); // 6
return 0;
}
int-a daje int — 10 / 3 = 3, ne 3.33. Ako hoces decimalni rezultat, castaj: (float)a / b.% je modulo — vraca ostatak dijeljenja. Koristit ces ovo stalno, posebno za parnost: broj % 2 == 0 = parni broj.= je dodjela, == je usporedba.
Ako napises if (a = 5) umjesto if (a == 5), program se kompajlira ali radi krivo.
Svaki pocetnik to napravi barem jednom.
Citanje unosa — scanf
Do sada su svi programi imali unaprijed zadane podatke u kodu.
U stvarnosti, program mora citati unos od korisnika. Funkcija scanf
cita podatke s tipkovnice — odmah je korisna cim naucis varijable i operatore,
pa dolazi ovdje, prije uvjeta i petlji.
#include <stdio.h>
int main() {
int a, b;
printf("Unesi prvi broj: ");
scanf("%d", &a);
printf("Unesi drugi broj: ");
scanf("%d", &b);
printf("\nZbroj: %d\n", a + b);
printf("Razlika: %d\n", a - b);
printf("Produkt: %d\n", a * b);
if (b != 0) {
printf("Kolicnik: %.2f\n", (float)a / b);
} else {
printf("Dijeljenje s nulom nije moguce.\n");
}
return 0;
}
scanf("%d", &a) — cita cijeli broj s tipkovnice i sprema ga u varijablu a. Zasto & (ampersand)? Zato sto scanf mora znati gdje u memoriji da spremi podatak. Dajemo mu adresu varijable a putem &a. Bez & program se rusi!Citanje vise tipova — mini kalkulator
#include <stdio.h>
int main() {
float a, b;
char operacija;
printf("=== Mini kalkulator ===\n");
printf("Unesi racun (npr. 5 + 3): ");
scanf("%f %c %f", &a, &operacija, &b);
printf("%.2f %c %.2f = ", a, operacija, b);
switch (operacija) {
case '+': printf("%.2f\n", a + b); break;
case '-': printf("%.2f\n", a - b); break;
case '*': printf("%.2f\n", a * b); break;
case '/':
if (b != 0) printf("%.2f\n", a / b);
else printf("Greska: dijeljenje s nulom\n");
break;
default:
printf("Nepoznata operacija.\n");
}
return 0;
}
scanf("%f %c %f", &a, &operacija, &b) — cita tri vrijednosti odjednom: float, char i float. Format stringa definira sto se ocekuje.switch — elegantnija alternativa dugom if-else if lancu kad usporedujemo jednu varijablu s vise mogucih vrijednosti. break izlazi iz switch bloka, default hvata sve ostalo.%d — int | %f — float |
%lf — double | %c — char |
%s — string (bez razmaka)Uvijek stavljaj & ispred varijable, osim kod stringa!
Uvjetni iskazi — if / else
if / else je kako program donosi odluke. Ako je uvjet tocan, izvrsi jedno — inace drugo. U C-u nema pravog bool tipa: 0 je false, sve ostalo (1, -5, 42...) je true.
#include <stdio.h>
int main() {
int rezultat = 75;
if (rezultat >= 90) {
printf("Odlican (5)\n");
} else if (rezultat >= 75) {
printf("Vrlo dobar (4)\n");
} else if (rezultat >= 60) {
printf("Dobar (3)\n");
} else if (rezultat >= 50) {
printf("Dovoljan (2)\n");
} else {
printf("Nedovoljan (1)\n");
}
int broj = 7;
if (broj % 2 == 0) {
printf("%d je paran broj.\n", broj);
} else {
printf("%d je neparan broj.\n", broj);
}
return 0;
}
else na kraju hvata sve sto nije proslo ni jedan uvjet.broj % 2 == 0 je standardni nacin provjere parnosti. Koristit ces ovo cesto.#include <stdio.h>
int main() {
int dob = 22;
int ima_vozacku = 1; // 1 = true, 0 = false
// && = I (oba uvjeta moraju biti istinita)
if (dob >= 18 && ima_vozacku) {
printf("Moze voziti.\n");
}
// || = ILI (barem jedan uvjet mora biti istinit)
int je_vikend = 0;
int je_praznik = 1;
if (je_vikend || je_praznik) {
printf("Ne radi se!\n");
}
// ! = negacija
int je_zatvoren = 0;
if (!je_zatvoren) {
printf("Ducan je otvoren.\n");
}
return 0;
}
!je_zatvoren = nije zatvoren = otvoren je.for petlja
Petlja ponavlja blok koda. Bez petlji, za ispis 100 redova trebas 100 linija.
S petljom, trebas 3. for koristis kada znas tocno koliko puta ponavljati.
Kako radi for petlja
int i = 0i < 10{ }i++ — pa nazad na korak 2#include <stdio.h>
int main() {
// Ispis brojeva od 1 do 5
for (int i = 1; i <= 5; i++) {
printf("Broj: %d\n", i);
}
printf("---\n");
// Zbrajanje od 1 do 100
int suma = 0;
for (int i = 1; i <= 100; i++) {
suma += i;
}
printf("Suma 1 do 100 = %d\n", suma); // 5050
return 0;
}
;: 1) int i = 1 — postavi brojac, jednom. 2) i <= 5 — provjera pred svaki prolaz. 3) i++ — pomak nakon svakog prolaza.suma += i je skraceni zapis za suma = suma + i. Nakon svakog prolaza dodamo trenutni i na ukupnu sumu.#include <stdio.h>
int main() {
// Odbrojavanje
for (int i = 5; i >= 1; i--) {
printf("%d... ", i);
}
printf("Start!\n\n");
// Korak od 2 (samo parni)
printf("Parni od 2 do 10: ");
for (int i = 2; i <= 10; i += 2) {
printf("%d ", i);
}
printf("\n");
return 0;
}
break — odmah izlazi iz petlje.continue — preskace trenutni prolaz i ide na sljedeci.Npr.
if (i == 3) continue; — sve normalno osim broja 3 koji se preskace.
while petlja
while koristis kad ne znas unaprijed koliko ponavljanja treba — samo znas uvjet.
Npr. ponavljaj dok je igrac ziv, citaj dok ima podataka, pitaj korisnika dok ne unese nesto ispravno.
{ }#include <stdio.h>
int main() {
// Dijeli broj s 2 dok ne postane manji od 1
int broj = 100;
printf("Pocetni broj: %d\n", broj);
while (broj >= 1) {
printf("%d -> ", broj);
broj = broj / 2;
}
printf("kraj\n\n");
// Digitalna suma broja (zbroj znamenki)
int n = 12345;
int suma = 0;
while (n > 0) {
int znamenka = n % 10; // izvuci zadnju znamenku
suma += znamenka;
n = n / 10; // ukloni zadnju znamenku
}
printf("Suma znamenki broja 12345: %d\n", suma); // 15
return 0;
}
broj = broj / 2 — ovo je korak koji mijenja stanje. Bez ovoga, broj se nikad ne bi promijenio i dobili bismo beskonacnu petlju. Uvijek se pitaj: "Sto mijenjam unutar petlje da bi uvjet jednom postao lazan?"n % 10 je trik za izvlacenje zadnje znamenke. 12345 % 10 = 5. Zatim n / 10 = 1234 brise zadnju znamenku.do-while — uvjet se provjerava na kraju
#include <stdio.h>
int main() {
int broj;
// Trazi unos dok korisnik ne unese pozitivan broj
do {
printf("Unesi pozitivan broj: ");
scanf("%d", &broj);
if (broj <= 0) {
printf("Krivo! Mora biti > 0.\n");
}
} while (broj <= 0);
printf("Unio si: %d\n", broj);
return 0;
}
do {} izvodi tijelo barem jednom bez obzira na uvjet. Savrseno za validaciju unosa — uvijek moraš pitati korisnika barem jednom.} while (uvjet); — jedina petlja u C-u koja zahtijeva ; iza zatvorene zagrade.for — znas tocan broj ponavljanja (petlja N puta)while — ne znas broj ponavljanja, provjera je na pocetkudo-while — tijelo mora izvesti barem jednom, provjera na kraju
Kombinacije petlji
Petlje postaju puno mocnije kad ih kombiniras — petlja unutar petlje, uvjeti unutar petlji, for i while zajedno. Ovo su uzorci koje ces koristiti stalno na testovima i u prakticnom radu.
Ugnjezdene for petlje — tablice i uzorci
#include <stdio.h>
int main() {
printf("Tablica mnozenja (1-5):\n\n");
printf(" ");
for (int j = 1; j <= 5; j++) printf("%4d", j);
printf("\n --------------------\n");
for (int i = 1; i <= 5; i++) {
printf("%2d |", i); // ispisi red-oznaku
for (int j = 1; j <= 5; j++) {
printf("%4d", i * j); // unutarnja petlja: stupci
}
printf("\n"); // kraj retka
}
return 0;
}
i). Za svaki i, unutarnja petlja prolazi kroz sve stupce.j). Prolazi svih 5 vrijednosti za svaki jedan prolaz vanjske petlje. Ukupno: 5 x 5 = 25 izvrsavanja.Uzorci od zvjezdica
#include <stdio.h>
int main() {
int n = 5;
// Desni trokut
printf("Desni trokut:\n");
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
printf("* ");
}
printf("\n");
}
printf("\n");
// Piramida (centrirana)
printf("Piramida:\n");
for (int i = 1; i <= n; i++) {
// Ispis razmaka za centriranje
for (int j = 0; j < n - i; j++) {
printf(" ");
}
// Ispis zvjezdica
for (int j = 0; j < 2 * i - 1; j++) {
printf("*");
}
printf("\n");
}
return 0;
}
i ispisuje tocno i zvjezdica. Linija 1 → 1 zvjezdica, linija 2 → 2 zvjezdice, itd. Unutarnja petlja ide od 1 do i.i ima n-i razmaka i 2*i-1 zvjezdica. Provjeri: red 1 = 4 razmaka + 1 zvjezdica, red 5 = 0 razmaka + 9 zvjezdica.for + uvjet — filtriranje i trazenje
#include <stdio.h>
int main() {
int gornja_granica = 30;
printf("Prosti brojevi do %d:\n", gornja_granica);
for (int n = 2; n <= gornja_granica; n++) {
int je_prost = 1; // pretpostavi da je prost
// Provjeri sve djelitelje od 2 do n-1
for (int d = 2; d < n; d++) {
if (n % d == 0) {
je_prost = 0; // nasao djeljitelja, nije prost
break; // nema smisla dalje provjeravati
}
}
if (je_prost) {
printf("%d ", n);
}
}
printf("\n");
return 0;
}
break izlazi samo iz unutarnje petlje, vanjska se nastavlja.while + for — pretraga niza
#include <stdio.h>
int main() {
int ocjene[6] = {3, 5, 2, 4, 5, 3};
int n = 6;
// Ispis niza
printf("Ocjene: ");
for (int i = 0; i < n; i++) printf("%d ", ocjene[i]);
printf("\n");
// Pronalazak pozicije prve maksimalne ocjene
int max = ocjene[0];
int pozicija = 0;
for (int i = 1; i < n; i++) {
if (ocjene[i] > max) {
max = ocjene[i];
pozicija = i;
}
}
printf("Maksimalna ocjena %d je na poziciji %d\n", max, pozicija);
// Bubble sort (razvrstavanje od najmanjeg prema najvecem)
printf("\nSortiranje (bubble sort):\n");
int promjena = 1;
while (promjena) {
promjena = 0; // pretpostavi da nema zamjene
for (int i = 0; i < n - 1; i++) {
if (ocjene[i] > ocjene[i + 1]) {
// Zamjena elemenata
int temp = ocjene[i];
ocjene[i] = ocjene[i + 1];
ocjene[i + 1] = temp;
promjena = 1; // bila je zamjena, treba jos jedan prolaz
}
}
}
printf("Sortirano: ");
for (int i = 0; i < n; i++) printf("%d ", ocjene[i]);
printf("\n");
return 0;
}
while kontrolira "prolaze kroz niz" — ne znamo unaprijed koliko prolaza treba. Ponavljamo dok god u zadnjem prolazu bude barem jedna zamjena.for prolazi kroz parove susjednih elemenata. Ako je lijevi veci od desnog, zamijeni ih. Nakon cijelog prolaza, ako nije bilo ni jedne zamjene, niz je sortiran.temp. Bez nje, izgubio bi jednu vrijednost.• Obrada 2D podataka (tablice, matrice, uzorci)
• Usporedba svakog elementa sa svakim (sortiranje, trazenje)
• Generiranje kombinacija ili permutacija
Paznja: dvije ugnjezdene petlje do N = N² operacija. Tri ugnjezdene = N³. Brzo raste!
Funkcije
Funkcija je imenovan blok koda koji radi odredenu zadacu. Umjesto da isti kod pises na 10 mjesta, napises ga jednom kao funkciju i pozoves je gdje god trebas. Dobar program je skup malih, jasnih funkcija.
#include <stdio.h>
// Funkcija koja prima dva broja i vraca njihov zbroj
int zbroji(int a, int b) {
return a + b;
}
// Funkcija bez povratne vrijednosti (void)
void ispisi_liniju(int duljina) {
for (int i = 0; i < duljina; i++) {
printf("-");
}
printf("\n");
}
// Rekurzivna funkcija za faktorijel
int faktorijel(int n) {
if (n <= 1) return 1;
return n * faktorijel(n - 1);
}
int main() {
int x = 8, y = 3;
printf("%d + %d = %d\n", x, y, zbroji(x, y));
ispisi_liniju(20);
printf("5! = %d\n", faktorijel(5));
printf("7! = %d\n", faktorijel(7));
return 0;
}
int zbroji(int a, int b) — anatomija funkcije: int = povratni tip, zbroji = ime, (int a, int b) = parametri s tipovima.void ispisi_liniju — void znaci da funkcija ne vraca nikakvu vrijednost. Koristi se za funkcije koje samo "rade nesto".faktorijel(5) = 5 * faktorijel(4) = ... dok ne dode do faktorijel(1) = 1. Svaka rekurzija mora imati bazni slucaj koji zaustavi pozivanje!Nizovi (Arrays)
Niz je kolekcija varijabli istog tipa smjestena uzastopno u memoriji.
Umjesto 5 zasebnih varijabli ocjena1, ocjena2, ..., imas jedan
niz ocjene[5]. Svi elementi su dostupni putem indeksa koji pocinje od 0.
#include <stdio.h>
int main() {
int ocjene[5] = {4, 3, 5, 4, 5};
// Ispis svakog elementa
for (int i = 0; i < 5; i++) {
printf("Ocjena %d: %d\n", i + 1, ocjene[i]);
}
// Pronalazak maksimuma
int max = ocjene[0];
for (int i = 1; i < 5; i++) {
if (ocjene[i] > max) max = ocjene[i];
}
printf("Maksimalna ocjena: %d\n", max);
// Izracun prosjeka
int suma = 0;
for (int i = 0; i < 5; i++) suma += ocjene[i];
printf("Prosjek: %.1f\n", (float)suma / 5);
return 0;
}
int ocjene[5] = {4, 3, 5, 4, 5}; — deklaracija i inicijalizacija. Elementi su: ocjene[0]=4, ocjene[1]=3... Indeks pocinje od 0! Zadnji element je ocjene[4], ne ocjene[5].(float)suma / 5 — cast pretvara suma u float przed dijeljenjem, da dobijemo decimalni rezultat. Bez toga: 21 / 5 = 4, ne 4.2.Stringovi
U C-u ne postoji poseban tip za tekst — string je zapravo
niz znakova (char array) koji zavrsava specijalnim
znakom '\0' (null terminator). To je jedini nacin da
funkcije znaju gdje string zavrsava.
#include <stdio.h>
#include <string.h>
int main() {
char ime[50] = "Ana";
char prezime[50] = "Kovac";
char puno_ime[100];
// Spajanje stringova
strcpy(puno_ime, ime); // kopiraj "Ana" u puno_ime
strcat(puno_ime, " "); // dodaj razmak
strcat(puno_ime, prezime); // dodaj "Kovac"
printf("Ime: %s\n", puno_ime);
printf("Duljina: %zu znaka\n", strlen(puno_ime));
// Usporedba stringova
char lozinka[20] = "tajno123";
char unos[20] = "tajno123";
if (strcmp(lozinka, unos) == 0) {
printf("Lozinka tocna!\n");
} else {
printf("Pogresna lozinka.\n");
}
return 0;
}
#include <string.h> — biblioteka s funkcijama za rad sa stringovima. Bez nje ne mozemo koristiti strcpy, strcat, strlen, strcmp.strcpy(odrediste, izvor) — kopira string. Ne mozes koristiti = za kopiranje stringova. Uvijek koristi strcpy.strcmp(s1, s2) — usporeduje dva stringa. Vraca 0 ako su jednaki. Nikad ne koristi == za usporedbu stringova!Pokazivaci (Pointers)
Pokazivac je varijabla koja cuva memorijsku adresu neke druge varijable, umjesto same vrijednosti. Ovo je tema koja zbunjuje mnoge pocetnike — ali je temelj C-a. Operativni sustavi, driveri i sve visoko-performantne strukture podataka ovise o pokazivacima.
Vizualni prikaz memorije
Varijabla ptr cuva adresu varijable broj, ne njenu vrijednost.
#include <stdio.h>
int main() {
int broj = 42;
int *ptr = &broj; // ptr cuva adresu varijable broj
printf("Vrijednost broja: %d\n", broj);
printf("Adresa broja: %p\n", (void*)&broj);
printf("Vrijednost ptr-a: %p\n", (void*)ptr);
printf("Dereferenciranje ptr: %d\n", *ptr);
// Promjena vrijednosti PUTEM pokazivaca
*ptr = 100;
printf("\nNakon *ptr = 100:\n");
printf("broj = %d\n", broj); // ispisuje 100!
return 0;
}
int *ptr = &broj;& = address-of operator — daj mi adresu varijable broj.int * = tip "pokazivac na int". ptr sada sadrzi memorijsku adresu gdje zivi varijabla broj.*ptr = dereferenciranje — "idi na adresu koju ptr cuva i procitaj vrijednost tamo". Zvjezdica ovdje znaci nesto drugo nego pri deklaraciji.*ptr = 100 — pisi 100 na adresu gdje ptr pokazuje. Budući da ptr pokazuje na broj, ovo mijenja broj. Linija 15 ispisuje 100 iako smo mijenjali putem pokazivaca.NULL: int *ptr = NULL;. Dereferenciranje NULL pokazivaca
uzrokuje crash programa (Segmentation fault). Provjeri: if (ptr != NULL) { ... }
Sada znas: printf, varijable, tipove, operatore, scanf, uvjete, petlje (for/while/do-while), kombinacije petlji, funkcije, nizove, stringove i pokazivace. To su temelji C-a — sve ostalo (strukture, dinamicka alokacija, datoteke) gradi se na ovome.