Differences

This shows you the differences between two versions of the page.

Link to this comparison view

uso:laboratoare:new:04-appdev:need-to-know [2018/10/26 08:42]
mbarbulescu [Fișiere header vs Fișiere cod sursă în C]
uso:laboratoare:new:04-appdev:need-to-know [2019/10/24 11:23] (current)
adrian.zatreanu [Automatizarea procesului de compilare - Makefile]
Line 1: Line 1:
 ===== Need to know ===== ===== Need to know =====
  
-==== Fișiere header vs Fișiere cod sursă în C====+==== Fișiere header vsFișiere cod sursă în C====
  
 Nu există vreo diferență ''​tehnică''​ între cele două tipuri de fișiere; compilatorul Nu există vreo diferență ''​tehnică''​ între cele două tipuri de fișiere; compilatorul
Line 7: Line 7:
 este ceea ce dorești să faci. este ceea ce dorești să faci.
 Există însă o diferență culturală: Există însă o diferență culturală:
-    * ''​Declaraţiile''​ se găsesc în fișierele .h; acestea pot fi vizualizate ca o interfață către orice este implementat în fișierul .c corespondent.+    * ''​Declarările''​ se găsesc în fișierele .h; acestea pot fi vizualizate ca o interfață către orice este implementat în fișierul .c corespondent.
     * ''​Definițiile''​ se găsesc în fișierele .c; ele implementează interfața specificată în fișierul .h.     * ''​Definițiile''​ se găsesc în fișierele .c; ele implementează interfața specificată în fișierul .h.
  
 Un fișier .h poate (și este de obicei) inclus în mai multe fișiere .c. În cazul Un fișier .h poate (și este de obicei) inclus în mai multe fișiere .c. În cazul
 în care o funcție este definită în cadrul acestuia, va ajunge în mai multe fișiere în care o funcție este definită în cadrul acestuia, va ajunge în mai multe fișiere
-cod obiect, iar linker-ul se '​va ​plange' de multiple definiții ale simbolului respectiv.+cod obiect, iar linker-ul se '​va ​plânge' de multiple definiții ale simbolului respectiv.
 Din acesta cauză definițiile nu trebuie să fie în fișierele header. Din acesta cauză definițiile nu trebuie să fie în fișierele header.
  
-====Variabile în Makefile==== +La calea ''​~/​uso-lab/​04-appdev/​support/​need-to-know'' ​avem 3 fișiere cod sursă C și 2 fișiere header.
-Un fișier ​''​Makefile'' ​permite folosirea de variabile. Astfel, un exemplu uzual de fișier ''​Makefile''​ este: +
-  +
-<code make Makefile>​ +
-CC = gcc +
-CFLAGS = -Wall -g+
  
-allhello+<code bash> 
 +student@uso:~/​uso-lab/​04-appdev/​support/​need-to-know$ ls 
 +a.c     ​a.h ​    ​b.c ​    ​b.h ​    ​main.c 
 +</​code>​
  
-hello: hello.+Fișierul ''​main.c''​ este cel care conține și funcția main, adică punctul de intrare în program.
-        $(CC) hello.o -o hello+
  
-hello.ohello.c +<code bash> 
-        $(CC) $(CFLAGS) -c hello.c+student@uso:~/​uso-lab/​04-appdev/​support/​need-to-know$ cat main.c 
 +#include <stdio.h> 
 +#include <​stdlib.h>​
  
-clean: +#include "​a.h"​ 
-        rm *.o hello+#include "b.h" 
 + 
 +int main() { 
 +    printf("​globalVar = %d\n", globalVar);​ 
 +    print1(); 
 + 
 +    double value = print2(0.5);​ 
 +    printf("​value = %f\n", value); 
 +     
 +    return 0; 
 +}
 </​code>​ </​code>​
  
