C fordítás – Makefile használata

Elmélet | Példa | Feltöltendő | Feladatok | Megjegyzések


Elméleti összefoglaló:

A forráskódtól a végrehajtható programig

Előfeldolgozás (preprocess)
Az előfeldolgozás során a forráskódú programból egy ugyancsak forráskódú programot kapunk, amelyben a makrodefiníciók (#define) ki vannak terjesztve, és tartalmazza az „include” file-okat (#include).

Fordítás (compile)
A fordítás révén a forráskódból (pl. C, C++) tárgykódot (object) kapunk. C, illetve C++ programok esetén a bemenet egy C vagy C++ forráskódot tartalmazó állomány, kimenetként egy tárgykódú állományt kapunk. A fordítóprogram elvégzi az előfeldolgozást is. A UNIX-beli C fordító általában a bemeneti állománnyal azonos nevű, de „.o” kiterjesztésű tárgykódú állományt hoz létre.

Összeszerkesztés (link)
Ennek a munkafázisnak a szerepe, hogy esetleg több tárgykódú állományból, és ha szükséges a program által használt könyvtárakból egy végrehajtható állományt hozzon létre. Ez azért szükséges, mert egy program több modulból is állhat, amelyeket külön fordítunk le. Ugyanakkor külső könyvtárakat is használhatunk.

A gcc/g++ fordítóprogram
A gcc fordítóprogram jelenleg az egyik legerősebb C fordító. C++ forráskód fordításához kiterjesztett változata a g++, amit ugyancsak meghívhatunk gcc néven is. A forráskódú állomány kiterjesztése alapján dönti el a fordító, hogy milyen kódról van szó. „.c” kiterjesztés C, míg „.C”, „.cc”, „.cpp”, „.c++”, stb. kiterjesztés C++ forráskódot jelent. Ez a fordítóprogram elvégzi az előfeldolgozást, a fordítást és az összeszerkesztést is. Ha nem akarunk minden fázison végigmenni, opciókkal, illetve az állománynév kiterjesztésével jelezhetjük ezt a fordítóprogramnak. Ha nem adtuk meg a parancssorban másként, a létrehozott végrehajtható file az a.out. Részletek: man gcc.

Példák:

Egyetlen modul:
    gcc pelda.C -o pelda

Több modul:
    gcc pelda.C -c
    gcc seged.cc -c
    gcc pelda.o seged.o -o pelda

Külső könyvtár is szükséges:
    gcc pelda.C -c
    gcc seged.cc -c
    gcc pelda.o seged.o -I/usr/lib/libncurses.a -o pelda

A make segédprogram – makefile

Nagyobb, összetett programok esetén a program több, illetve sok modulból épül fel. Ezek a modulok egymástól függnek, egy modul változása szükségessé teheti több más modul újrafordítását is, illetve az egész program összeszerkesztését. A modulok között „függőségek” vannak. A make segédprogram segítségével egyszerűen lefordíthatunk egy nagy projektet úgy, hogy csak azokat a modulokat fordítja újra, amelyeket a függőségek alapján szükséges, nem kell a programozó figyeljen minden változásra. A make segédprogram a makefile-ban található szabályok és az állományok „időbélyege” alapján dönti el, hogy milyen parancsokat kell végrehajtson.

Egy makefile szabályokat, változódefiníciókat, direktívákat és kommentárokat tartalmazhat.

Szabályok
Egy szabály azt adja meg, hogy a szabály célállománya melyik állományoktól függ, illetve milyen paranccsal hozzuk létre. Ha a függést okozó file-ok közül bármelyik időbélyege újabb, mint a célállományé, a parancssor végrehajtásra kerül. Egy szabály szerkezete:

célállomány: függést okozó állományok
    parancssor

célállomány: függést okozó állományok; parancssor

A parancssort általában újsorba írjuk, ekkor kötelezően egy <Tab> karakterrel kezdődik. Tartalmazhat több parancsot is. Ezeket vagy „;”-vel elválasztva, vagy <Tab> karakterrel kezdődő új sorba írjuk.

Változódefiníciók
A változódefiníció egy olyan sor, amely egy változó értékeként egy karaktersort ad meg. A továbbiakban a változó nevét ezzel a karaktersorral helyettesíti a program.

Direktívák
Egy direktíva azt jelzi a make segédprogramnak, hogy a makefile beolvasása közben valamilyen speciális feladatot végezzen el. Ez lehet például egy másik makefile beolvasása, valamilyen feltétel, stb.

Kommentárok
A program a „#” utáni részt kommentárnak tekinti, nem értelmezi. Hasonlóan az üres sor is kommentár.

A make segédprogramot meghívhatjuk egyszerűen paraméterek nélkül, ebben az esetben „makefile”, „Makefile”, „GNUmakefile” kell legyen a makefile neve, vagy -f opcióval megadhatjuk a makefile nevét.

Részeletek: info make

Jó összefoglaló: http://www.cs.umd.edu/class/fall2002/cmsc214/Tutorial/makefile.html

Példa – egy makefile

# Változó deklarációk  

CC = g++   

CCFLAGS = -Wall 

SRC = seged.cc \ 
      pelda.cc 

OBJ = seged.o \ 
      pelda.o 

PROGRAM = pelda 

# fordítási szabályok 

$(PROGRAM) : $(OBJ) 
    $(CC) $(OBJ) -o $(PROGRAM)   

seged.o : seged.cc adat.h 
    $(CC) seged.cc -c $(CCFLAGS) 

pelda.o : pelda.cc adat.h 
    $(CC) pelda.cc -c $(CCFLAGS) 

# Törlési parancsok  

.PHONY : clean 
clean : 
    rm -f $(PROGRAM) $(OBJ) core *~

Feltöltendő:

  1. makefile
  2. C program file-ok, header file
  3. a fenti állományok becsomagolva (.gz)

Az állományokat a

tar -czf zipnev.gz allomanynevek

paranccsal csomagoljuk.

Kicsomagolni a

tar -xzf zipnev.gz
vagy
tar -xzf zipnev.gz -C katalogus/

paranccsal lehet.

Részeletek: man tar.

Feladatok:

1. Írjunk egy „gyok(k,a)” függvényt, amelyik kiszámítja a k-ad rendű gyökét e=0.00000001 pontossággal (k>2 természetes szám, a pozitív valós), mint az xn+1 = 1/k ((k-1) xn +a/xnk-1) sorozat határértékét tudva, hogy x0 = a/2. Ezt a függvényt használjuk az S = gyok(k,x1) + ... + gyok(k,xn) összeg kiszámítására, ahol az x1, x2, ..., xn pozitív valós értékek, melyeket az „input.dat” file-ból olvasunk be. 

2. Írjunk egy „lnko(k,m)” függvényt, amelyik kiszámítja k és m legnagyobb közös osztóját. Ezt a függvényt használjuk az x1, x2, ..., xn, n>1, természetes számok legnagyobb közös osztójának kiszámítására. A számokat az „input.dat” file-ból olvasunk be. 

3. Írjunk egy „lkkt(k,m)” függvényt, amelyik kiszámítja k és m legkisebb közös többszörösét. Ezt a függvényt használjuk az x1, x2, ..., xn, n>1, természetes számok legkisebb közös többszörösének kiszámítására. A számokat az „input.dat” file-ból olvasunk be.

4. Írjunk egy „szhal(a,b,c)” függvényt, amelyik ellenőrzi, hogy az a, b és c számok számtani haladványt alkotnak-e. Ezt a függvényt használjuk arra, hogy eldöntsük, hogy az x1, x2, ..., xn, n>1, számok számtani haladványt alkotnak-e. A számokat az „input.dat” file-ból olvasunk be.

5. Írjunk egy „mhal(a,b,c)” függvényt, amelyik ellenőrzi, hogy az a, b és c számok mértani haladványt alkotnak-e. Ezt a függvényt használjuk arra, hogy eldöntsük, hogy az x1, x2, ..., xn, n>1, számok mértani haladványt alkotnak-e. A számokat az „input.dat” file-ból olvasunk be.

6. Írjunk egy „teszt(k)” függvényt, amelyik ellenőrzi, hogy az k természetes szám megegyezik-e azzal a számmal, amit a számjegyek fordított sorrendben történő felírásával kapunk. Ezt a függvényt használjuk arra, hogy megszámoljuk, az x1, x2, ..., xn, n>1, természetes számok közül hány rendelkezik ezzel a tulajdonsággal. A számokat az „input.dat” file-ból olvasunk be.

7. Írjunk egy „tokeletes(k)” függvényt, amelyik ellenőrzi, hogy a k természetes szám tökéletes szám-e, azaz osztóinak összege (önmagát nem beleszámítva) megegyezik-e a számmal, pl. 6=1+2+3. Ezt a függvényt használjuk arra, hogy megszámoljuk,  az x1, x2, ..., xn, n>1, természetes számok közül hány tökéletes szám. A számokat az „input.dat” file-ból olvasunk be.

8. Írjunk egy „enorma(x,n)” függvényt, amelyik kiszámítja az x = (xi), i = 1, ..., n vektor elemeinek euklideszi normáját (a vektor elemei négyzetösszegének a négyzetgyöke). Ezt a függvényt használjuk k vektor normájának kiszámítására. A vektorokat az „input.dat” file-ból olvassuk be, az eredményt pedig, a bemenő adatokkal együtt az „output.dat” file-ba írjuk.

9. Írjunk egy „tavolsag(x,y,n)” függvényt, amelyik kiszámítja az x = (xi) és y = (yi), i = 1, ..., n vektorok euklideszi távolságát (a vektorok azonos rangú elemei különbsége négyzetösszegének a négyzetgyöke). Ezt a függvényt használjuk 2 vektor távolságának kiszámítására. A vektorokat az „input.dat” file-ból olvassuk be, az eredményt pedig, a bemenő adatokkal együtt az „output.dat” file-ba írjuk.

10. Írjunk egy „csere(x,n,m)” függvényt ( m < n természetes számok), amelyik az x = (xi), i = 1, ..., n vektor utolsó n - m elemét a vektor elejére, az első m elemet pedig a vektor végére viszi. Ezt a függvényt használjuk k vektor feldolgozására. A vektorokat az „input.dat” file-ból olvassuk be, az eredményt pedig, a bemenő adatokkal együtt az „output.dat” file-ba írjuk.

11. Írjunk egy „min(n,x,k)” függvényt ( k <= n természetes számok), amelyik megkeresi az x = (xi), i = 1, ..., n számsorozat első k elemének minimumát. Ezt a függvényt használjuk az y = (yi), yi = min(n,x,i), i = 1, ..., n számsorozat meghatározására. A számsorozatot az „input.dat” file-ból olvassuk be, az eredményt pedig az „output.dat” file-ba írjuk.

12. Írjunk egy „osszeg(n,x,k)” függvényt ( k <= n természetes számok), amelyik kiszámítja az x = (xi), i = 1, ..., n számsorozat első k eleme négyzetének összegét. Ezt a függvényt használjuk az y = (yi), yi = osszeg(n,x,i), i = 1, ..., n számsorozat meghatározására. A számsorozatot az „input.dat” file-ból olvassuk be, az eredményt pedig az „output.dat” file-ba írjuk.

13. Írjunk egy „egyesit(A,B,C,n,m,k)” függvényt, amelyik meghatározza a  C=A U B halmaz elemeit, ahol A = {a1, a2, ..., an}és B = {b1, b2, ..., bm} valós számhalmazok.. Ezt a függvényt használjuk az R = A1 U A2 U ... U Ap halmaz elemeinek meghatározására. A halmazokat az „input.dat” file-ból olvassuk be, az eredményt pedig a bemenő adatokkal együtt  az „output.dat” file-ba írjuk.

14. Írjunk egy „csere(A,n,m,i,k)” függvényt, amelyik felcseréli az A = (aij , i = 1, ..., n, j = 1, ..., m) mátrix i-edik és k-adik sorát. Ezt a függvényt használjuk egy B mátrix sorainak a felcserélésére úgy, hogy az első oszlop elemei növekvő sorrendben legyenek. A mátrixot az „input.dat” file-ból olvassuk be, az eredményt pedig, a bemenő adatokkal együtt az „output.dat” file-ba írjuk.

15. Írjunk egy „segit(A,n,m,i,k)” függvényt, amelyik az A = (aij , i = 1, ..., n, j = 1, ..., m) mátrix i-edik sorában és k-adik oszlopában levő elem helyére a 0-tól különböző szomszédainak számtani középarányosát írja, ha ez az elem 0 . Ezt a függvényt használjuk egy B mátrix minden 0 értékű elemének módosítására. A B mátrix elemei valósak és legyen minden 0 elemének legalább két 0-tól különböző eleme. A mátrixot az „input.dat” file-ból olvassuk be, az eredményt pedig, a bemenő adatokkal együtt az „output.dat” file-ba írjuk.


Megjegyzések:

1. A programokat C-ben írjuk, és legalább 2 C modulból kell álljanak.
2. A deklarációkat egy .h header file-ban kell megadni.
3. Minden feladathoz meg kell írni a makefile-t is.
4. A végrehajtandó állomány neve kötelezően „p”.
5. Az „input.dat” és „output.dat” szöveges file-ok.
6. Teszteléshez a következő script-et használom:

rm -IR *
cp ../input$1.dat input.dat
tar -xzf $2
make
./p
more output.dat

ahol $1 a feladat száma, $2 a .gz filenév. Amennyiben a linux-on futtatva hibajelzést kapok a feladatot „???”-lel utasítom vissza.