Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Info

Ciele cvičenia

Cvič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. 

Info

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

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.





Úloha 1:

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é:

  • Program bude pozostávať z dvoch zdrojových súborov - hlavny.c a funkcie.c
  • Program bude obsahovať jeden vlastný hlavičkový súbor - funkcie.h 
  • Modul funkcie.c bude prv skompilovaný na objektový súbor - funkcie.o
  • Použite kostru programu ktorá je zobrazená nižšie! V poskytnutom kóde je už vyriešené zobrazenie matice a tiež výpočet strednej (priemernej) hodnoty. 

Kompilácia zdrojového kódu do objektu 

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:

Code Block
// 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č -:

Code Block
// 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: 

Code Block
$ 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:

Code Block
languagebash
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:

Code Block
languagebash
ab123cd@zapfei:$ ./PROGRAM

 1  2  3
 4  5  6
 7  8  9

Stredna hodnotoa matice = 5.000000

Kostra úlohy:

Code Block
languagecpp
titlehlavny.c
#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;
}
Code Block
languagecpp
titlefunkcie.h
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);  
Code Block
languagecpp
titlefunkcie.c
#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)
//{
//
//}

Nástroj make a konfiguračný súbor Makefile

Úč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. 

Code Block
languagebash
# 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:

Code Block
languagebash
# 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)
Note
titlePOZOR!

Ak dostanete hlášku:  Makefile:xzy: *** missing separator. Stoptak zrejme nemáte TAB na začiatku príkazu. Editor Moba namiesto TAB dáva medzery.  Odporúčam v tomto prípade použiť nástroj mcedit. Tam sa TAB dáva tak, že 2x stlačíte klávesu TAB.

Súbor Makefile otvoríte/vytvoríte zadaním príkazu:

ab123cd@zapfei:$ mcedit Makefile

Nástroj make použijeme nasledovne:

Code Block
languagebash
ab123cd@zapfei:MODULAR$ make clean
 rm *.o PROGRAM
 ab123cd@zapfei:MODULAR$ make all
 Skompilovane iba funkcie.o
 Skompilovane iba hlavny.o
 gcc -Werror -Wall hlavny.o funkcie.o -o PROGRAM
 Skompilovaný celý program



Úloha 1.1:

  • Doplňte svoje riešenie o ďalšie súbory (nacitaj.c + nacitaj.h), pre načítanie matice od používateľa
  • Aktualizujte hlavný program o túto funkcionalitu a tiež aktualizujte Makefile o tento modul 

Úloha 2: