Debugging Techniques

Using debugging tools and techniques to identify and fix errors in your C programs.


Stepping Through Code in C

Debugging is an essential part of software development. Stepping through code allows you to execute your C program line by line, inspect variable values, and understand the flow of execution. This is crucial for identifying and fixing errors that might otherwise be difficult to pinpoint.

What is Stepping Through Code?

Stepping through code means executing your program one instruction at a time. This gives you granular control over the execution and enables you to:

  • Understand the Flow: Observe the exact sequence in which your code is executed.
  • Inspect Variables: Check the value of variables at different points in your program to ensure they are as expected.
  • Identify Bugs: Pinpoint the exact line of code where an error occurs or where the program deviates from the intended behavior.

Stepping Commands in a Debugger (GDB Example)

We'll use GDB (GNU Debugger) as an example, as it's a common debugger for C programs. Other debuggers will have similar commands, though the exact syntax might differ.

  1. Compile with Debugging Symbols: First, compile your C code with the -g flag. This adds debugging information to the executable, allowing the debugger to map the executable code back to the source code.
    gcc -g myprogram.c -o myprogram
  2. Start the Debugger: Launch GDB with your executable.
    gdb myprogram
  3. Set a Breakpoint: A breakpoint tells the debugger to pause execution at a specific line of code. This is where you start stepping.
    break main  # Set a breakpoint at the beginning of the main function
            break 15  # Set a breakpoint at line 15 of the source code 
  4. Run the Program: Start the program. It will run until it hits a breakpoint.
    run
  5. Use Stepping Commands: Once execution is paused at a breakpoint, use the following commands to step through your code:
    • step (or s): Executes the next line of code. If the next line is a function call, step will go inside that function and pause at the first line within the function. Think of it as "stepping into" a function.
    • next (or n): Executes the next line of code. If the next line is a function call, next will execute the entire function and pause at the line after the function call. Think of it as "stepping over" a function.
    • continue (or c): Resumes execution of the program until the next breakpoint is encountered. If there are no more breakpoints, the program will run to completion (or crash).
    • finish: Executes the remaining code in the current function and returns to the calling function, pausing at the line immediately after the function call. This is useful if you've stepped into a function and want to quickly get back to where you called it from.
  6. Inspect Variables: While stepping, you can use the print command to view the value of variables.
    print my_variable
            print *my_pointer  # If my_variable is a pointer, print the value it points to 

Example

 #include <stdio.h>

    int add(int a, int b) {
        int sum = a + b;
        return sum;
    }

    int main() {
        int x = 5;
        int y = 10;
        int result = add(x, y);
        printf("The sum is: %d\n", result);
        return 0;
    } 

Let's say you want to debug this code using GDB.

  1. Compile: gcc -g example.c -o example
  2. Start GDB: gdb example
  3. Set Breakpoint: break main
  4. Run: run
  5. You are now at the beginning of the main function. Use next (or n) to step over each line. When you get to the add(x, y) line, you can choose to:
    • Use step (or s) to step *into* the add function.
    • Use next (or n) to execute the entire add function and pause at the line printf("The sum is: %d\n", result);.
  6. If you stepped *into* the add function, you can use finish to execute the rest of the add function and return to the main function at the printf line.
  7. At any point, you can use print x, print y, print result, or any other variable to inspect their values.
  8. Use continue (or c) to let the program run until the next breakpoint (if any) or the end of the program.

Tips for Effective Debugging

  • Plan Your Debugging: Before you start stepping, think about where you suspect the problem might be. Set breakpoints accordingly.
  • Use Conditional Breakpoints: You can set breakpoints that only trigger when a specific condition is met (e.g., break 20 if x > 10).
  • Watch Variables: Some debuggers allow you to "watch" variables, automatically displaying their values as you step through the code. In GDB you can use 'display'.
  • Don't Be Afraid to Experiment: Try different stepping commands and breakpoint locations to gain a deeper understanding of your code's behavior.