CATEGORY: Chain of blunders (where one blunder leads to another). Knuth: Blunder (Thinking the right thing but doing it wrong) REFERENCE: https://www.tug.org/TUGboat/tb10-4/tb26knut.pdf EXPLANATION: The forward declaration of PRIME_TABLE in prime_table.h lacks an 'extern' specifier: // prime_table.h const unsigned int PRIME_TABLE[10]; Since the unlucky author of the Makefile also forgot to link the prime_table.o object file to the executable: # Makefile prime_sum : prime_sum.o the real, intended array definition with the desired prime numbers is missing. According to the C language standard [1], a declaration that is not followed by a definition becomes the de facto definition. CONSEQUENCES: Since the de facto definition of PRIME_TABLE is not explicitily initialized, it's zero-initialized by the compiler. As a result, the program will sum-up 10 zeros and hence output zero as the result. BUGFIX: Declaring PRIME_TABLE 'extern' in prime_table.h: extern const unsigned int PRIME_TABLE[10]; makes the blunder in the Makefile immediately obvious: gcc -Wextra -Wall -pedantic -ansi-c -o prime_sum.o prime_sum.c gcc prime_sum.o prime_table.h -o prime_sum prime_sum.o: In function `main': prime_sum.c:(.text+0x25): undefined reference to `PRIME_TABLE' Fixing the Makefile is trivial: # Now contains prime_table.o as well: prime_sum : prime_sum.o prime_table.o REMARKS: * This bug could not have happened in C++ as definitions of constants without explicit initialization values are considered illegal. * clang supports the -Wmissing-variable-declarations warning that is able to catch this bug: prime_table.h:4:20: warning: no previous extern declaration for non-static variable 'PRIME_TABLE' Unfortunately, this warning is disabled by default and neither part of -Wall nor -Wextra. NOTES: [1] Refer to ISO C99 standard, 6.9.2 External object definitions: A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.