You are on page 1of 40

Univerzitet u Sarajevu

Elektrotehniki fakultet Sarajevo

Osnove raunarstva 2015/2016

Preprocesorske direktive
i struktura programa
Doc. dr Vedran Ljubovi

Preprocesorske direktive

Preprocesor je program koji se izvrava prije kompajliranja


koda i transformie kod na odreeni nain.
Preprocesorske direktive (naredbe za preprocesor) poinju
znakom #
Do sada smo radili dvije preprocesorske direktive:
#include <stdio.h>

ukljuivanje biblioteka
#define PI 3.1415926

7.1.2016

simbolike konstante

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

Direktiva #define
#define PI 3.1415926

Kada smo definisali simboliku konstantu, mi smo ustvari


kreirali makro substituciju. Prije kompajliranja vrijednost
makroa e biti zamijenjena u kodu. Npr.
#define pisi printf
...
pisi("Ovo ce se ispisati");

Ili recimo
#define y x
...
x = y + 1;

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

Makro sa parametrima

Makroi mogu imati parametre npr.:


#define kvadrat(x) ((x)*(x))
...
printf("%d", kvadrat(5));

Obratite panju da makro nije funkcija! Ovdje e se prije


kompajliranja u kod izvriti supstitucija u:
printf("%d", ((5)*(5)));

Prednost je to se makro izvrava znatno bre od poziva


funkcije.

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

Makro sa parametrima

Treba paziti na zagrade zbog prioriteta operatora. Npr. da smo


definisali:
#define kvadrat(x) x*x

U tom sluaju
int x = kvadrat(x+2)-x;

bi dalo
int x = x+2*x+2-x;

to je oito netano.

Jo jedan koristan primjer:


#define MAX(x,y) ((x)>(y) ? (x) : (y))

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

Makro operatori

Operator stringifikacije # pretvara parametar u string:


#define ispisi(x) printf("%s=%d\n", #x, x);
...
int x=15;
ispisi(x);

Operator spajanja ## spaja dva tokena u jedan:


#define broj(x) printf("broj %d = %d", x, broj##x);
...
int broj15=1;
broj(15);

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

Direktiva #undef

Direktiva #undef nam omoguuje da promijenimo


vrijednost makroa:
#define KOLICINA 100
...
#undef KOLICINA
#define KOLICINA 200

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

Direktiva #ifdef

Pomou #ifdef moe se provjeriti da li je neka konstanta


definisana. Ako nije, kod izmeu #ifdef i #endif nee se
uopte kompajlirati! Npr.
#define DEBUG
...
#ifdef DEBUG
printf("Niz glasi:\n");
for (i=0; i<100; i++)
printf("%d,", niz[i]);
printf("\n");
#endif

Obratite panju da makro ne mora imati nikakvu vrijednost.

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

Direktive #elif i #else

Za veinu operativnih sistema definisani su makroi od


strane kompajlera:
#ifdef _WIN32
char putanja[] = "C:\\Program
Files\\MojProg\\podaci";
#elif __linux__
char putanja[] = "/usr/local/MojProg/podaci
#else
printf("Greska! Nepoznat OS\n");
exit(1);
#endif

#elif ima funkciju kao else if

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

Direktive #ifndef i #error

#ifndef blok se izvrava ako makro nije definisan, npr:


#ifndef KAPACITET
#define KAPACITET 100
#endif

#error ispisuje greku koristei standardni izlaz za greke


(stderr)
#error "Pogresan parametar"

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

10

Neki predefinisani makroi

__DATE__ - trenutni datum

__TIME__ - trenutno vrijeme

__FILE__ - naziv datoteke

__LINE__ - linija koda

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

11

Direktiva #include

Direktiva #include slui za ukljuivanje biblioteka sa


dodatnim funkcijama.
Parametar je naziv header datoteke (zaglavlja).

Ako je naveden u znakovima <> header datoteka se


trai u sistemskim direktorijima.

Ako je pod navodnicima, moe se koristiti apsolutni put


ili relativni u odnosu na lokaciju trenutnog fajla.

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

12

Header datoteke

Kako napraviti vlastitu biblioteku?


U header datoteci (ekstenzija .h) nalaze se prototipovi
funkcija, a implementacija se nalazi u .c datotekama.

Pored toga, u headeru se mogu nalaze konstante i


promjenljive koje se mogu koristiti iz vie .c datoteka.

Svaka .c datoteka predstavlja jedan nezavisan modul koji


se moe povezati sa programom.
Probaemo napraviti header datoteku sa funkcijom
faktorijel.

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

13

main.c
#include <stdio.h>
#include "faktorijel.h"
int main() {
double x, S=0;
int n, i;
printf("Unesite x: ");
scanf("%lf", &x);
printf("Unesite n: ");
scanf("%d", &n);
for (i=1; i<=n; i++)
S += 1.0/faktorijel(i);
printf("S=%g", S);
return 0;
}

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

14

main.c
#include <stdio.h>
#include "faktorijel.h"
int main() {
double x, S=0;
Datoteka faktorijel.h se nalazi
int n, i;
u istom folderu kao main.c
printf("Unesite x: ");
scanf("%lf", &x);
printf("Unesite n: ");
scanf("%d", &n);
for (i=1; i<=n; i++)
S += 1.0/faktorijel(i);
printf("S=%g", S);
return 0;
}

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

15

faktorijel.h
#ifndef FAKTORIJEL_H
#define FAKTORIJEL_H
long faktorijel(int x);
#endif

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

16

Include guard

Moramo paziti da vie puta ne uradimo include istog headera,


jer se funkcije i promjenljive ne mogu deklarisati vie puta.
To je esto vrlo teko napraviti npr. ako imamo fajlove A, B i C
koji ukljuuju D, i imamo E koji ukljuuje A, B i C, samim time
e D biti ukljuen triput.
Ovo se rjeava koritenjem preprocesorske direktive koja
osigurava da se samo jednom deklariu elementi.
Definisaemo konstantu FAKTORIJEL_H (praksa je da se
konstante definiu velikim slovima, znak taka se ne smije
nalaziti u imenu konstante). Ako je ona ve definisana,
preskaemo kompletan .h fajl (#endif je na samom kraju).

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

17

faktorijel.c
long faktorijel(int x) {
int i, rez=1;
for (i=2; i<=x; i++)
rez *= i;
return rez;
}

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

18

Dodatne datoteke

Pravljenje projekta sastavljenog od vie datoteka u:

Code::Blocks

C9

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

19

Komandna linija

koljka (eng. shell) ili ljuska je program koji omoguuje rad


sa datotenim sistemom i pokretanje programa koristei
tekstualne naredbe.
Na Windowsu se pokree naredbom cmd
U gornjem dijelu prozora je pozdravna poruka (verzija
Windows OS).
Zatim slijedi tekui direktorij
(na slici C:\Users\Vedran),
te oznaka > da je program
spreman da primi upit.

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

20

Komandna linija

Slino izgleda i koljka na


Linuxu (bash).
Terminal emulator (ili krae
samo terminal) je poznati
crni prozor sa bijelim slovima
koji slui za pokretanje
tekstualnih programa kao to
je koljka.
Npr. Code::Blocks pokree programe u terminal emulatoru.

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

21

Osnovne naredbe

Osnove koritenja komandne linije u Windows operativnom


sistemu:

dir spisak datoteka u trenutnom direktoriju

cd promjena direktorija

Koritenje tipke TAB


del brisanje datoteke

copy kopiranje datoteke

rename promjena imena datoteke

mkdir pravljenje direktorija

rmdir brisanje direktorija

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

22

Osnovne naredbe

Osnove koritenja komandne linije u Linux operativnom sistemu:

ls spisak datoteka u trenutnom direktoriju

cd promjena direktorija

Koritenje tipke TAB


rm brisanje datoteke

cp kopiranje datoteke

mv promjena imena datoteke

mkdir pravljenje direktorija

rmdir brisanje direktorija

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

23

Pokretanje programa

Da biste pokrenuli program dovoljno je da otkucate


njegovo ime, ako se program nalazi u tekuem direktoriju
ili u sistemskoj putanji. Npr. program Notepad se nalazi u
sistemskoj putanji pa moemo kucati:
notepad

U suprotnom, moramo kucati kompletnu putanju do


izvrne datoteke:
"C:\Program Files\CodeBlocks\codeblocks.exe"

Sistemsku putanju moemo ispisati naredbom


echo $PATH

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

24

Princip rada kompajlera (P03)


1) preprocesiranje
2) prevoenje
3) asembliranje
4) linkovanje

Koraci 1-3) se izvravaju za svaku datoteku zasebno.


