File inclusion There are two directives in the C preprocessor for including contents of files: • #include, used for directly including the contents of a file in-place (typically containing code of some kind) • #embed, used for directly including or embedding the contents of a binary resource in-place
Code inclusion To include the content of one file into another, the preprocessor replaces a line that starts with #include with the content of the file specified after the directive. The inclusion may be logical in the sense that the resulting content may not be stored on disk and certainly is not overwritten to the source file. The file being included need not contain any sort of code, as this directive will copy the contents of whatever file is included in-place, but the most typical use of #include is to include a header file (or in some rarer cases, a source file). In the following example code, the preprocessor replaces the line #include with the content of the standard library header file named '' in which the
function printf() and other symbols are declared. • include int main(void) { printf("Hello, World!\n"); return 0; } In this case, the file name is enclosed in angle brackets to denote that it is a system file. For a file in the
codebase being
built, double-quotes are used instead. The preprocessor may use a different search algorithm to find the file based on this distinction. For C, a header file is usually named with a .h extension. In C++, the convention for file extension varies with common extensions .h and .hpp. But the preprocessor includes a file regardless of the extension. In fact, sometimes code includes .c or .cpp files. To prevent including the same file multiple times, which often leads to a compiler error, a header file typically contains an
guard or if supported by the preprocessor pragma once| to prevent multiple inclusion.
Binary resource inclusion C23 and
C++26 introduce the #embed directive for
binary resource inclusion, which allows including the content of a binary file into a source even if it is not valid C code. This allows binary resources (like images) to be included into a program without requiring processing by external tools like xxd -i and without the use of
string literals, which have a length limit on
MSVC. Similarly to xxd -i, the directive is replaced by a comma separated list of integers corresponding to the data of the specified resource. More precisely, if an array of type is initialized using an #embed directive, the result is the same as-if the resource was written to the array using
fread (unless a parameter changes the embed element width to something other than
CHAR_BIT). Apart from the convenience, #embed is also easier for compilers to handle, since they are allowed to skip expanding the directive to its full form due to the
as-if rule. The file to embed is specified the same as for #include either with
brackets or double quotes. The directive also allows certain parameters to be passed to it to customize its behavior. The C standard defines some parameters and implementations may define additional. The limit parameter is used to limit the width of the included data. It is mostly intended to be used with "infinite" files like
urandom. The prefix and suffix parameters allow for specifying a prefix and suffix to the embedded data. Finally, the if_empty parameter replaces the entire directive if the resource is empty. All standard parameters can be surrounded by double underscores, just like standard attributes on C23, for example __prefix__ is interchangeable with prefix . Implementation-defined parameters use a form similar to
attribute syntax (e.g., vendor::attr) but without the square brackets. While all standard parameters require an argument to be passed to them (e.g., limit requires a width), this is generally optional and even the set of parentheses can be omitted if an argument is not required, which might be the case for some implementation-defined parameters. const unsigned char iconDisplayData[] = { #embed "art.png" }; // specify any type which can be initialized form integer constant expressions will do const char resetBlob[] = { #embed "data.bin" }; // attributes work just as well alignas(8) const signed char alignedDataString[] = { #embed "attributes.xml" }; int main() { return • embed limit(1) ; }
Conditional compilation Conditional compilation is supported via the
if-else core directives #if, #else, #elif, and #endif and with contraction directives #ifdef and #ifndef, which stand for and , respectively. In the following example code, the printf() call is only included for compilation if VERBOSE is defined. • ifdef VERBOSE printf("trace message"); • endif The following demonstrates more complex logic: • if !(defined __LP64__ || defined __LLP64__) || defined _WIN32 && !defined _WIN64 // code for a 32-bit system • else // code for a 64-bit system • endif
Macro string replacement Object-like A macro specifies how to replace text in the source code with other text. An
object-like macro defines a token that the preprocessor replaces with other text. It does not include parameter syntax and therefore cannot support parameterization. The following macro definition associates the text "1 / 12" with the token "VALUE": • define VALUE 1 / 12
Function-like A
function-like macro supports parameters, although the parameter list can be empty. The following macro definition associates the expression "(A + B)" with the token "ADD" that has parameters "A" and "B". • define ADD(A, B) (A + B) A function-like macro declaration cannot have whitespace between the token and the first, opening parenthesis. If whitespace is present, the macro is interpreted as object-like with everything starting at the first parenthesis included in the replacement text.
Expansion The preprocessor replaces each token of the code that matches a macro token with the associated replacement text in what is known as
macro expansion. Note that text of string literals and comments is not parsed as tokens and is therefore ignored for macro expansion. For a function-like macro, the macro parameters are also replaced with the values specified in the macro reference. For example, ADD(VALUE, 2) expands to 1 / 12 + 2.
Variadic A variadic macro (introduced with
C99) accepts a varying number of arguments, which is particularly useful when wrapping functions that accept a variable number of parameters, such as
printf.
Order of expansion Function-like macro expansion occurs in the following stages: • Stringification operations are replaced with the textual representation of their argument's replacement list (without performing expansion). • Parameters are replaced with their replacement list (without performing expansion). • Concatenation operations are replaced with the concatenated result of the two operands (without expanding the resulting token). • Tokens originating from parameters are expanded. • The resulting tokens are expanded as normal. This may produce surprising results: • define HE HI • define LLO _THERE • define HELLO "HI THERE" • define CAT(a,b) a##b • define XCAT(a,b) CAT(a,b) • define CALL(fn) fn(HE,LLO) CAT(HE, LLO) // "HI THERE", because concatenation occurs before normal expansion XCAT(HE, LLO) // HI_THERE, because the tokens originating from parameters ("HE" and "LLO") are expanded first CALL(CAT) // "HI THERE", because this evaluates to CAT(a,b)
Undefine macro A macro definition can be removed from the preprocessor context via #undef such that subsequent reference to the macro token will not expand. For example: • define VALUE 15 // do stuff with VALUE... • undef VALUE // token 'VALUE' no longer expands from here...
Predefined macros The preprocessor provides some macro definitions automatically. The C standard specifies that __FILE__ expands to the name of the file being processed and __LINE__ expands to the number of the line that contains the directive. The following macro, DEBUGPRINT, formats and prints a message with the file name and line number. • define DEBUGPRINT(_fmt, ...) printf("[%s:%d]: " _fmt, __FILE__, __LINE__, __VA_ARGS__) For the example code below that is on line 30 of file util.c and for count 123, the output is: [util.c:30]: count=123. DEBUGPRINT("count=%d\n", count); The first
C Standard specified that __STDC__ expand to "1" if the implementation conforms to the ISO standard and "0" otherwise and that __STDC_VERSION__ expand to a numeric literal specifying the version of the standard supported by the implementation. Standard C++ compilers support the __cplusplus macro. Compilers running in non-standard mode must not set these macros or must define others to signal the differences. Other standard macros include __DATE__, the current date, and __TIME__, the current time. The second edition of the C Standard,
C99, added support for __func__, which contains the name of the function definition within which it is contained, but because the preprocessor is
agnostic to the grammar of C, this must be done in the compiler itself using a variable local to the function. One little-known usage pattern of the C preprocessor is known as
X-Macros. An X-Macro is a
header file. Commonly, these use the extension .def instead of the traditional .h . This file contains a list of similar macro calls, which can be referred to as "component macros." The include file is then referenced repeatedly. Many compilers define additional, non-standard macros. A common reference for these macros is the Pre-defined C/C++ Compiler Macros project, which lists "various pre-defined compiler macros that can be used to identify standards, compilers, operating systems, hardware architectures, and even basic run-time libraries at compile-time." Most compilers targeting
Microsoft Windows implicitly define _WIN32. This allows code, including preprocessor commands, to compile only when targeting Windows systems. A few compilers define WIN32 instead. For such compilers that do not implicitly define the _WIN32 macro, it can be specified on the compiler's command line, using -D_WIN32. • ifdef __unix__ // __unix__ is usually defined by compilers targeting Unix systems • include • elifdef _WIN32 // _WIN32 is usually defined by compilers targeting 32 or 64 bit Windows systems • include • endif • ifdef __linux__ • define CURRENT_PLATFORM "Linux" • elifdef __APPLE__ • define CURRENT_PLATFORM "Apple" • elifdef _WIN32 • define CURRENT_PLATFORM "Windows" • else • define CURRENT_PLATFORM "Other" • endif The example code tests if a macro __unix__ is defined. If it is, the file is then included. Otherwise, it tests if a macro _WIN32 is defined instead. If it is, the file is then included. Similarly, depending on which macro is defined, it defines CURRENT_PLATFORM to be a string with the name of the platform of the system.
Line control The values of the predefined macros __FILE__ and __LINE__ can be set for a subsequent line via the #line directive. In the code below, __LINE__ expands to 314 and __FILE__ to "pi.c". • line 314 "pi.c" printf("line=%d file=%s\n", __LINE__, __FILE__);
Operators The preprocessor is capable of interpreting operators and evaluating very basic expressions, such as integer constants, arithmetic operators, comparison operators, logical operators, bitwise operations, the defined operator, and the # stringificafion operator. This allows the preprocessor to make evaluations such as: // if X equals 10, the preprocessor sees #if 10 == 10 • if X == 10
Defined operator While the
defined operator, denoted by defined is not a directive in its own right, if it is read within a directive, it is interpreted by the preprocessor and determines whether a macro has been defined. The following are both accepted ways of invoking the defined operator. • if defined(MY_MACRO) • if defined MY_MACRO
Token stringification operator The
stringification operator (a.k.a. stringizing operator), denoted by # converts a token into a
string literal, escaping any quotes or backslashes as needed. For definition: • define STR(s) #s STR(\n) expands to "\n" and STR(p = "foo\n";) expands to "p = \"foo\\n\";". If stringification of the expansion of a macro argument is desired, two levels of macros must be used. For definition: • define XSTR(s) STR(s) • define STR(s) #s • define FOO 4 STR(FOO) expands to and XSTR(FOO) expands to . A macro argument cannot be combined with additional text and then stringified. However, a series of adjacent string literals and stringified arguments, also string literals, are concatenated by the C compiler.
Token concatenation The
token pasting operator, denoted by ##, concatenates two tokens into one. For example, this can be used to simplify boilerplate by generating
getter and setter methods, similar those of
Project Lombok (in
Java). • define GETTER(type, method, name) \ type get##method() const noexcept { \ return this->name; \ } • define SETTER(type, method, name) \ void set##method(type value) noexcept { \ this->name = value; \ } using std::string; class Student { private: string name; int age; double gpa; public: // constructors... GETTER(string, Name, name) SETTER(string, Name, name) GETTER(int, Age, age) SETTER(int, Age, age) GETTER(double, Gpa, gpa) SETTER(double, Gpa, gpa) // more methods... };
Abort Processing can be aborted via the #error directive. For example: • if RUBY_VERSION == 190 • error Ruby version 1.9.0 is not supported • endif
Warning As of
C23 and
C++23, a warning directive, #warning, to print a message without aborting is provided. Some typical uses are to warn about the use of
deprecated functionality. For example: Prior to C23 and C++23, this directive existed in many compilers as a non-standard feature, such as the C compilers by GNU, Intel, Microsoft and IBM. Because it was non-standard, the warning macro had varying forms: // GNU, Intel and IBM • warning "Do not use ABC, which is deprecated. Use XYZ instead." // Microsoft • pragma message("Do not use ABC, which is deprecated. Use XYZ instead.") ==Non-standard features ==