C / GETTING STARTED
Compiler warnings and reading gcc error messages
Read gcc diagnostics precisely, tell warnings from errors, and use -Wall -Wextra to expose bugs the compiler would otherwise translate in silence.
What you will learn
- Compile with gcc -Wall -Wextra; the default warning set is deliberately near-silent.
- Split any diagnostic into location, severity, message and its bracketed -W flag.
- Fix the first error and recompile; the later ones are usually parser fallout.
- Treat warnings as bug reports: a built executable proves nothing about correctness.
Understanding Compiler warnings and reading gcc error messages
gcc issues two different verdicts. An error means the text cannot be translated at all: the grammar or the type rules were broken, so no object file and no executable appear. A warning means the text is legal C with exactly one defined meaning, but gcc's heuristics suspect that meaning is not the one you had in mind, so it translates the code anyway and hands you a working program. That is why if (answer = 42) builds and runs: assigning 42 and then testing the result is valid C, so the compiler is obliged to accept it and can only remark that it looks wrong.
gcc says almost nothing by default, because its baseline job is to translate conforming programs, including decades-old code where the suspicious patterns were intentional. The -Wall option enables the checks with a low false-positive rate, -Wextra adds a stricter set, and together they are still not every warning gcc knows. Each diagnostic has the same shape: file name, line, column, the word error, warning or note, the message text, and for warnings the controlling option in square brackets. That bracketed name is the stable part worth searching for, and it is the handle you use with -Wno-sign-compare to switch the check off or -Werror=sign-compare to make it fatal.
When errors appear, read from the top and fix only the first one. A reported location marks where the compiler could no longer make sense of the text, which is often just after the real mistake: a missing semicolon on one line is reported on the next, and a missing closing brace is reported at the end of the file. Because the parser loses its place, one such slip can generate twenty later errors that all disappear together, so recompiling after each fix is faster than reading the whole list. Lines that begin with note: are not additional problems; they are supporting locations for the message above them, such as where a function was declared.
Once a build is silent, keep it that way with -Werror, since a warning that scrolls past in a long build is a warning nobody reads.
<stdio.h>
int main(void)
{
int answer = 0;
/* Compiled with: gcc -Wall -Wextra -o warn warn.c
gcc warns here: suggest parentheses around assignment
used as truth value [-Wparentheses] */
if (answer = 42) {
printf("answer looks true, and it is now %d\n", answer);
} else {
printf("answer is still %d\n", answer);
}
return 0;
}
An error means gcc could not give your text any meaning, while a warning means it could but probably not the meaning you intended, so a clean build is not evidence of a correct program.
Worked examples
-Wall is not all warnings
One dead variable that -Wall reports and one dead parameter that only -Wextra reports, in a program that still prints the right answer.
<stdio.h>
static int twice(int n, int scale)
{
return n * 2;
}
int main(void)
{
int unused_total = 0;
printf("%d\n", twice(21, 3));
return 0;
}
Example explained
Line 1The declaration int unused_total = 0; produces: unused variable 'unused_total' [-Wunused-variable], which -Wall enables.
Line 2The parameter scale is never read, but that message, unused parameter 'scale' [-Wunused-parameter], appears only after you add -Wextra.
Line 3Both are warnings, so the executable is built and prints 42; the diagnostics describe work you forgot to wire up, not a broken result.
Line 4The fix is to delete the variable and the parameter; writing (void)scale; merely silences the message.
A warning that predicts a wrong answer
-Wsign-compare marks a comparison where a negative int is converted to a huge unsigned value before the test.
<stdio.h>
int main(void)
{
int n = -1;
unsigned int count = 3;
if (n < count) {
printf("as expected: -1 is smaller\n");
} else {
printf("surprise: -1 compared as %u\n", (unsigned int)n);
}
return 0;
}
Example explained
Line 1gcc -Wextra reports: comparison of integer expressions of different signedness: 'int' and 'unsigned int' [-Wsign-compare]; gcc -Wall alone stays quiet, because in C this check belongs to -Wextra.
Line 2The rule behind the message is the usual arithmetic conversions: n is converted to unsigned int, and where int is 32 bits -1 becomes 4294967295, so n < count is false.
Line 3Nothing here is undefined; the program is well defined and consistently wrong, which is precisely the class of bug only a warning will reveal.
Line 4Declaring count as int fixes it; casting n to unsigned would remove the warning and keep the bug.
Important notes
A message like undefined reference to 'foo' has no line, no column and no bracketed flag because it comes from the linker, not the compiler; that shape tells you the source parsed fine and something is missing at link time.
Wording, columns and even severity change between versions: gcc 14 and later reject implicit function declarations, implicit int and incompatible pointer assignments as errors, where older gcc only warned, so an old file can suddenly fail to build.
Common mistakes
Running plain gcc prog.c, seeing no output, and concluding the code is correct: the default set does not report if (x = 1), unused variables or sign mismatches, so those bugs reach the running program untouched.
Reading the error list from the bottom. The last messages are usually fallout from the first, so you end up editing correct code while the real cause, often a missing semicolon or brace, stays in place.
Dismissing a format warning such as expects argument of type 'int', but argument 2 has type 'double' as cosmetic: that mismatch is undefined behaviour, and printf reads the wrong bytes rather than just printing an odd number.
Try it yourself
Change, predict, then run
Type the assignment-in-condition program, compile it with gcc -Wall -Wextra, and write down the exact flag name printed in the brackets. Then change = to == and confirm the program prints answer is still 0 with no diagnostics at all.
Open the C workspaceCheck your understanding
gcc prints one warning for your file and still produces an executable that runs. What does that combination actually tell you about the code?
- The program has undefined behaviour and will crash sooner or later.
- The warning came from the linker, so the compiler itself found nothing wrong.
- The code is valid C with a defined meaning, and gcc suspects that meaning is not the one you intended.
- The warning would become an error at a higher optimisation level, so the build only succeeded by accident.
Show answer
gcc withholds output only when it cannot translate the text at all, so a warning plus an executable means translation succeeded and a heuristic flagged the result as probably unintended, as with an assignment used as a condition. Undefined behaviour is tempting because some warnings do report it, but many others, such as unused variables, sign mismatches and missing parentheses, describe fully defined code that simply does the wrong thing.