Preprocessor
Overview
The C preprocessor parses C source files (.h/ .c) for preprocessing directives and tokens to perform textual substitution on. The resulting text is stored in an ASCII intermediate file (.i).
Preprocessing directives are specified by placing a # character as the first non-whitespace character on a line.
Conditional Inclusion
The #if and #elif directives (closed with #endif) can be used to include source code depending on whether or not the corresponding controlling expression evaluates truthily.
In a preprocessor conditional, unknown identifiers evaluate to 0. To check whether or not an identifier has been defined, use defined. There also exist a number of equivalent formulations:
#ifdefis equivalent to#if defined(...)#ifndefis equivalent to#if !defined(...)#elifdefis equivalent to#elif defined(...)#elifndefis equivalent to#elif !defined(...)
Source File Inclusion
The #include directive searches in an inplementation-defined sequence of locations for a header uniquely identified by the string found between either angle brackets (<...>) or quotes ("...").
Starting in C23, the __has_include operator can be used to check if a header is available for inclusion.
Binary File Inclusion
Beginning in C23, the #embed directive searches in an inplementation-defined sequence of locations for a resource uniquely identified by the string found between either angle brackets (<...>) or quotes ("...").
The directive is replaced with a comma-delimited list of integer constant expressions. As such, it is often used in the following way:
static char const example[] = {
#embed "resource.ext"
}
The __has_embed operator can be used to check if a resource is available for inclusion. In addition, there exist a few standard embed parameters that can control how embedding works:
limit(...)specifies a maximum to the number of elements to include from the resource.if_empty(...)places a token in place of the embedded result if the resource is empty.prefix(...)places a token before the embedded result if the resource is non-empty.suffix(...)places a token after the embedded result if the resource is non-empty.
Pragmas
A pragma directive refers, in most cases, to implementation-specific directives. The only exception is when the STDC token immediately follows the #pragma token.
Macros
Macros refer to #define directives that specify terms textually replaced by the preprocessor during compilation. For types that don't have literals that describe their constants, compound literals can be used on the replacement side of the macro:
#define NAME (T){ INIT }
Some commonly used macros recognized by the preprocessor include:
__LINE__. Expands to a decimal integer constant for the number of the line in source.__DATE__. Expands to a string literal containing the date of compilation.__TIME__. Expands to a string literal containing the time of compilation.__FILE__. Expands to a string literal containing the name of the current TU.__func__. Expands to a local static variable holding the name of the enclosing function.
The preprocessor temporarily disables the definition of macros on expansion to avoid infinite recursion. To avoid any ambiguity in the resulting expression, parameter references should be surrounded by parentheses in the replacement text.
Implementation Flags
A few macros are defined by the implementation to indicate some subset of the language is unavailable:
__STDC_NO_ATOMICS__: If defined as1, indicates the implementation does not support atomic types nor the<stdatomic.h>header.__STDC_NO_COMPLEX__: If defined as1, indicates the implementation does not support complex types or the<complex.h>header.__STDC_NO_THREADS__: If defined as1, indicates the implementation does not support the<threads.h>header.__STDC_NO_VLA__: If defined as1, indicates the implementation does not support VLAs or VMTs.
Function-Like Macros
If the name of a macro is immediately followed by a left parenthesis, the macro is said to be function-like or functional.
Argument Checking
Arguments found in the replacement side of a functional macro should be protected by specifying with surrounding parentheses. Additionally, types of arguments can be enforced with a variety of different tricks:
- If parameter
sis expected to be a string literal, write"" s ""in the replacement text. - Integer types can be promoted using e.g.
+0.0F,+0UL, etc. - Assignment compatibility can be enforced by wrapping arguments in compound literal initializers, e.g.
(void*){ (X) }.
Token Operators
The # operator is used to stringify an argument. For example, calling foo(bar) in the following yields "bar".
#define foo(T) #T
The ## operator is used to concatenate tokens. For example, calling foo(bar) in the following yields bar_init.
#define foo(T) T ## _init
Variadic Macros
The final parameter of a function-like macro may be .... This parameter corresponds to a sequence of arguments accessible inside macro expansion __VA_ARGS__.
Starting in C23, the macro __VA_OPT__ is introduced. This macro takes in a preprocessing token sequence. If __VA_ARGS__ is not empty, the token sequence is included. For example,
#define F(X, ...) fprintf("" X "" __VA_OPT__(,) __VA_ARGS__)