Why Does the C Preprocessor Interpret "linux" as "1"?
You're writing C code and you create a variable named linux, then your code won't compile. Or worse, it compiles but behaves strangely. What's going on?
TL;DR
The C preprocessor on Linux systems automatically defines the macro linux with the value 1. This is a legacy feature from older compilers that predefined system names as macros. Modern code should use standard macros like __linux__ instead. If you need to use linux as an identifier, you can undefine it with #undef linux or use compiler flags to prevent the predefinition.
This is one of those unexpected behaviors that can waste hours of debugging time if you don't know about it. The issue comes from how compilers historically handled platform detection.
Let's say you write this seemingly innocent code:
#include <stdio.h>
int main() {
int linux = 5;
printf("linux = %d\n", linux);
return 0;
}
When you compile it on a Linux system with gcc:
gcc test.c -o test
The preprocessor replaces linux with 1 before compilation, so your code effectively becomes:
#include <stdio.h>
int main() {
int 1 = 5; // Syntax error!
printf("1 = %d\n", 1);
return 0;
}
This produces a confusing error message like "expected identifier before numeric constant."
Why Does This Happen?
In the early days of Unix and C, compilers predefined macros for the operating system to help with platform-specific code. The idea was that you could write:
#ifdef unix
// Unix-specific code
#endif
#ifdef linux
// Linux-specific code
#endif
The compiler would define these symbols automatically, so you could check which platform you were compiling for without needing to pass flags.
This seemed convenient at the time, but it had a major problem: it polluted the global namespace with common words like unix and linux. Any variable, function, or struct member with these names would be replaced by the preprocessor.
The Modern Standard
C standards organizations recognized this was a bad idea. The C standard now specifies that implementation-defined macros should start with an underscore followed by a capital letter (like __linux__ or __unix__).
Modern compilers define proper macros:
#ifdef __linux__
// Linux-specific code
#endif
#ifdef __unix__
// Unix-specific code
#endif
#ifdef __APPLE__
// macOS-specific code
#endif
But for backward compatibility, gcc still defines the old linux and unix macros by default when compiling C code (not C++).
Seeing the Predefined Macros
You can ask gcc to show you all its predefined macros:
# Show all predefined macros
gcc -dM -E - < /dev/null
# Filter for linux-related macros
gcc -dM -E - < /dev/null | grep linux
You'll see output like:
#define __linux 1
#define __linux__ 1
#define linux 1
All three macros are defined with the value 1.
How to Work Around It
If you need to use linux as an identifier (variable name, function name, etc.), you have several options.
The simplest is to undefine the macro at the top of your file:
#undef linux
#include <stdio.h>
int main() {
int linux = 5; // Now this works
printf("linux = %d\n", linux);
return 0;
}
Put #undef linux before any includes to avoid issues with headers that might use the macro.
Alternatively, compile with the -std=c99 or -std=c11 flag, which disables these non-standard predefined macros:
gcc -std=c99 test.c -o test
Or use -ansi for strict ANSI C compliance:
gcc -ansi test.c -o test
Both approaches prevent gcc from defining linux as a macro.
Real-World Example: The Linux Kernel
Interestingly, even the Linux kernel itself has to deal with this. If you look at kernel headers, you'll find code like:
#undef unix
#undef linux
// ... kernel code
The kernel developers have to undefine these macros to avoid conflicts with their own code.
When This Causes Subtle Bugs
The real danger isn't syntax errors (those are obvious). It's when the code compiles but behaves unexpectedly.
Consider this code:
#include <stdio.h>
struct system_info {
char *name;
int linux; // Meant to be a boolean flag
};
int main() {
struct system_info info = { "Server01", 0 };
if (info.linux) {
printf("Running Linux\n");
} else {
printf("Not running Linux\n");
}
return 0;
}
After preprocessor expansion, the struct becomes:
struct system_info {
char *name;
int 1; // Syntax error
};
But if you had a different name collision that didn't cause a syntax error, you might get runtime bugs that are hard to track down.
Checking for Platform at Compile Time
If you're writing portable code and need to check the platform, use the modern macros:
#include <stdio.h>
int main() {
#ifdef __linux__
printf("Compiled on Linux\n");
#elif defined(__APPLE__)
printf("Compiled on macOS\n");
#elif defined(_WIN32)
printf("Compiled on Windows\n");
#else
printf("Unknown platform\n");
#endif
return 0;
}
These macros are standardized and won't interfere with your identifiers.
C++ Doesn't Have This Problem
If you compile C code as C++ (using g++ instead of gcc), the linux macro isn't defined:
# C compiler - defines linux
gcc test.c -o test
# C++ compiler - doesn't define linux
g++ test.c -o test
This is because C++ has stricter namespace rules, and the standards committee decided not to carry over this legacy behavior.
Finding the Problem in Your Code
If you're getting weird errors and suspect the linux macro might be involved, preprocess your code to see what the compiler actually sees:
# Preprocess only, output to stdout
gcc -E test.c
# Preprocess and save to a file
gcc -E test.c -o test.i
Look at the output to see if linux has been replaced with 1.
You can also add a check at the top of your file during debugging:
#ifdef linux
#warning "linux macro is defined!"
#endif
This will produce a warning during compilation if the macro is defined.
Other Problematic Predefined Macros
It's not just linux - there are other common words that get predefined:
// Other potentially problematic macros
#define unix 1
#define i386 1 // On x86 systems
#define arm 1 // On ARM systems
If you're writing portable code or library code, be aware of these. Use the standard __linux__, __unix__, __i386__, __arm__ variants instead.
Best Practices
To avoid problems with predefined macros:
- Use modern platform detection macros (
__linux__,__unix__, etc.) instead of legacy ones - If you must use common words as identifiers, undefine problematic macros at the top of your file
- Compile with strict standards flags (
-std=c99,-std=c11) to disable non-standard extensions - Check preprocessor output (
gcc -E) when debugging weird compilation errors - For library code, prefix your identifiers to avoid collisions (
mylib_linuxinstead oflinux)
The linux macro is a historical artifact that modern C programmers need to be aware of. While it made sense in the 1970s, today it's mostly a source of confusion. Understanding why it exists and how to work around it will save you debugging time and help you write more portable code.
Found an issue?