-În exemplul de mai sus au fost definite variabilele ​''​CC''​ și ''​CFLAGS''​. Variabila ''​CC''​ reprezintă compilatorul folositiar variabila ​''​CFLAGS'' ​reprezintă opțiunile (flag-urile) ​de compilare utilizate; în cazul de față sunt afișarea avertismentelor ​și compilarea cu suport de depanare. Referirea unei variabile se realizează prin intermediul construcției %%$%%(VAR_NAME). Astfel, ​''​%%$%%(CC)'' ​se înlocuiește cu ''​gcc''​, iar ''​%%$%%(CFLAGS)''​ se înlocuiește cu ''​-Wall -g''​.+Se observă faptul că pe lângă bibliotecile standard ​''​stdio''​ și ''​stdlib'',​ ''​main.c'' ​mai are nevoie ​de câteva resurse externe ​și 
 +anume ''​a.h''​ ș''​b.h''​. În aceste 2 fișiere găsim declarații de **funcții** și/sau **variable** pe care vrem să le folosim în ''​main.c''​.
  
-Observăm că șirurile ''​hello'',​ ''​gcc'',​ precum și flagurile date la compilare apar în foarte multe locuri. Modificați fișierul ''​Makefile''​ astfel încât să avem de schimbat o singură linie în cazul în care dorim să schimbăm numele executabilului,​ compilatorului sau să mai adăugăm un alt flag.+**a.h** 
 +<code bash> 
 +#ifndef A 
 +#define A
  
-Exemple variabilele predefite:​ +extern int globalVar;
-  * ** %%$%%@ ** se expandează la numele target-ului.  +
-  * ** %%$%%^ ** se expandează la lista de cerințe. +
-  * ** %%$%%< ** se expandează la prima cerință. ​+
  
-Înainte de a rezolva exercițiile, ​parcurgeți fișierele ​din directorul ​~/uso.git/labs/​04-appdev/​support/​need-to-know ​și încercați să înțelegeți cum sunt legate între ele fișierele.+void print1(); 
 + 
 +#endif 
 +</​code>​ 
 + 
 +**b.h** 
 +<code bash> 
 +#ifndef B 
 +#define B 
 + 
 +double print2(double angle); 
 + 
 +#endif 
 +</​code>​ 
 + 
 +Fiecare fișier header vine asociat cu un fișier cod sursă în care sunt definite toate funcțiile/​variabilele declarate în headere. 
 +Fișierele cod sursă includ toate headerele asociate. Spre exemplu''​a.c''​ include fișierul ''​a.h'',​ la fel șpentru ''​b.c''​. 
 +Trebuie reținut faptul că pentru a putea avea un program funcțional,​ trebuie să fie compilate toate fișierele ​cod sursă. 
 + 
 +În această situație, vom compila toate cele 3 fișiere cod sursă într-un singur binar. Pentru asta folosim tot ''​gcc''​. 
 + 
 +<code bash> 
 +student@uso:​~/uso-lab/​04-appdev/​support/​need-to-know$ gcc main.c a.c b.c -o main 
 +student@uso:​~/uso-lab/​04-appdev/​support/​need-to-know$ ls 
 +a.c     ​a.h ​    ​b.c ​    ​b.h ​    ​main ​   main.c 
 +</​code>​
  
