A structured lesson workspace with readable content, hands-on examples, and a clean path to completion.
#error halts compilation immediately and prints your message. It is the preprocessor's assertion mechanism. Use it to enforce build requirements: minimum compiler version, required platform, mandatory defines. If a header requires a specific feature macro and it is missing, #error tells the user exactly what is wrong and what to do, instead of letting the compiler produce a cascade of confusing errors twenty lines later.
#include <stdio.h>#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L#error "This code requires C99 or later. Compile with -std=c99 or -std=c11."#endif#ifndef REQUIRED_BUFFER_SIZE#error "REQUIRED_BUFFER_SIZE must be defined. Use -DREQUIRED_BUFFER_SIZE=1024."#endifint main(void) {printf("Buffer size: %d\n", REQUIRED_BUFFER_SIZE);return 0;}
The first compilation failed with a clear, actionable message. The user knows exactly what to define and how. The second compilation succeeded. The #error directive turned a confusing build failure into a one-line instruction. This is the difference between a library that is pleasant to use and one that generates Stack Overflow questions.
Enforcing architecture: #ifndef __x86_64__ / #error "x86-64 required". Enforcing compiler: #if defined(_MSC_VER) && _MSC_VER < 1900 / #error "MSVC 2015+ required". Enforcing configuration: #if !defined(USE_OPENSSL) && !defined(USE_MBEDTLS) / #error "Select a TLS backend". The pattern is always the same: check a precondition, fail with a message that tells the user how to fix it.
#error is your build system's voice. It speaks directly to the developer who is trying to compile your code. Make the message actionable. Tell them what is missing and how to provide it. A good #error message saves an hour of confusion. Next: the token-pasting operator ##, which concatenates tokens to create new identifiers.
Finish the lesson once you have worked through the material. This awards ★ 20 XP.