C++ POO Lab Lucrarea 1

De la WikiLabs
Jump to navigationJump to search

În acest laborator ne vom reaminti sintaxa limbajului C, vom exersa utilizarea mediului de dezvoltare CLion și a debugger-ului.

Tool-ul de depanare GDB

Depanatorul GNU, cunoscut drept GDB (GNU debugger), este depanatorul standard pentru sistemul de software GNU.
Scopul unui depanator precum GDB este de a permite utilizatorului să vadă ce se întâmplă în interiorul unui alt program în timp ce acesta se execută sau ce s-a întâmplat cu programul în momentul în care acesta a dat crash.
GDB este capabil de a face 4 mari categorii de operații (și alte tipuri de operații ce duc la îndeplinirea celor 4):

  • să pornească programul, specificând orice ar putea interveni în buna funcționare a acestuia;
  • să facă programul să se oprească din execuție în anumite condiții specificate de utilizator;
  • să examineze ce s-a întâmplat în momentul opririi programului;
  • să schimbe anumite lucruri în program pentru a putea corecta efectele unui bug și a investiga urmările altuia.

Odată pornit, GDB citește comenzi din terminal până la întâlnirea comenzii de ieșire "quit".

Atenție: Pentru a putea folosi debugger-ul, la generarea fișierului executabil trebuie folosită opțiunea -g, care adaugă simboluri de debug în executabil, fără de care depanarea nu este posibilă. (vezi Laboratorul 1)

Exemplul unei comenzi de compilare în vederea depanării:

  student@pracsis01 ~/Desktop $ gcc -g hello.c -o hello

Comenzi specifice GDB

Cea mai folosită modalitate de a porni tool-ul de depanare este de a scrie gdb în terminal, urmat de numele executabilului ce se dorește a fi depanat.

  student@pracsis01 ~/Desktop $ gdb hello

O parte din comenzile cele mai utilizate ale GDB sunt următoarele (pentru lista completă studiați pagina de manual GDB - man gdb):

Opțiune Efect
break [nume_fișier:] nume_funcție
break [nume_fișier:] număr_linie
Setează un breakpoint (punct de întrerupere) la începutul funcției nume_funcție sau la linia specificată prin număr_linie din fișierul specificat prin nume_fișier.
run [listă_argumente] Pornește programul în execuție (cu lista de argumente, dacă au fost specificate).
bt Backtrace: afișează stiva de program.
print expresie Afișează valoarea unei expresii (valoarea stocată într-o variabilă, valoarea returnată de o funcție etc.).
c Continuă execuția programului (după ce a fost oprit, spre exemplu după un breakpoint).
next Execută următoarea linie de program (după oprire), sărind peste orice apel de funcție care se găsește în această linie.
list [nume_fișier:] nume_funcție Afișează liniile de cod ale programului din vecinătatea locului unde este oprit acum.
step Execută următoarea linie de program (după oprire), intrând în orice funcție apelată de acea linie.
help [nume] Afișează informații despre comanda GDB nume sau informații generale despre utilizarea GDB.
quit Iese din GDB.
Observație: Cele mai multe dintre aceste comenzi pot fi apelate doar prin prima literă a numelui lor.

Spre exemplu, următoarea comandă:

  (gdb) next

este identică cu:

  (gdb) n
Observație: Dacă se apasă pe enter fără a scrie o comandă, se repetă ultima comandă specificată.

Exemplu de depanare

Se dă fișierul sursă suma_fact.c, care conține următoarele instrucțiuni:

#include<stdio.h>

unsigned int factorial(int n) {
    if (n == 0) {
        return 1;
    }
    int i;
    unsigned int fact = 0;
    for (i = 1; i <= n; i++){
        fact *= i;
    }
    return fact;
}


int main() {
    int n, i;
    do {
        printf("Introduceti un numar natural mai mic sau egal cu 8: ");
        scanf("%d", &n);
    } while (n < 0 || n > 8);
    unsigned int suma = 0;
    for (i = 0; i <= n; i++) {
        suma += factorial(i);
    }
    printf("%u\n", suma);
    return 0;
}

Programul își propune să calculeze suma factorialelor de la 0 la n, unde n este introdus de la tastatură. Dacă rulăm programul vom obține ca rezultat numărul 1, indiferent de ce a fost introdus de la tastatură. Ne propunem să descoperim bug-ul folosind GDB.
Compilăm sursa, adăugând simboluri de debug în executabil, după care lansăm în execuție debugger-ul:

  student@pracsis01 ~/Desktop $ gcc -g suma_fact.c -o suma_fact
  student@pracsis01 ~/Desktop $ gdb suma_fact