-  - Scrieți un Makefile astfel încât fișierul main.c să compileze cu succes. (Hint: Compilați separat fiecare fișier obiect) 
-  - Rescrieți Makefile-ul de mai sus folosind variabile automate. ​ (Hint: https://​www.gnu.org/​software/​make/​manual/​html_node/​Automatic-Variables.html) 
 <​note>​ <​note>​
-De ce nu generează eroare ​de compilare ​folosirea variabilei globalVar ​în fișierul main.c?+Observați că fișierele header nu au fost incluse în comanda ​de compilare ​deoarece ele sunt incluse ​în fișierele cod sursă și nu e  
 +nevoie să le includem și aici.
 </​note>​ </​note>​
 +
 +<note warning>
 +**NU** includeți niciodată fișiere cod sursă în alte fișiere cod sursă.
 +</​note>​
 +
 +<note warning>
 +**NU** includeți niciodată definiții în fișiere header. Fișierele header sunt **doar** pentru declarații,​ fișierele cod sursă sunt cele
 +care trebuie să conțină implementarea.
 +</​note>​
 +
 +  - Rulați programul ''​main''​ și asigurați-vă că rularea lui se termină cu succes.
 + - Urmăriți pașii de mai jos:
 + - Declarați o funcție cu numele ''​bad''​ fără parametri care nu întoarce nimic, în fișierul ''​a.h''​ (Atenție: doar declarația!).
 + - În fișierul ''​main.c'',​ înainte de linia ''​return 0'',​ apelați această funcție (''​bad()''​).
 + - Compilați din nou acest program (cu aceeași comandă pe care ați văzut-o mai sus). Ce s-a întâmplat?​ Care este motivul? Discutați cu asistentul.
 + - Rezolvați problema apărută **fără** a șterge apelul de funcție ''​bad()''​ din funcția ''​main''​ din fișierul ''​main.c''​.
 +
 +<​note>​
 +De fiecare dată când vreți să apelați o funcție declarată într-un fișier header, trebuie să existe o definiție / implementare a ei într-un ​
 +fișier cod sursă, altfel programul nu este complet și nici corect!
 +</​note>​
 +
 +==== Linkare cu o bibliotecă ====
 +
 +Există câteva biblioteci care sunt linkate automat atunci când compilăm un program C. Una dintre acestea este ''​libc''​. Majoritatea ​
 +programelor C folosesc funcții din aceastea bibliotecă și s-a hotărât să fie linkată întotdeauna,​ însă aceasta nu este singura bibliotecă
 +externă pe care putem să o folosim. Putem să creăm noi o bibliotecă și să o linkăm programului nostru sau să folosim o bibliotecă deja 
 +existentă cum ar fi biblioteca ce conține funcții matematice ''​math''​.
 +
 +Pentru a folosi biblioteca ''​math''​ în programul nostru, trebuie să facem 2 lucruri.
 + - Să includem headerul ''​math.h''​ în program, pentru a putea preciză că se vor folosi funcții declarate acolo.
 + - În momentul în care compilăm programul nostru, trebuie să-i precizăm compilatorului că noi folosim funcții din biblioteca ''​math.h''​ și că vrem ca binarul nostru să fie linkat cu biblioteca ''​math''​.
 +
 +Urmăriți tutorialul de mai jos.
 +
 +În directorul ''​~/​uso-lab/​04-appdev/​support/​need-to-know''​ creați un nou director cu numele ''​using-math''​ și intrați în el.
 +
 +<code bash>
 +student@uso:​~/​uso-lab/​04-appdev/​support/​need-to-know$ mkdir using-math
 +student@uso:​~/​uso-lab/​04-appdev/​support/​need-to-know$ cd using-math
 +student@uso:​~/​uso-lab/​04-appdev/​support/​need-to-know/​using-math$ ls
 +student@uso:​~/​uso-lab/​04-appdev/​support/​need-to-know/​using-math$
 +</​code>​
 +
 +Creați aici un fișier simplu numit ''​main.c''​ cu următorul conținut:
 +
 +<​code>​
 +#include <​stdio.h>​
 +#include <​math.h>​
 +
 +int main(void) {
 + float x;
 + printf("​Give me a number: ");
 + scanf("​%f",​ &x);
 + printf("​The square root for it is: %f\n", sqrt(x));
 + return 0;
 +}
 +</​code>​
 +
 +<​note>​
 +Observați faptul că am inclus biblioteca ''​math.h''​ pentru a putea folosi funcția ''​sqrt''​ pentru calcularea radicalului unui număr.
 +</​note>​
 +
 +Compilați programul folosind ''​gcc''​ astfel încât executabilul obținut să se numeasca ''​main''​.
 +
 +<code bash>
 +student@uso:​~/​uso-lab/​04-appdev/​support/​need-to-know/​using-math$ ls
 +main.c
 +student@uso:​~/​uso-lab/​04-appdev/​support/​need-to-know/​using-math$ gcc main.c -o main
 +/​tmp/​ccRBgYrK.o:​ In function `main':​
 +main.c:​(.text+0x35):​ undefined reference to `sqrt'
 +collect2: error: ld returned 1 exit status
 +</​code>​
 +
 +Eroarea apare din cauză că ''​gcc''​ nu știe unde să localizeze funcția ''​sqrt''​ pe care noi o folosim în program. Pentru a rezolva această
 +eroare, atunci când compilăm programul trebuie să dăm un argument în plus lui ''​gcc''​ care să specifice că vrem să linkăm executabilul
 +nostru cu biblioteca ''​math''​. Practic, îi promitem compilatorului că la rularea programului,​ funcția ''​sqrt''​ va exista pentru că îi spunem
 +noi acum unde este.
 +
 +<code bash>
 +student@uso:​~/​uso-lab/​04-appdev/​support/​need-to-know/​using-math$ ls
 +main.c
 +student@uso:​~/​uso-lab/​04-appdev/​support/​need-to-know/​using-math$ gcc main.c -o main -lm
 +student@uso:​~/​uso-lab/​04-appdev/​support/​need-to-know/​using-math$ ./main
 +Give me a number: 1234
 +The square root for it is: 35.128336
 +</​code>​
 +
 +Acum programul a compilat cu succes și am putut să-l și rulăm.
 +
 +==== Automatizarea procesului de compilare - Makefile ====
 +
 +Până acum am compilat fișierele sursă folosind comanda ''​gcc''​ direct din terminal. Acest proces poate deveni anevoios în momentul în
 +care proiectul conține mai multe fișiere sursă sau dacă codul depinde de niște variable externe. Nu vrem să scriem comanda de compilare ​
 +de fiecare dată, vrem să **automatizăm acest proces**. Pentru asta folosim fișierele ''​Makefile''​.
 +
 +Dacă avem într-un directorul fișierul ''​test.c''​ și vrem să-l compilăm, pur și simplu rulăm comanda ''​gcc test.c''​ acolo și obținem un
 +executabil cu numele ''​a.out''​. Pot apărea situații când compilarea unui program este mai complexă. Spre exemplu, mai sus, existau 3 fișiere
 +sursă pe care trebuia să le compilăm ca programul să funcționeze. ​
 +
 +Un exemplu de fișier Makefile simplu care ar automatiza acest proces este următorul:
 +
 +<​code>​
 +main:
 + gcc main.c a.c b.c -o main
 +clean:
 + rm main
 +</​code>​
 +
 +Fișierul Makefile de mai sus conține 2 **reguli**: ''​main''​ și ''​clean''​. Regula main va fi folosită pentru a compila programul, pe 
 +când regula ''​clean''​ va fi folosită pentru a șterge toate fișierele generate în urma compilării.
 +
 +<note warning>
 +Atenție! Regulile în Makefile trebuie scrise la stânga de tot (fără spații la începutul rândului), iar comenzile aferente fiecărei reguli
 +trebuie să se afle pe linia următoare, iar linia să înceapă cu <​TAB>,​ **nu** spații.
 +</​note>​
 +
 + - Creați fișierul Makefile de mai sus la calea ''​~/​uso-lab/​04-appdev/​support/​need-to-know/''​. Denumiți fișierul ''​Makefile''​.
 +
 +Pentru a rula regula ''​main''​ trebuie să scriem comanda ''​make main''​.
 +
 +<code bash>
 +student@uso:​~$ cd ~/​uso-lab/​04-appdev/​support/​need-to-know
 +student@uso:​~/​uso-lab/​04-appdev/​support/​need-to-know$ ls
 +a.c     ​a.h ​    ​b.c ​    ​b.h ​    ​main.c ​   Makefile
 +student@uso:​~/​uso-lab/​04-appdev/​support/​need-to-know$ make main
 +gcc main.c a.c b.c -o main
 +student@uso:​~/​uso-lab/​04-appdev/​support/​need-to-know$ ls
 +a.c     ​a.h ​    ​b.c ​    ​b.h ​    ​main main.c ​   Makefile
 +</​code>​
 +
 +Vedem acum că a fost creat executabilul ''​main''​ pe care putem să-l rulăm în continuare.
 +
 + - Rulați executabilul ''​main''​ și asigurați-vă că funcționează.
 + - Adăugați o regulă numită ''​run''​ în Makefile care să aibă drept comandă asociată chiar comanda cu care rulăm programul ''​main''​. Nu treceți mai departe până când partea aceasta nu este clară.
 + - Ștergeți executabilul ''​main''​ folosind una dintre regulile definite în Makefile.
 +
 +Dacă se va rula în terminal doar comanda ''​make''​ (fără a fi urmată de vreun argument), atunci se va executa prima regulă găsită în Makefile, ​
 +în cazul acesta tot regula ''​main''​.
 +
 +
 +<code bash>
 +student@uso:​~$ cd ~/​uso-lab/​04-appdev/​support/​need-to-know
 +student@uso:​~/​uso-lab/​04-appdev/​support/​need-to-know$ ls
 +a.c     ​a.h ​    ​b.c ​    ​b.h ​    ​main.c ​   Makefile
 +student@uso:​~/​uso-lab/​04-appdev/​support/​need-to-know$ make
 +gcc main.c a.c b.c -o main
 +student@uso:​~/​uso-lab/​04-appdev/​support/​need-to-know$ ls
 +a.c     ​a.h ​    ​b.c ​    ​b.h ​    ​main main.c ​   Makefile
 +</​code>​
 +
 + - La calea ''​~/​uso-lab/​04-appdev/​support/​need-to-know/''​ creați un Makefile care să conțină regulile ''​main'',​ ''​run''​ și ''​clean''​. Nu treceți mai departe până când asistentul confirmă că este în regulă. ​
 +
 +
 +==== Următorii pași în lumea git-ului ====
 +
 +Înainte de a parcurge această parte a secțiunii, rulați comanda ''​cd ~/​my-awesome-project''​.
 +
 +Vom crea acum 2 fișiere noi în acest director. Le vom numi ''​tom.txt''​ și ''​jerry.txt''​. Scrieți câte un mesaj în fiecare dintre ele.
 +
 +<code bash>
 +student@uso:​~/​my-awesome-project$ touch tom.txt
 +student@uso:​~/​my-awesome-project$ touch jerry.txt
 +(..) # aici editați fișierele
 +</​code>​
 +
 +Vrem să vedem statusul repository-ul de git. Folosim comanda ''​git status''​.
 +
 +<code bash>
 +student@uso:​~/​my-awesome-project$ git status
 +# On branch master
 +# Untracked files:
 +#   (use "git add <​file>​..."​ to include in what will be committed)
 +#
 +# jerry.txt
 +# tom.txt
 +nothing added to commit but untracked files present (use "git add" to track)
 +</​code>​
 +
 +Scopul nostru acum este de a crea 2 commit-uri, unul care să se refere la adăugarea fișierului ''​tom.txt'',​ iar celălalt la adăugarea lui ''​jerry.txt''​
 +
 + - Creați 2 commit-uri, unul cum mesajul ''​added tom.txt file''​ și celălalt cu ''​added jerry.txt file''​. Verificați că cele 2 commit-uri s-au efectuat cu succes (hint: ''​git log''​).
 +
 +La rularea comenzii ''​git log''​ va trebui să primiți un output similar cu următorul:
 +
 +<​code>​
 +commit 368bf97efe83109491a3f21874aa763a8f1fe682
 +Author: student <​student@uso>​
 +Date:   Wed Oct 9 14:30:34 2019 +0300
 +
 +    added jerry.txt file
 +
 +commit 55e8b77f46ae355d67e8ed072d2e87c367a078c4
 +Author: student <​student@uso>​
 +Date:   Wed Oct 9 14:30:23 2019 +0300
 +
 +    added tom.txt file
 +
 +commit a67a3fb85fea113b25e412f813382c4cbaf196f0
 +Author: student <​student@uso>​
 +Date:   Wed Oct 9 14:26:38 2019 +0300
 +
 +    added main file
 +
 +commit 64bccb03c2f3a8e76730582676bd944902a0c1eb
 +Author: student <​student@uso>​
 +Date:   Mon Oct 7 15:49:20 2019 +0300
 +
 +    Initial commit
 +</​code>​
uso/laboratoare/new/04-appdev/need-to-know.1540532538.txt.gz · Last modified: 2018/10/26 08:42 by mbarbulescu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0