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:
- Read the message properly, including the file and line it names.
- Decide which stage you are in. A message containing
error:is the compiler reading your code. A message containingundefined referenceis the linker. No message at all, and wrong behaviour, means the program ran. - Fix the first error only, then rebuild. Later errors are usually echoes.
- If the named line looks correct, read the line above it.
The anatomy of a message, piece by piece:
| 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.
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.
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.
- Read the first error, including any
note:line beneath it. - Check the line above the one named.
- 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.
- Look up the term in this module's mistakes table, then the glossary.
- 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.
- 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.
- 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.