Utilizarea și programarea calculatoarelor 2 - Laborator 4
Depanarea programelor
Pentru a depana erorile care apar la scrierea programelor, avem la
dispoziție mai multe procedee.
În primul rând trebuie făcută o lectură atentă a programului. Tot după cum
programul nu trebuie scris la întâmplare, ci numai după o gândire precisă,
la fel urmărind cu atenție textul programului, și regândind situațiile în
care se execută diversele instrucțiuni (se intră pe o ramură sau alta,
se intră sau se iese dintr-un ciclu, se atribuie anumite valori), putem
descoperi eroarea adesea mai rapid decât prin alte procedee. E util să
"rulăm mental" programul, instructiune cu instructiune pe un set simplu
de date de intrare, uneori aceasta e suficient pentru a detecta eroarea.
În al doilea rând, se pot introduce în puncte cheie din program instrucțiuni
suplimentare care tipăresc valorile unor variabile, sau mesaje care indică
locul în care a ajuns execuția programului și ramurile urmate. În acest fel
putem urmări programul la rulare și obține indicii despre cauza erorii.
Pentru cazurile mai dificile, putem folosi programe și medii de depanare.
Un astfel de mediu este Data Display Debugger (ddd), care oferă
o interfață grafică pentru programe de depanare ca de exemplu GNU Debugger
(gdb) din aceeași suită de dezvoltare de programe ca și gcc.
Pentru a depana un program în acest fel, el trebuie compilat obligatoriu
cu opțiunea -g pentru gcc, care are ca efect
generarea de informație suplimentară pentru depanare (de ex.
numele variabilelor din program, și corespondența dintre liniile codului
sursă și instrucțiunile codului executabil).
După compilarea gcc -g -o fișier fișier.c, lansați
depanatorul în fundal cu comanda ddd fișier &
Astfel linia de comandă rămâne liberă.
Fereastra principală e împărțită în mai multe părți (vezi meniul
View):
o porțiune centrală cu sursa programului, și una inferioară in care se
prezintă interacțiunea cu programul de depanat și cu depanatorul gdb.
Pe lângă aceasta, apare o altă mică fereastră cu butoane pentru principalele
comenzi. Ne rezumăm la cele mai simple:
- Încărcarea programului
Dacă ddd a fost lansat fără argumente, programul de depanat poate
fi deschis ulterior cu File -> Open Program.
- Întreruperea programului
Se pot specifica puncte în care execuția programului să fie intreruptă
(breakpoints) poziționând cursorul pe începutul liniei respective de program
și apăsând butonul Break. Va apărea un semn roșu de STOP, și
programul se va opri de fiecare dată înaintea instrucțiunii respective.
Un punct de întrerupere trebuie specificat înainte de rularea programului,
altfel el va fi executat normal, fără oprire.
- Afișarea variabilelor
Selectând cu cursorul numele unei variabile, și apăsând apoi butonul
Display, în fereastra de vizualizare va apărea o căsuță care indică
permanent numele variabilei și valoarea ei curentă. Căsuțele pot fi rearanjate
in fereastră după dorință.
Când programul e întrerupt, se pot examina și alte variabile, selectându-le
și apăsând butonul Print.
- Rularea programului
Se face cu butonul Run din mica fereastră de comenzi. Dacă sunt necesare argumente în linia de comandă, se pot introduce din meniul principal:
Program->Run (sau cu tasta F2). Întreruperea programului
se face cu butonul roșu Interrupt.
- Rularea pas cu pas
Cu butonul Step se execută o linie sursă din program, intrând în
apelurile de funcții (cu oprire la prima instrucțiune a funcției în
acest caz). Cu butonul Next se execută de asemenea o linie sursă,
dar un apel de funcție e considerat ca o singură instrucțiune (programul
nu se oprește în funcție, ci doar după execuția ei). În cazul unor funcții
sistem, vom folosi Next, pentru a nu intra în detaliile
execuției lor. Butonul Continue reia execuția programului, până
când acesta e întrerupt sau ajunge la următorul punct de oprire.
- Interacțiunea cu programul
Atât intrarea cât și ieșirea programului (citirea și tipărirea) se fac prin
intermediul părții inferioare a ferestrei, care ia locul terminalului în
care acesta e executat în mod normal. Tot aici se efectuează și interacțiunea
în mod text cu depanatorul gdb. Puteți observa cum apăsările pe
butoane sunt convertite în comenzi pentru acesta.
După detectarea erorii, programul trebuie editat, recompilat, și reîncărcat
în depanator (altfel va fi executată tot versiunea anterioară).
Problemă: sortarea prin interclasare
Scrieți un program care ia două fișiere, fiecare deja sortat, și afișează
conținutul lor combinat și sortat.
Numele celor două fișiere de intrare se dau pe linia de comandă.
Fișierele pot conține după caz, numere întregi, numere reale, cuvinte,
sau linii de text (se va rezolva una din variante).
Principiul de rezolvare e simplu, fiecare din fișiere fiind gata sortat:
se citește câte un obiect (element) din fiecare fișier, și se compară.
Ce se poate spune despre elementul cel mai mic, în comparație cu toate
elementele rămase în ambele fișiere ? După afișare, se avansează în
fișierul corespunzător, și procedura se repetă. Atenție la sfârșitul
de fișier(e) !
Implementați problema gradat, în următorii pași. După fiecare pas, compilați
programul și testați rularea lui.
- Testați că s-a introdus exact numărul dorit de argumente pe linia de
comandă. Dacă nu, afișați un mesaj la stderr și terminați programul.
- Deschideți fișierele date pe linia de comandă. În caz de eroare,
afișați un mesaj la stderr și ieșiți din program. Închideți fișierele
la sfârșitul programului.
Atenție Dacă aceeași prelucrare trebuie făcută de mai multe ori
(deschiderea fișierelor, testarea rezultatului, afișarea mesajului de eroare,
ieșîrea din program, pentru toate fișierele), nu duplicați fragmente de cod
repetându-le cu mici modificări. Aceasta e un stil dăunător de programare,
pentru că ulterior orice modificare trebuie repetată în mai multe locuri.
Creați o funcție care cuprinde prelucrările care trebuie repetate, și
apelați-o de mai multe ori cu parametrii corespunzători.
- Citiți câte un obiect (element) din fiecare fișier și afișați-l.
- Implementați programul complet.
Variantă avansată: Gândiți-vă cum s-ar putea scrie o funcție generică
mergesort care ia ca parametri cele două fișiere deja deschise
(FILE *) și produce combinația sortată la ieșire.
Pe lângă fișîere, funcția ar mai avea nevoie ca parametri de câte o
funcție care citește și respectiv scrie un obiect din (în) fișier, o
funcție care compară două obiecte, și două adrese la cre să se citească
cele două elemente. Funcțiile de citire și scriere vor lua ca parametru
un fișier și un un void *, adresa la care va fi citit / de la care va
fi scris elementul din/în fișier. Funcția de citire ar fi util să semnaleze
prin valoarea returnată dacă citirea s-a făcut corect (sau s-a ajuns, de
exemplu la sfârșit de fișier). Eventual funcția de citire ar putea
returna adresa obiectului citit, dacă acesta trebuie alocat dinamic, cum
ar fi cazul liniilor de lungime necunoscută. Funcția de comparație
va avea un tip similar celui folosit pentru qsort (care la rândul
lui e similar funcției strcmp).
Marius Minea
Last modified: Sun Mar 21 15:31:03 EET 2004