#include <stdio.h>
//compile with: gcc -Wall -Wextra c-c.c -o c-c
//run with: ./c-c -df -n hello # word: -df letters: d f | word: -n letters: n | stopped at: hello
//Your two lines (unchanged), inside a main so they can run. The outer
//loop steps through the WORDS on the command line; the inner loop
//steps through the LETTERS of one word.
int main(int argc, char *argv[])
{
int c;
while (--argc > 0 && (*++argv)[0] == '-') {
printf("word: %s letters:", *argv);
while((c = *++argv[0]))
printf(" %c", c);
printf("\n");
}
if (argc > 0)
printf("stopped at: %s (doesn't start with -)\n", *argv);
return 0;
}
win@DESKTOP-MEIH88T:~/webdev-projects$ gcc -g -Wall -Wextra c-c.c -o c-c
win@DESKTOP-MEIH88T:~/webdev-projects$ gdb --args ./c-c -df -n hello
GNU gdb (Ubuntu 17.1-2ubuntu1) 17.1
Copyright (C) 2025 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".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./c-c...
(gdb) list 9,23
9 int main(int argc, char *argv[])
10 {
11 int c;
12
13 while (--argc > 0 && (*++argv)[0] == '-') {
14 printf("word: %s letters:", *argv);
15 while((c = *++argv[0]))
16 printf(" %c", c);
17 printf("\n");
18 }
19 if (argc > 0)
20 printf("stopped at: %s (doesn't start with -)\n", *argv);
21
22 return 0;
23 }
(gdb) start
Temporary breakpoint 1 at 0x117c: file c-c.c, line 13.
Starting program: /home/win/webdev-projects/c-c -df -n hello
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/x86_64-linux-gnu/libthread_db.so.1".
Temporary breakpoint 1, main (argc=4, argv=0x7fffffffde08) at c-c.c:13
13 while (--argc > 0 && (*++argv)[0] == '-') {
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/win/webdev-projects/c-c -df -n hello
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/x86_64-linux-gnu/libthread_db.so.1".
word: -df letters: d f
word: -n letters: n
stopped at: hello (doesn't start with -)
[Inferior 1 (process 227242) exited normally]
(gdb) info args
(gdb) start
Temporary breakpoint 2 at 0x55555555517c: file c-c.c, line 13.
Starting program: /home/win/webdev-projects/c-c -df -n hello
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/x86_64-linux-gnu/libthread_db.so.1".
Temporary breakpoint 2, main (argc=4, argv=0x7fffffffde08) at c-c.c:13
13 while (--argc > 0 && (*++argv)[0] == '-') {
(gdb) info args
argc = 4
argv = 0x7fffffffde08
(gdb) info locals
c = 0
(gdb) print argc
$1 = 4
(gdb) ptype argv
type = char **
(gdb) ptype *argv
type = char *
(gdb) print argv[1]
$2 = 0x7fffffffe0f1 "-df"
(gdb) print *argv[1]
$3 = 45 '-'
(gdb) print argv[1][1]
$4 = 100 'd'
(gdb) print argv[4]
$5 = 0x0
(gdb) print sizeof(argv[0])
$6 = 8
(gdb) print/x argc
$7 = 0x4
(gdb) print/t argc
$8 = 100
(gdb) print/d *argv[1]
$9 = 45
(gdb) print/x *argv[1]
$10 = 0x2d
(gdb) x/3s argv[1]
0x7fffffffe0f1: "-df"
0x7fffffffe0f5: "-n"
0x7fffffffe0f8: "hello"
(gdb) x/4cb argv[1]
0x7fffffffe0f1: 45 '-' 100 'd' 102 'f' 0 '\000'
(gdb) x/4b argv[1]
0x7fffffffe0f1: 45 '-' 100 'd' 102 'f' 0 '\000'
(gdb) x/4xb argv[1]
0x7fffffffe0f1: 0x2d 0x64 0x66 0x00
(gdb) x/5xg argv
0x7fffffffde08: 0x00007fffffffe0d3 0x00007fffffffe0f1
0x7fffffffde18: 0x00007fffffffe0f5 0x00007fffffffe0f8
0x7fffffffde28: 0x0000000000000000
(gdb) x/5i $pc
=> 0x55555555517c <main+19>: jmp 0x5555555551e9 <main+128>
0x55555555517e <main+21>: mov -0x20(%rbp),%rax
0x555555555182 <main+25>: mov (%rax),%rax
0x555555555185 <main+28>: lea 0xe7c(%rip),%rdx # 0x555555556008
0x55555555518c <main+35>: mov %rax,%rsi
(gdb)
This is a fuller tour than last time, organized by what you want to do. Shell commands and GDB commands are kept in separate blocks, and every output is real, from sessions I ran on your c-c.c just now.
0. Setup (in bash)
gcc -g -Wall -Wextra c-c.c -o c-c
gdb --args ./c-c -df -n hello
1. Seeing the code: list
Type inside gdb:
list 9,23
This prints lines 9–23 with their numbers, so you know which line numbers to use for breakpoints. A plain list shows the 10 lines around where you are.
2. Starting: start and run
Type inside gdb:
start
Temporary breakpoint 1, main (argc=4, argv=0x7fffffffd708) at c-c.c:13
13 while (--argc > 0 && (*++argv)[0] == '-') {
start sets a temporary breakpoint at main (it deletes itself after one hit) and runs. It's the quickest way to begin.
run starts without stopping unless a breakpoint is set. You can also change the arguments for a new run: run -nr restarts with -nr as the only argument.
3. Looking at values
Type inside gdb:
info args
info locals
argc = 4
argv = 0x7fffffffd708
c = 0
c = 0 here is luck. c hasn't been assigned yet, so it holds whatever was in that memory. It could have been anything.
Types:
ptype argv
ptype *argv
type = char **
type = char *
GDB confirms the two-level structure: argv is a pointer to pointers, and *argv is one string pointer.
print accepts any C expression:
print argv[1]
print *argv[1]
print argv[1][1]
print argv[4]
print sizeof(argv[0])
$4 = 0x7fffffffdae6 "-df"
$5 = 45 '-'
$8 = 100 'd'
$9 = 0x0
$10 = 8
argv[4] is 0x0, the NULL that ends the array. sizeof a pointer is 8 bytes on your 64-bit machine. The $4, $5 labels are saved results, so you can reuse them later, for example print $4.
Number formats are written as print/format:
print/x argc
print/t argc
print/d *argv[1]
print/x *argv[1]
$2 = 0x4 hex
$3 = 100 binary: 4 is 100
$6 = 45 decimal
$7 = 0x2d hex
The character '-' is just the number 45, or 0x2d. They're three views of the same byte.
4. Looking at raw memory: x
x/ + count + format + size, then an address. Formats: s string, c char, x hex, i instruction. Sizes: b byte, g 8 bytes.
x/3s argv[1]
0x7fffffffdae6: "-df"
0x7fffffffdaea: "-n"
0x7fffffffdaed: "hello"
This shows three strings in a row, packed back to back.
x/4cb argv[1]
x/4xb argv[1]
0x7fffffffdae6: 45 '-' 100 'd' 102 'f' 0 '\000'
0x7fffffffdae6: 0x2d 0x64 0x66 0x00
These are the same four bytes, as characters and as hex. The fourth is the '\0' that ends the string.
The argv array itself, the pointers rather than the strings:
x/5xg argv
0x7fffffffd708: 0x00007fffffffdac8 0x00007fffffffdae6
0x7fffffffd718: 0x00007fffffffdaea 0x00007fffffffdaed
0x7fffffffd728: 0x0000000000000000
This is the argv diagram in real memory. It's five 8-byte slots. Each holds the address of one word: ...dae6 is "-df", ...daea is "-n", ...daed is "hello". The fifth slot is 0, the NULL. The array lives near d708, and the strings near dae6.
The machine code you're about to run:
x/5i $pc
=> 0x55555555517c <main+19>: jmp 0x5555555551e9 <main+128>
0x55555555517e <main+21>: mov -0x20(%rbp),%rax
$pc is the program counter, the address of the next instruction. The => marks it. This is the jump to the loop test at the bottom, which is why break 13 hit only once last time.
5. Moving through the program
command what it does
next run one line, treating function calls as one step
step run one line, entering any function it calls
finish run until the current function returns
continue run until the next breakpoint
advance 17 run until line 17
Enter repeat the last command
step + bt + finish, stopped at line 16, printf(" %c", c):
step
bt
finish
__printf (format=0x55555555601b " %c") at ./stdio-common/printf.c:28
#0 __printf (format=0x55555555601b " %c") at ./stdio-common/printf.c:28
#1 0x00005555555551b7 in main (argc=1, argv=0x7fffffffd730) at c-c.c:16
main (argc=1, argv=0x7fffffffd730) at c-c.c:15
Value returned is $1 = 2
step went into the library's printf.
bt shows the two levels: printf (#0) was called from main line 16 (#1).
finish came back out, and GDB reported printf's return value: 2. printf returns how many characters it printed, here a space and f. Most people never notice that printf returns anything.
Leaving a loop early with advance:
delete
advance 17
main (argc=1, argv=0x7fffffffd720) at c-c.c:17
17 printf("\n");
This finishes the rest of the inner loop in one command. Delete the breakpoints first; otherwise advance stops at them on the way.
6. Breakpoints in more detail
A conditional breakpoint stops only when an expression is true:
break 16 if c == 'f'
break 20
info breakpoints
run
Num Type Disp Enb Address What
1 breakpoint keep y 0x...119e in main at c-c.c:16
stop only if c == 'f'
2 breakpoint keep y 0x...1210 in main at c-c.c:20
Breakpoint 1, main (argc=3, argv=0x7fffffffd710) at c-c.c:16
It skipped the d and stopped only at the f. In a loop that runs a thousand times, this is how you stop at the one pass you care about.
command effect
info breakpoints list them, with how often each has been hit
delete 2 / delete remove one / all
disable 1 / enable 1 switch one off and on without deleting it
tbreak 15 a breakpoint that deletes itself after one hit
7. Watchpoints: stop when a value changes
start
watch argc
continue
Hardware watchpoint 2: argc
Old value = 4
New value = 3
... at c-c.c:13
Each continue stops at the next change: 3→2, then 2→1. You don't say where to stop, only what to watch. At the end:
Watchpoint 2 deleted because the program has left the block in
which its expression is valid.
argc lives only inside main, and once main returns it no longer exists. "Hardware" means the CPU itself watches that memory address.
8. Changing the program while it runs: set var
Stopped at line 14, on the word "-df":
set var argv[0][1] = 'z'
print *argv
delete
continue
$1 = 0x7fffffffdae6 "-zf"
word: -zf letters: z f
word: -n letters: n
stopped at: hello (doesn't start with -)
That wrote one byte in memory, changing d (0x64) to z (0x7a), and the program printed -zf, which you never typed. You can test "what if" without editing or recompiling.
9. Automatic displays
display argc
display *argv
These print after every step until undisplay, as you used last time to watch *argv move one byte at a time.
10. Split-screen mode
gdb -tui --args ./c-c -df -n hello
This shows the source in a pane, with the current line highlighted as you step. Ctrl-x a switches the split screen on and off.
Quick reference
task command
begin start / run / run -nr
see code list
move next step finish continue advance N
stop points break N / break N if expr / tbreak / watch var
manage info breakpoints / delete / disable
look print (/x /t /d /c) / ptype / info locals / info args
memory x/s x/4cb x/4xb x/5xg x/5i $pc
calls bt / frame N / up / down
change set var x = ...
auto-show display / undisplay
quit quit
A good next session to try yourself: start, then x/5xg argv. Compare the addresses with x/3s argv[1], and you'll see the pointer array pointing at the packed strings.