Lesson 1 of 6
The compiler already knows about a large class of your bugs. It just says so quietly, in a stream of text most people scroll past.
Turning warnings on costs nothing and finds real defects before the program ever runs.
The flags
g++ -std=c++20 -Wall -Wextra -Wpedantic -Werror main.cpp -o main
| Flag | What it does |
|---|---|
| -Wall | the common warnings — badly named, it is nowhere near "all" |
| -Wextra | a second, stricter set |
| -Wpedantic | rejects non-standard extensions like the VLA from lesson 3.7 |
| -Werror | turns every warning into an error |
-Wall is the historical name; it enables a curated set, not everything. -Wall -Wextra together
is the practical baseline.
What they catch
Each of these compiles and runs. Each is a bug:
#include <iostream>
int classify(int n) {
if (n > 0) {
return 1;
}
// no return for n <= 0 — falls off the end
return 0;
}
int main() {
int a = 5;
int b = 10;
// Comparing signed with unsigned: b converts, and the comparison
// is not what it looks like when the values differ in sign.
unsigned int count = 3;
if (count > 2) {
std::cout << "count is " << count << "\n";
}
std::cout << classify(a) << classify(-a) << b << "\n";
return 0;
}
count is 3
1010
That version is clean. Remove the return 0; at the end of classify and -Wall reports:
warning: control reaches end of non-void function [-Wreturn-type]
Falling off the end of a non-void function is undefined behaviour (lesson 8.7). The compiler
knew, and told you.
Others you will meet constantly:
-Wunused-variable— a variable you declared and never used, often the leftover of a half-finished edit.-Wsign-compare— comparing signed and unsigned, the trap from lesson 3.3.-Wuninitialized— reading a variable before assigning it (lesson 7.2).-Wreorder— member initializers written out of declaration order (lesson 9.3).-Wpessimizing-move—return std::move(local)blocking copy elision (lesson 9.10).
Every one of those has appeared in this curriculum as a bug worth a section.
Why -Werror
A warning you can ignore is a warning you will ignore. The failure is gradual: one accepted warning becomes ten, ten become two hundred, and the one that matters is invisible in the noise.
-Werror prevents the accumulation by making it impossible. The build fails, so the warning gets
fixed while the code is still fresh in your mind.
The usual objection is that it blocks you on something trivial while you are mid-thought. Two answers:
- Suppress the specific case, not the category.
(void)unused;silences one unused variable without disabling the warning everywhere. - Use it in CI, not necessarily locally. Many projects build with
-Wall -Wextraday to day and add-Werrorin the automated build, so nothing lands with a new warning while local iteration stays fast.
What you should not do is turn the warning class off globally. That trades one visible problem for an invisible one.
Warnings are not errors, and that matters
A warning means "this compiles, and it is probably not what you meant." The compiler cannot refuse it, because the code is legal.
That is why Module 8's diagnostics were so uneven: use-after-free and double-delete produce no
warning at all, because the compiler cannot know at compile time which pointers are still live.
-Warray-bounds fires only with optimisation on, because it needs the optimiser's analysis.
So warnings are a first filter, not a safety net. The next three lessons cover the tools that catch what the compiler cannot.
Suppressing one case honestly
#include <iostream>
void handle(int used, int ignored_for_now) {
// Explicitly mark the parameter as deliberately unused, rather than
// switching off -Wunused-parameter for the whole file.
(void)ignored_for_now;
std::cout << "handled " << used << "\n";
}
int main() {
handle(1, 2);
return 0;
}
handled 1
Alternatively, omit the parameter's name entirely: void handle(int used, int). Both say "I know,
and I mean it" at the exact spot, which is what a reader needs.
A build script worth copying
# Development: fast, warnings visible
g++ -std=c++20 -Wall -Wextra -Wpedantic -g -O0 main.cpp -o main
# Testing: warnings fatal, sanitizers on
g++ -std=c++20 -Wall -Wextra -Wpedantic -Werror \
-fsanitize=address,undefined -g main.cpp -o main_test
# Release: optimised, no sanitizers
g++ -std=c++20 -Wall -Wextra -O2 -DNDEBUG main.cpp -o main_release
Note the release build keeps the warnings and drops the sanitizers, and that the testing build is the strict one. Module 18 puts this in a CMake file so you do not type it each time.
Try it
Write a short program containing four deliberate warnings: an unused variable, a signed/unsigned
comparison, a missing return, and an uninitialised read. Compile it plain, then with
-Wall -Wextra, then add -Werror. Fix each one properly rather than silencing it.
Checkpoint
-Wall -Wextra -Wpedanticis the practical baseline;-Wallalone is far from "all".- Warnings catch missing returns, sign mismatches, uninitialised reads, and more.
-Werrorstops warnings accumulating into noise that hides the important one.- Suppress a specific case with
(void)x;or an unnamed parameter — never a whole category. - Warnings mean "legal but probably wrong"; the compiler cannot refuse the code.
- Use-after-free and double-delete produce no warning at all — hence the next three lessons.
Questions about this lesson
Log in to ask a question.