This article introduces the GNU Debugger (GDB).

Here we have code to build a list.

#include <stdio.h>
#include <stdlib.h>

struct node {
    int element;
    struct node *next;
};

struct node *append(struct node *l, int x)
{
    struct node *n = malloc(sizeof (n));
    n->element = x;
    n->next = NULL;

    if (l == NULL)
        l = n;
    else {
        while (l != NULL)
            l = l->next;

        l->next = n;
    }

    return l;
}

int main(void)
{
    struct node *l = append(NULL, 0);
    append(l, 1);
    append(l, 2);

    return EXIT_SUCCESS;
}


% gcc -o list list.c
% ./list
Segmentation fault

GDB can help you find the cause of a segfault, add -g to your compile command and re-run. -g tells gcc to emit debugging information. The debugging information is used by gdb.

% gcc -o list list.c -g
% gdb ./list
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/richardipsum/Desktop/list...done.
(gdb) run
Starting program: /home/richardipsum/Desktop/list

Program received signal SIGSEGV, Segmentation fault.
0x000000000040056a in append (l=0x0, x=1) at list.c:21
21          l->next = n;

GDB shows us where the segfault occurs.

Breakpoints

GDB lets the user set a break point, this is a point in the code where execution can be stopped, which allows the user to examine the state of the program's objects. Afterward the user can resume execution.

Here we set a breakpoint at line 13, just after we assign our element to its node.

% gcc -o list list.c -g
% gdb ./list
% ...
% ...
% (gdb) break 13
Breakpoint 1 at 0x400532: file list.c, line 13.
(gdb) run
Starting program: /home/richardipsum/Desktop/list

Breakpoint 1, append (l=0x0, x=0) at list.c:13
13      n->next = NULL;

At this point n->element should have been assigned the value of x (0), to confirm this we can use gdb's print command.

% (gdb) print n->element
% $1 = 0

To resume execution we use the continue command.

(gdb) continue
Continuing.

Breakpoint 1, append (l=0x601010, x=1) at list.c:13
13      n->next = NULL;

Notice that l now has a value of 0x601010, our first call to append appended the node with element 0 to the empty list and returned a pointer to a node located at address 0x601010, the beginning of the list l. Our second call to append will append the node with element 1 to l.

Stepping

Another useful command is the step command, this allows the user to examine execution step by step.

Note that if you hit return without entering a command gdb will assume the command entered previously, this makes stepping very convenient.

(gdb) step
15      if (l == NULL)
(gdb)
18          while (l != NULL)
(gdb)
19              l = l->next;
(gdb)
18          while (l != NULL)
(gdb)
21          l->next = n;
(gdb)

Program received signal SIGSEGV, Segmentation fault.
0x000000000040056a in append (l=0x0, x=1) at list.c:21
21          l->next = n;

(gdb) print l
$1 = (struct node *) 0x0

l is NULL, dereferencing NULL is undefined and causes a segfault on the computer running this code.

GDB is a powerful tool, it can do a lot more than has been shown here, for more information see gdb online docs.