Skip to content

Instantly share code, notes, and snippets.

@javiermon
Forked from cqfd/learn-c-with-gdb.org
Created January 15, 2014 23:38
Show Gist options
  • Save javiermon/8446956 to your computer and use it in GitHub Desktop.
Save javiermon/8446956 to your computer and use it in GitHub Desktop.

High-level

I want to write about why GDB is a great tool for learning C. At least part of the difficulty in learning C is that the language isn’t as interactive as using Python or Ruby.

TL;DR You can kinda use gdb as a repl for c

What’s the smallest possible program we could debug to learn about pointers?

Motivation and thesis

One of my favorite things about being a facilitator at Hacker School is that part of my job is to think about how to learn efficiently.

One thing we’ve noticed is that our most efficient students are always looking for ways to validate their understanding. Something something you want feedback to come as quickly as possible.

A number of Hacker Schoolers this batch have been learning C. Compared to many more modern languages, such as Ruby or Scheme or Haskell, C is challenging to learn interactively; the traditional write-compile-run cycle means that it can take orders of magnitude longer to validate your understanding than in a language with a repl. What happens if I add one to this pointer? Hmm… let’s fire up vim, add one to that pointer, stick in a couple just-in-case printfs, recompile, rerun, realize you should have included another printf… I’m exaggerating a bit, and things could admittedly be worse <insert link to article on old computers or something>, but it’s not ideal.

It occurred to me recently that there’s a better way to learn C: use gdb as a repl.

Assumptions

Let’s assume you know a little bit about C. You know how to write simple functions, you can compile your programs using gcc. You kind of know what pointers do.

Commands

The print command, the ptype command, and the x command. How much C can you learn just by fiddling with p/pt/x?

The post

TODO: It’s not clear where the program is going as we develop it. It doesn’t feel like we’re approaching a stopping point.

The plan is to build a simple program that illustrates some fun features of C. We’ll end up with an illustration of the difference between pointers and arrays. We’ll write our program a little bit at a time, stopping to play with it interactively in gdb along the way. If at any point you’re unfamiliar with a gdb command, run “help <command name>” to learn more.

Let’s begin by considering the following aptly-named minimal.c program:

int main()
{
    return 0;
}

Although the program doesn’t do anything, we can still investigate it with gdb:

gcc -g minimal.c -o minimal
gdb minimal

If you’re following along at your terminal, you should now find yourself dumped into a rather stark gdb prompt.

You can use the “list” command to see where you’re at in the source:

(gdb) list
1        int main()
2        {
3            return 0;
4        }

Let’s set a breakpoint on main’s return statement and start running the program:

(gdb) break 3
(gdb) run

We’ve hardly got any program to poke yet, but we can still use gdb as a repl:

(gdb) print 1 + 2
$1 = 3
(gdb) print (int) 214748368
$2 = -2147483648

As modest as this may be, it’s actually pretty cool: we can use gdb to explore arithmetic in C, which, as you may have noticed from the second print command, can be surprisingly tricky!

Let’s add a bit more structure to minimal.c:

int main()
{
    int i;
    return 0;
}

Now that we’ve got a variable to play with, let’s hop back into gdb (don’t forget to recompile) and start playing with it:

(gdb) break main
(gdb) run
(gdb) print i
$1 = 32767

Remember that in C, it’s your responsibility to initialize your local variables!

A variable in C labels a particular chunk of memory. One of C’s defining features is that it gives us direct access to the address of a variable’s chunk of memory, using the & operator:

(gdb) print &i
$2 = (int *) 0x7fff5fbff624

In words, this says that the address of `i`’s chunk has the value 0x7fff5fbff624.

Now that we’ve got an address, we can poke at it with gdb’s “x” command. The “x” command allows us to directly examine memory. Let’s first make sure that `i`’s chunk of memory holds `i`’s value, 32767:

(gdb) x/1dw &i
0x7fff5fbff624: 32767

In full, “x/1dw” tells gdb to “examine 1 (signed) decimal word”, where a word means four contiguous bytes (on my machine at least). For more info on formatting flags, try running “help x” in gdb.

On my machine, ints take four bytes of space. The `x` command allows us to look at each individual byte:

(gdb) x/4xb &i
0x7fff5fbff624: 0xff 0x7f 0x00 0x00

In words, this asks gdb to examine four hex bytes starting at the address of `i`.

It’s perhaps not obvious how these individual bytes relate to `i`’s decimal value, so let’s take a look at `i`’s value in hex:

(gdb) print/x i
$3 = 0x7fff

My machine is little endian [http://en.wikipedia.org/wiki/Endianness], so `i`’s least significant byte, 0xff, comes first in memory, followed by 0x7f.

Let’s now add a bit more structure to minimal.c:

int main()
{
    int i;
    int arr[3];
    return 0;
}

Back in gdb, we can take a look at our new array:

(gdb) break main
(gdb) run
(gdb) print arr
$1 = {0, 0, 1606415928}

Once again, in C, it’s your responsibility to initialize your local variables!

Concluding tips

Personally, I find gdb to be a bit too stark. I prefer using cgdb [http://cgdb.github.com/].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment