För att fixa detta kan man göra på några olika sätt
printf()/cout/cerr)
De två andra metoderna handlar om att lokalisera var problemet
uppstår, genom att avgränsa området som behövs undersökas.
Nackdelen med att lägga in debugutskrifter är att koden lätt
blir oöverskådelig samt att det tar extra tid att skriva in dem;
Oftast sker användningen i följande steg:
krasch -> lägg in debug -> krasch -> lägg in mer debug ->
krasch -> lokalisera/fixa felet ->ta bort debugutskrifter
Det vore klart enklare om man kunde se direkt vad som hänt,
och varför. Eftersom programvarufel har plågat programmerare
i alla tider så har verktyg skapats för syftet att
kunna se vad som händer i programmet hela tiden, så kallade
avlusningsprogram eller debuggers.
GNU projektet som utvecklat bl.a. kompilatorn gcc har även gjort gdb, som är en förkortning av Gnu DeBugger, och det är denna debugger vi kommer använda oss av. Det finns även ett program som förenklar användningen av gdb med ett grafiskt gränssnitt som heter DDD (DataDisplayDebugger), men denna introduktion koncentrerar sig på gdb.
En annan anledning att spendera några timmar på att lära sig en debugger är att det återbetalar sig i väldigt stor grad. Tid är den resurs vi har minst av, och genom att själv kunna fixa sina buggar så sparar du tid åt dig själv, eftersom du inte behöver vänta på att handledaren ska komma, du sparar tid åt handledaren som inte behöver göra samma sak hela tiden, du sparar tid åt din kamrat som inte behöver vänta så länge på dig när du får hjälp. Vore inte du glad om handledningen gick fort för den som är före dig i kön? :)
$ gcc -g list.cc -o list
$ gdb ./list
GNU gdb 19990928
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i686-pc-linux-gnu"...
(gdb)
List med funktionerna put(), size(), print()
och en mainfunktion som använder sig av listan.
Först vill vi få programmet att krascha, så vi startar det
med kommandot "run"
(gdb) run
Starting program: /a/home/alfs/WebDocs/gdb/./list1
Storlek: 0
Program received signal SIGSEGV, Segmentation fault.
0x8048731 in List::put (this=0xbffffa68, element=5, pos=0) at list1.cc:51
51 ny->next = cur->next;
(gdb)
put
funktion, närmare bestämt rad 51. Vi får även se vilka argument
vi skickade med till put. Så varför krashade programmet?
Tydligen har det något att göra med rad 51, så vi tittar efter
vad ny och cur pekar på.
Till detta används kommandot print
(gdb) print ny
$1 = 0x8049b70
(gdb)
ny är en pekare, så den bör innehålla en minnesadress.
Vi kan titta på vad som finns i denna adress på samma sätt som
vi skulle gjort i C; Via dereferensiering med *
(gdb) print *ny
$2 = {data = 5, next = 0x0}
(gdb)
cur då?
(gdb) print *cur
Cannot access memory at address 0x0.
(gdb)
list
så får vi upp källkoden runtomkring problemet
(gdb) list
46 while(count-- > 0) { prev = cur; cur = cur->next; }
47
48 ny = new nod;
49
50 ny->data = element;
51 ny->next = cur->next;
52
53 if (pos == 0)
54 head = ny;
55 else
(gdb)
cur tilldelas, så vi använder
list 46 för att titta på koden runt
rad 46
(gdb) list 46
41 pnod cur, prev, ny;
42 int count;
43
44 cur = head;
45 count = pos;
46 while(count-- > 0) { prev = cur; cur = cur->next; }
47
48 ny = new nod;
49
50 ny->data = element;
(gdb)
cur är samma som head till att börja
med, och räknas sedan fram i loopen. Men eftersom
count = pos = 0 så kommer vi aldrig in i loopen, så
vi undersöker hur head ser ut
(gdb) print head
$1 = 0x0
(gdb)
head är också NULL, och det är som det ska vara
eftersom listan är tom.
Vad är felet då? Tydligen kan vi inte använda rad 51 om vi har en tom lista, så vi måste flytta ner denna rad till testet för specialfallet (rad 53--56) och får följande
if (pos == 0) {
ny->next = head;
head = ny;
} else {
ny->next = cur->next;
prev->next = ny;
}
help breakpoints för mer information.
break,
eftersom det inte går att köra vidare om programmet kraschat.
(Man vet ungefär var felet är, sätter en brytpunkt, och stegar
sig framåt för att se var det går snett)
(gdb) break List::put
Breakpoint 1 at 0x80486de: file list1.cc, line 44.
(gdb)
(gdb) break 92
Breakpoint 1 at 0x80487ee: file list1.cc, line 92.
(gdb)
(gdb) backtrace
#0 List::put (this=0xbffffa68, element=5, pos=0) at list1.cc:44
#1 0x8048855 in main () at list1.cc:96
(gdb)
(gdb) break main
Breakpoint 1 at 0x80486a2: file ex1.cc, line 12.
(gdb) run
Starting program: /a/home/alfs/WebDocs/gdb/./ex1
Breakpoint 1, main () at ex1.cc:12
12 a = 2;
(gdb) next
13 b = kvadrat(a);
(gdb) next
14 cout << "Kvadraten på " << a << " är " << b << endl;
(gdb)
(gdb) break main
Breakpoint 1 at 0x80486a2: file ex1.cc, line 12.
(gdb) run
Starting program: /a/home/alfs/WebDocs/gdb/./ex1
Breakpoint 1, main () at ex1.cc:12
12 a = 2;
(gdb) step
13 b = kvadrat(a);
(gdb) step
kvadrat (x=2) at ex1.cc:5
5 y = x*x;
(gdb) step
6 return y;
(gdb) step
7 }
(gdb) step
main () at ex1.cc:14
14 cout << "Kvadraten på " << a << " är " << b << endl;
(gdb)
$ gcc -Wall list.cc -o list
Istället för att skriva "break", "step", "run" kan man förkorta dem till "b", "s", "r". Det finns förkortningar för de flesta kommandon, normalt räcker det med första bokstaven, men börjar många kommandon på samma bokstav måste man ange tydligare (dvs med fler bokstäver) vilket kommando som åsyftas.
Jag har tänkt att uppdatera denna introduktion med ett större exempel, som studenten själv får hitta felen i, tillsammans med ett "facit" om det blir för svårt.
Vill du veta mer om gdb, så kan du använda kommandot help.
Mer uttömmande information fås genom att skriva $ info gdb i kommandoprompten, alternativt Alt-x info i emacs.
$Id: gdb.html,v 1.2 2000/05/04 13:25:05 alfs Exp alfs $