U koraku 1) #include direktiva e kompletan sadraj .h
datoteke ubaciti na mjesto u kodu gdje je ona ukljuena.
U koraku 4) povezuju se pojedinane datoteke te sistemske
biblioteke kako bi se dobila izvrna datoteka (exe).

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

25

gcc

gcc (Gnu Compiler Collection) je standardni kompajler na


Linuxu i nalazi se u sistemskoj putanji.
Code::Blocks koristi verziju gcc-a za Windows poznatu
pod imenom MinGW. Zbog toga treba paziti da se prilikom
instalacije izabere verzija "CodeBlocks with MinGW".

Na Windowsu pokreemo gcc naredbom:


"C:\Program Files\CodeBlocks\mingw\bin\gcc.exe"

gcc objedinjuje sva 4 koraka u jednoj naredbi, da bismo


kompajlirali na program kucamo:
gcc main.c faktorijel.c

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

26

Gdje se nalaze datoteke?

Recimo na Windowsu da smo kreirali projekat "dekomp" na


Dekstopu. Trebamo prvo ui u odgovarajui direktorij:
cd C:\Users\Vedran\Desktop\dekomp
dir

Ako otkucamo dir vidjeemo datoteke main.c, faktorijel.h i


faktorijel.c. Potrebno je da gcc-u navedemo sve .c datoteke
Bie kreirana izvrna datoteka koja se zove a.out (na
Windowsu a.exe). Ako elimo neko drugo ime, moemo
koristiti parametar -o
gcc main.c faktorijel.c -o MojProgram

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

27

Preprocesiranje

Da bismo "natjerali" gcc da izvrava kod fazu po fazu,


koristimo posebne parametre:
-E
preprocesiranje
-S
kompajliranje
-c
asembliranje
Bez parametara gcc e uraditi fazu linkovanja.
Prethodno emo modificirati main.c tako da lake moemo
uoiti da je obavljeno preprocesiranje, npr. dodaemo par
makroa.

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

28

Modifikacija main.c
#include <stdio.h>
#include "faktorijel.h"
#define RECIPRO(x) 1.0/x
#define KRAJ return 0
int main() {
double x, S=0;
int n, i;
printf("Unesite x: ");
scanf("%lf", &x);
printf("Unesite n: ");
scanf("%d", &n);
for (i=1; i<=n; i++)
S += RECIPRO(faktorijel(i));
printf("S=%g", S);
KRAJ;
}

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

29

Preprocesiranje

Kucamo:
gcc -E main.c -o main_pre.c

Dobiemo preprocesiran kod u datoteci main_pre.c koja


izgleda otprilike kao na sljedeem slajdu.
Za svaku ukljuenu biblioteku imamo po jedan komentar.
Najprije je ukljuena stdio.h, ali ona ukljuuje mnoge druge
biblioteke pa e biti dosta komentara na poetku.
Generisana datoteka se moe uporediti sa datotekom
stdio.h.

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

30

main_pre.c
#
#
#
#
#
#
#

1 "main.c"
1 "<command-line>"
1 "main.c"
1 "c:\\program files\\.../stdio.h" 1 3
19 "c:\\program files\\.../stdio.h" 3
1 "c:\\program files\\.../_mingw.h" 1 3
32 "c:\\program files\\.../_mingw.h" 3

# 33 "c:\\program files\\.../_mingw.h" 3
# 20 "c:\\program files\\.../stdio.h" 2 3

Za svaku ukljuenu biblioteku


dobijamo po jedan komentar

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

31

main_pre.c
...
# 129 "c:\\program files\\...include/stdio.h" 3
typedef struct _iobuf
{
char* _ptr;
int _cnt;
char* _base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
} FILE;
...
Definicija strukture FILE

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

32

main_pre.c
FILE* __attribute__((__cdecl__)) __attribute__ ((__nothrow__))
fopen (const char*, const char*);
FILE* __attribute__((__cdecl__)) __attribute__ ((__nothrow__))
freopen (const char*, const char*, FILE*);
int __attribute__((__cdecl__)) __attribute__ ((__nothrow__))
fflush (FILE*);
int __attribute__((__cdecl__)) __attribute__ ((__nothrow__))
fclose (FILE*);
int __attribute__((__cdecl__)) __attribute__ ((__nothrow__))
remove (const char*);
int __attribute__((__cdecl__)) __attribute__ ((__nothrow__))
rename (const char*, const char*);

