Skip to content

Module 1 — Debugging

Debugging is not what you do when learning fails. It is a skill with techniques, and this module adds the first one — the one you will use more than any other for the rest of your programming life.


The technique this module adds

Reading a compiler message, and knowing which stage produced it.

Most students read the first line of a compiler error, panic, and start changing code at random. The fix is a thirty-second routine that you should run every single time, before touching anything:

  1. Read the message properly, including the file and line it names.
  2. Decide which stage you are in. A message containing error: is the compiler reading your code. A message containing undefined reference is the linker. No message at all, and wrong behaviour, means the program ran.
  3. Fix the first error only, then rebuild. Later errors are usually echoes.
  4. If the named line looks correct, read the line above it.

The anatomy of a message, piece by piece:

main.cpp:6:13: error: expected ';' before 'return'
   6 |     return 0;
     |             ^
Part Meaning
main.cpp:6:13 File, line 6, column 13 — where the compiler stopped
error: A hard failure in the compilation stage
expected ';' before 'return' What it wanted, and what it found instead
the quoted line The source line, with the column marked
^ The exact position
note: (when present) Context for the error above it. Read it — from Module 8 onwards it is where the useful information lives

A worked diagnosis

A real one, from a student's first program. Follow the reasoning, not the fix.

Symptom.

$ g++ -std=c++17 -Wall -Wextra main.cpp -o main
main.cpp:4:1: error: expected initializer before 'int'

First hypothesis. "There is something wrong with int on line 4." (Reasonable — it is what the message says.)

Test. Look at line 4.

1  #include <iostream>
2
3  void greet(std::string name)
4  int main() {
5      greet("Nadia");

Result. Line 4 begins with int main(), which is valid C++. So the message is pointing at where the compiler got confused, not at what is wrong.

Actual cause. Line 3 has no semicolon. A function definition ends with a body in braces; a function declaration ends with ;. The compiler read void greet(...) as the start of a definition, kept looking for the {, and found int instead.

Fix.

3  void greet(std::string name);

Lesson. The compiler reports where it ran out of options, which is usually just after the mistake — never at the mistake. Read the line above.

The second question you should ask. Why did the compiler accept line 3 at all? Because void greet(std::string name) followed by int main() is parseable as something else for a moment. Compilers are reading machines, not mind readers: they will find a legal interpretation of your mistake before they complain.


The escalation ladder

Seven rungs, in order. Most bugs never reach the third.

  1. Read the first error, including any note: line beneath it.
  2. Check the line above the one named.
  3. Reduce the problem. Comment things out until it builds, then restore them one at a time. The smallest failing program is almost always trivial to fix.
  4. Look up the term in this module's mistakes table, then the glossary.
  5. Ask the machine — add output, add a counter. From Module 3 onwards, this becomes the most powerful rung on the ladder: a program that prints what it is doing is easier to debug than one that does not.
  6. Say the problem out loud, in words, without code. "My program calls a function that I never defined." Half of all bugs are found here, and the sentence usually names the fix.
  7. Only then look at a solution — and afterwards, rewrite your own version from scratch. Reading a fix produces recognition; retyping it produces knowledge.

Tools introduced here

Tool What it shows How to use it
The compiler itself Syntax, type and name errors, with positions g++ -std=c++17 -Wall -Wextra main.cpp -o main
-c Whether this file compiles, independently of the others g++ -std=c++17 -Wall -Wextra -c main.cpp
-Wall -Wextra Warnings the course treats as errors — uninitialised values, unused variables, suspicious comparisons Always on. -Werror in this repository's own examples
The exit status Whether the last command succeeded echo $? — 0 means success, anything else means failure
~ in a bash message Your home directory bash: ./main: No such file means the file is not where you think

One warning that deserves special mention. -Wunused-variable is a hint about your understanding, not noise. An unused variable usually means you intended to use it and forgot, or created it for a reason that no longer exists. Fix the cause; do not silence the warning by deleting the variable blindly.


The two habits worth keeping from this module

Predict before you run. Whenever you change something, write down what you expect before compiling. This converts every run into a test of your model — including the runs where nothing breaks, and those are the runs where nobody normally learns anything.

Break things on purpose. The four deliberate breakages in Lab 1 (a semicolon, an include, a typo'd identifier, a missing main) exist so that error messages stop being frightening. A student who has caused an error on purpose reads that error calmly the next time, which is most of the battle.


Next: Exercises — eight tasks across levels 1 to 4, each stating what success looks like.