Atenție: Comanda de mai sus lansează în execuție numai debugger-ul, nu și programul suma_fact.
Observație: Putem observa că am pornit debugger-ul prin faptul că fiecare rând nou din linia de comandă începe acum astfel:
  (gdb)

Punem un breakpoint la linia 20, unde se citește valoarea lui n:

  (gdb) b 20
  Breakpoint 1 at 0x80484c2: file suma_fact.c, line 20.
  (gdb) 

Pornim programul în interiorul debugger-ului:

  (gdb) run
  Starting program: /home/student/Desktop/suma_fact
   
  Breakpoint 1, main () at suma_fact.c:20
  20	        scanf("%d", &n);
  (gdb) 

Trecem mai departe și introducem o valoare pentru n:

  (gdb) next
  Introduceti un numar natural mai mic sau egal cu 8: 6 
  21	    }while(n<0 || n>8);
  (gdb) 

Vom parcurge programul pas cu pas până la apelarea funcției factorial:

  (gdb) n
  22	    unsigned int suma = 0;
  (gdb) n
  23	    for(i=0; i<=n; i++){
  (gdb) n
  24	        suma += factorial(i);
  (gdb) s
  factorial (n=0) at suma_fact.c:4
  4	    if(n==0){
  (gdb) s
  5	        return 1;
  (gdb) s
  13	}
  (gdb) s
  main () at suma_fact.c:23
  23	    for(i=0; i<=n; i++){
  (gdb) s
  24	        suma += factorial(i);
  (gdb) s
  factorial (n=1) at suma_fact.c:4
  4	    if(n==0){
  (gdb) s
  8	    unsigned int fact=0;
  (gdb) s
  9	    for(i=1; i<=n; i++){
  (gdb) s
  10	        fact *= i;
  (gdb) s
  9	    for(i=1; i<=n; i++){
  (gdb) s
  12	    return fact;
  (gdb) print fact
  $1 = 0
  (gdb) 

Observăm că valoarea returnată de funcția factorial pentru orice număr nenul este 0. Acest lucru se datorează inițializării variabilei fact cu valoarea 0 la începutul funcției factorial. Putem ieși din debugger pentru a modifica linia 8 din fișierul sursă suma_fact.c:

  (gdb) q
  A debugging session is active.
   
    	Inferior 1 [process 4279] will be killed.
   
  Quit anyway? (y or n) y 
  student@pracsis01 ~/Desktop $
Observație: Pentru a putea intra în interiorul funcției factorial este absolut necesar să se folosească comanda step și nu comanda next care ar trece peste acea linie 24 fără a intra în corpul funcției factorial.

După acest laborator veți putea folosi IDE-ul Netbeans pentru a scrie și depana programe în C. Totodată veți relua și recapitula noțiunile legate de pointeri în C.

Utilizarea IDE-ului CLion

Netbeans este un mediu integrat de dezvoltare (IDE) open-source și gratuit care permite dezvoltarea de programe în limbajele Java, C, C++, Javascript, HTML, PHP și Groovy. Netbeans nu instalează și compilatoare pentru Java și C/C++, acestea trebuie instalate manual, în prealabil.

Atenție: Imaginea de Linux folosită la Programarea Calculatoarelor are deja instalat compilatorul de C.

Instalarea GCC

Instalarea GCC în Linux

Pentru instalarea compilatorului de C (GCC) în distribuțiile de Linux provenite din Ubuntu (Ubuntu, Kubuntu, Xubuntu, Mint, LMDE, etc.) se poate folosi comanda:

apt-get install -y build-essential

Instalarea GCC în Windows

Pentru compilarea de programe cu GCC în Windows, aveți nevoie de instalarea acestui compilator care poate fi făcută prin instalarea unuia din următoarele două suite de programe:

Atenție: Nu uitați ca la instalare să bifați în lista de pachete și compilatorul de C (gcc), make și git.

Instalarea CLion

De la adresa https://www.jetbrains.com/clion/download/#section=linux puteți descărca kitul de instalare pentru sistemul vostru de operare (imaginea de Virtualbox cu Linux folosită la Programarea Calculatoarelor ruelază un Linux pe 32 de biți).

Aveți aici un tutorial pentru modalitatea de utilizarea a IDE-ului pentru depanarea codului.

Nu uitați de regulile următoare: Convenții de cod - C