Prototipovi nekih
poznatih funkcija

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

33

main_pre.c
# 2 "main.c" 2
# 1 "faktorijel.h" 1
long faktorijel(int x);
# 3 "main.c" 2
int main() {
double x, S=0;
int n, i;
printf("Unesite x: ");
scanf("%lf", &x);
printf("Unesite n: ");
scanf("%d", &n);
for (i=1; i<=n; i++)
S += 1.0/faktorijel(i);
printf("S=%g", S);
return 0;
}
7.1.2016

Sadraj datoteke faktorijel.h

Uraena je supstitucija
makro-a u main datoteci

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

34

Prevoenje

Izlaz iz faze prevoenja je asemblerski kod (eng.


assembly) tj. kod u programskom jeziku koji se zove
asembler

(zbunjujue, jer se sam program koji prevodi asemblerski kod u mainski kod
takoe zove asembler).

Asembler je jezik vrlo blizak mainskom kodu, prevoenje


iz asemblera u mainski jezik je skoro pa trivijalno.
Generisaemo asemblerski kod naredbom:
gcc -S main.c -o main.asm

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

35

main.asm
.file
"main.c"
.def ___main; .scl 2;
.type
32; .endef
.section .rdata,"dr"
LC1:
.ascii "Unesite x: \0"
LC2:
.ascii "%lf\0"
LC3:
.ascii "Unesite n: \0"
LC4:
.ascii "%d\0"
LC6:
.ascii "S=%g\0"
.text
.globl _main
.def _main; .scl 2;
.type
32; .endef
_main:
pushl
%ebp
movl %esp, %ebp
andl $-16, %esp
subl $64, %esp
call ___main
7.1.2016

movl $0, %eax


movl $0, %edx
movl %eax, 56(%esp)
movl %edx, 60(%esp)
movl $LC1, (%esp)
call _printf
leal 40(%esp), %eax
movl %eax, 4(%esp)
movl $LC2, (%esp)
call _scanf
movl $LC3, (%esp)
call _printf
leal 36(%esp), %eax
movl %eax, 4(%esp)
movl $LC4, (%esp)
call _scanf
movl $1, 52(%esp)
jmp L2
L3:
movl 52(%esp), %eax
movl %eax, (%esp)
call _faktorijel
movl %eax, 28(%esp)

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

36

Asembliranje

U fazi asembliranja asemblerski kod se prevodi u mainski


kod koji je spreman za izvrenje. Meutim nisu ukljuene
implementacije raznih bitnih funkcija niti kod potreban za
loader kako bi pokrenuo program na datom OS-u.
Ovakve datoteke se nazivaju objektne datoteke i imaju
ekstenziju .o
Moemo kucati:
gcc -c main.c
gcc -c faktorijel.c

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

37

Linkovanje

Ostaje jo da objektne datoteke poveemo u finalnu izvrnu


(.exe) datoteku. Pored objektnih datoteka, na program e biti
povezan i sa svim potrebnim sistemskim datotekama
gcc main.o faktorijel.o -o MojProgram
MojProgram

Datoteku faktorijel.o moete povezati sa bilo kojim programom


u kojem se koristi funkcija faktorijel.

Ali u programu mora biti i definisan prototip te funkcije, to


znai da morate u kodu ukljuiti faktorijel.h.

Na taj nain ste dali drugima svoju biblioteku ali im niste


dali izvorni kod biblioteke.

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

38

Disasembliranje

Moemo li analizirati izvrnu datoteku bez posjedovanja


izvornog koda?

Postoji programi koji se nazivaju dekompajleri.

U programskim jezicima kao to su .NET jezici (npr. C#)


ili Java u pravilu je mogue dekompajlirati program,
osim ako se koristi obfuskacija.

Za C i C++ je to ve dosta tee.

Mogue je ipak disasembliranje (prevoenje mainskog


koda nazad u asemblerski kod pri emu se gube neke
olakice kao to su fini nazivi labela i sl.)

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

39

Debugger

Pod pojmom debugger podrazumijeva se program koji


obavlja disasembliranje izvrne (.exe) datoteke i
omoguuje da se kroz kod prolazi korak po korak, pri
emu se prate vrijednosti registara, stacka i heap-a.

7.1.2016

Vedran Ljubovi * OR15 * P20: Preprocesorske direktive

40

You might also like