Ciele cvičeniaCvičenie je zamerané na prácu s kompilačným nájstrojom make. Tento nástroj slúži na kompiláciu zložitejších projetov, pozostávajúcich z niekoľkých súborov zdrojových kódov, vlastných hlavičkových súborov a podobne. Také projekty sa skladajú z takzvaných modulov, ktorých kombináciou a kompiláciou vzniká finálny softvérový produkt. Po osvojení si problematiky študent dokáže pracovať s nástrojom make, dokáže generovať konfiguračné súbory Makefile a rozumie filozofii modulárneho programovania.
|
Odporúčaná literatúra a dôležité odkazy[1] KPI-FEI-TUKE: Prednáška č. 1 [2] KPI-FEI-TUKE: Prednáška č. 2 [3] KPI-FEI-TUKE: Cvičenie č. 1 - Introduction to Modular Programming [4] Umberto Salsi: Modular programming in C [5] Kovač, O.: ZAP - Cvičenie č. 2 [6] wikipedia.org: Disperzia |
Modulárne programovanie je technika softvérového dizajnu, pri ktorej sa na výsledný program nepozeráme ako na monolit ale ako na celok zložený zo separátnych častí - modulov. Modulárne programovanie oproti klasickému - "monolitickému" vnáša do vývoja softvéru ako aj do finálneho produktu prehľadnosť. Táto prehľadnosť je zavedená tým, že za rôzne funkcionality programu sú zodpovedné rôzne moduly. Výhodou modulov je tiež to, že je možné pracovať na vylepšeniach alebo opravách (bugfix) programu bez toho, aby bolo potrebné kompilovať celý projekt. Jednotlivé časti programu môžu byť v jazyku C predkompilované do takzvaných objektových súborov a tieto sa následne spoja - "zlinkujú" do výsledného programu. Takýto prístup pri ozaj rozsiahlych projektoch výrazne šetrí čas. V prípade veľkých projektov je modulárne programovanie doslova nutnosťou. Bližšie si kompiláciu zdrojového kódu (.c) do objektového súboru (.o) popíšeme v ďalšej časti textu.
Vytvorte program pre zobrazenie zadanej matice a výpočet nasledovných parametrov matice: stredná hodnota, disperzia [6], maximum, minimum. Využite pritom zásady modulárneho programovania!
Riešenie:
Jedno z možných riešení je nasledovné:
Ako už bolo uvedené vyššie, v jazyku C sa pri modulárnom programovaní zdrojové súbory najprv kompilujú do objektových súborov (.o) a až následne sa spoja do výsledného spustiteľného programu. Na nasledujúcom príklade ukážeme tento postup s využitím nástroja gcc, ktorý už veľmi dobre poznáme [5]. Len pre pripomenutie tento nástroj/príkaz používame nasledovne:
// použitie gcc všeobecne $ gcc -prepinac1 -prepinac2 ... -prepinacN zdrojovy_kod_1.c zdrojovy_kod_2.c obejkotvy_subor_1.o -kniznica1 -kniznica2 -o nazov_programu // praktický príklad $ gcc -Werror -Wall program.c -lkarel -lcurses -o PROGRAM // -Werror znamená, že každé upozornenie (warning) sa vyhodnotí ako chyba (error) // -Wall zobraziť všetky upozornenia |
Nástrojom gcc objektový súbor vytvoríme tak, že za zdrojový kód, ktorý kompilujeme dáme prepínač -c :
// použitie gcc všeobecne $ gcc -prepinac1 -prepinac2 ... -prepinacN zdrojovy_kod.c -c // praktický príklad $ gcc -Werror -Wall funkcie.c -c |
V pracovnom priečinku by mal vzniknúť súbor s názvom funkcie.o. Teraz je možné pristúpiť ku kompilácii a zlinkovaniu celého programu:
$ gcc -Werror -Wall hlavny.c funkcie.o -o PROGRAM |
*poznámka: aj súbor hlavny.c by mohol byť vopred skompilovaný na objektový súbor.
Asi nie je potrebné uvádzať, že je pomerne nepraktické kompilovať kód takto z príkazového riadka. Na ZAP ste už používali nástroj make ale pritom ste do hĺbky nevedeli čo presne tento nástroj robí a ako funguje. V nasledujúcej časti sa teda bližšie pozrieme na tento nástroj.
Pokiaľ nenastala žiadna chyba aktuálny priečinok by mohol vyzerať nasledovne:
ab123cd@zapfei:MODULAR$ ls -l total 36 -rw-rw-r-- 1 ab123cd ab123cd 547 Mar 3 10:15 funkcie.c -rw-rw-r-- 1 ab123cd ab123cd 93 Mar 3 09:46 funkcie.h -rw-rw-r-- 1 ab123cd ab123cd 2128 Mar 3 10:15 funkcie.o -rw-rw-r-- 1 ab123cd ab123cd 312 Mar 3 09:46 hlavny.c -rwxrwxr-x 1 ab123cd ab123cd 16896 Mar 3 10:21 PROGRAM |
Výpis z terminálu po spustení programu, bude vyzerať nasledovne:
ab123cd@zapfei:$ ./PROGRAM 1 2 3 4 5 6 7 8 9 Stredna hodnotoa matice = 5.000000 |
#include <stdio.h> #include "funkcie.h" int main() { int matica[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; // Volanie funkcie pre vykreslenie matice vykreslenie(matica, 3); printf("\nStredna hodnotoa matice = %f \n \n", priemer(matica,3)); // tu budú ostatné výpočty a zobrazenie ich výsledkov //printf("\nMaximalna hodnota matice = %d \n \n", max(matica,3)); //printf("\nMinimalna hodnota matice = %d \n \n", min(matica,3)); return 0; } |
void vykreslenie(int matica[][3], int velkost); float priemer(int matica[][3], int velkost); int max(int matica[][3], int velkost); int min(int matica[][3], int velkost); float disp(int matica[][3], int velkost); |
#include <stdio.h> void vykreslenie(int matica[][3], int velkost) { printf("\n"); for(int i = 0; i < velkost; i++) { printf("\n"); for(int ii = 0; ii<velkost; ii++) printf(" %d ", matica[i][ii]); } printf("\n"); } // Výpočet strednej (priemernej) hodnoty float priemer(int matica[][3], int velkost) { float suma = 0; for(int i = 0; i < velkost; i++) for(int ii = 0; ii<velkost; ii++) suma = suma + matica[i][ii]; return suma/(velkost*velkost); } // tu budú definície ďalších funkcii - Disperzia, Max, Min, //int max(int matica[][3], int velkost) //{ // //} //int min(int matica[][3], int velkost) //{ // //} |
Účelom nástroja make je automaticky určiť, ktoré časti rozsiahleho programu musia byť skompilované alebo skompilované opakovane a skompilovať ich. Nástroj pracuje s konfiguračným súborom Makefile, ktorý obsahuje všetky jednotlivé príkazy pre kompiláciu objektových resp. spustiteľných súborov, rôzne prepínače či príkazy pre vymazanie nepotrebných súborov. Principiálne súbor Makefile obsahuje tri typy parametrov. Ide o takzvané cieľe - targets, nutné súčasti - dependecies a konštanty. Tiež môžeme definovať rôzne, prepínače, cesty k súborom, konštanty, knižnice, preferovaný prekladač a podobne. Súbor Makefile má nasledovnú štruktúru, kde [TAB] znamená odsadenie tabulátorom. Pozor na medzery a podobne, musí tam byť TAB inak skončíte pri chybe!
Komentáre sa v tomto súbore zadávajú za mriežku #. Ak pred príkaz dáme znak @, potlačí sa výpis príkazu do terminálu. V princípe v Makefile môžete používať ľubovoľný príkaz linux-u. Napr. v poslednej časti súboru Makefile z príkladu je zabezpečené "upratanie" nepotrebných súborov po kompilácii.
# Tu môže byť ozaj kadečo, kto má záujem veľa nájde na google INCLUDES = -I/home/newhall/include LIBS = -lmylib -lm CC = gcc TARGET: DEPENDENCIES [TAB] SYSTEM_COMMAND1 ... [TAB] SYSTEM_COMMANDn |
Pre príklad, ktorý riešime môže súbor Makefile vyzerať nasledovne:
# Tu su definovane prepinace, kompilator a vystup (toto sú konštanty) CC= gcc CFLAGS= -Werror -Wall OUTPUT= PROGRAM # Skompiluje sa cely program all: funkcie.o hlavny.o gcc $(CFLAGS) hlavny.o funkcie.o -o $(OUTPUT) @echo "Skompilovaný celý program" # Pokiaľ súbor funkcie.o nie je aktuálny tak sa skompiluje funkcie.c funkcie.o: funkcie.c @gcc -Werror -Wall funkcie.c -c @echo "Skompilovane iba funkcie.o" # Pokiaľ súbor hlavny.o nie je aktuálny tak sa skompiluje hlavny.c hlavny.o: hlavny.c @$(CC) $(CFLAGS) hlavny.c -c @echo "Skompilovane iba hlavny.o" # takto sa odstránia všetky súbory s príponou .o a tiež spustiteľný súbor clean: rm *.o $(OUTPUT) |
Úloha 2: