CPP(1)

πŸ“¦
version 9.5.0 Β· 3 versions
, in ubuntu noble

πŸŽ€ cpp(1) β€” Part 1 of 3 πŸŽ€

okay bestie πŸ’– this is the GNU manual page for cpp, and it's giving main character energy ✨ more parts are coming, so no skipping ahead 😀


✨ NAME ✨

cpp β€” The C Preprocessor πŸ’…


πŸ“œ SYNOPSIS πŸ“œ

here's the lineup for how you call it 🧚

cpp [-Dmacro[=defn]...] [-Umacro]
    [-Idir...] [-iquotedir...]
    [-M|-MM] [-MG] [-MF filename]
    [-MP] [-MQ target...]
    [-MT target...]
    infile [[-o] outfile]

only the most useful options are up there, so scroll down for the full list of preprocessor-specific options πŸ‘€ cpp also takes most gcc driver options, but those aren't listed here, so check the GCC docs for the details πŸ“š


πŸ¦‹ DESCRIPTION πŸ¦‹

the C preprocessor, aka cpp 🌸, is a macro processor that the C compiler runs automatically to transform your program before compilation. it's called a macro processor because it lets you define macros, which are brief abbreviations for longer constructs ✨

the C preprocessor is only meant for C, C++, and Objective-C source code. people have abused it as a general text processor in the past πŸ’€ and it will choke on any input that doesn't follow C's lexical rules. apostrophes, for example, get read as the start of character constants, and that causes errors πŸ™ˆ you also can't count on it keeping the stuff about your input that doesn't matter to C-family languages. if you preprocess a Makefile, all the hard tabs get removed and the Makefile won't work 😭

having said that, you can sometimes get away with running cpp on things that aren't C. other Algol-ish languages are often safe (Ada, etc.), and assembly is okay too, but with caution 🫢 -traditional-cpp mode keeps more white space and is otherwise more permissive. a lot of the problems can be avoided by writing C or C++ style comments instead of native language comments, and by keeping macros simple πŸ’œ

whenever you can, use a preprocessor made for the language you're writing in ✨ modern versions of the GNU assembler have macro facilities, and most high level programming languages have their own conditional compilation and inclusion mechanism. if all else fails, try a true general text processor, like GNU M4 πŸ“

C preprocessors vary in some details. this manual covers the GNU C preprocessor, which provides a small superset of the features of ISO Standard C 🌈 in its default mode, the GNU C preprocessor doesn't do a few things the standard requires. those features are rarely, if ever, used, but they can cause surprising changes to the meaning of a program that doesn't expect them πŸ™ˆ to get strict ISO Standard C, use -std=c90, -std=c99, -std=c11 or -std=c17, depending on which version of the standard you want. to get all the mandatory diagnostics, you must also use -pedantic πŸ’…

this manual describes the behavior of the ISO preprocessor πŸŽ€ to minimize gratuitous differences, where the ISO preprocessor's behavior doesn't conflict with traditional semantics, the traditional preprocessor should behave the same way. the various differences that do exist are detailed in the section Traditional Mode πŸ“–

for clarity, unless noted otherwise, references to CPP in this manual refer to GNU CPP πŸ’–


πŸŽ€ OPTIONS πŸŽ€

the cpp command expects two file names as arguments, infile and outfile 🧚 the preprocessor reads infile, along with any other files it pulls in with #include. all the output from the combined input files gets written to outfile ✨

either infile or outfile may be -. as infile, - means read from standard input, and as outfile, it means write to standard output. if either file is omitted, it's the same as if - had been specified for that file 🫢 you can also use the -o outfile option to specify the output file πŸ’…

unless otherwise noted, or the option ends in =, all options that take an argument can have that argument right after the option, or with a space between the option and argument. -Ifoo and -I foo have the same effect 🌸

lots of options have multi-letter names, so multiple single-letter options can't be grouped together πŸ™… -dM is very different from -d -M πŸ’€

  • -D name β€” predefine name as a macro, with definition 1 πŸŽ€
  • -D name=definition β€” the contents of definition get tokenized and processed as if they showed up during translation phase three in a #define directive. the definition gets truncated at any embedded newline characters βœ‚οΈ
    • if you're invoking the preprocessor from a shell (or something shell-like), you may need the shell's quoting syntax to protect characters like spaces that mean something in shell syntax 🐚
    • want a function-like macro on the command line? write its argument list in parentheses before the equals sign (if there is one). parentheses mean stuff to most shells, so quote the option. with sh and csh, -D'name(args...)=definition' works 🧁
    • -D and -U options are processed in the order they're given on the command line. all -imacros file and -include file options get processed after all -D and -U options πŸ”₯
  • -U name β€” cancel any previous definition of name, whether it was built in or provided with a -D option 🚫
  • -include file β€” process file as if "#include "file"" appeared as the first line of the primary source file 🧚 the first directory searched for file is the preprocessor's working directory, instead of the directory containing the main source file. if it's not found there, it gets searched for in the remainder of the "#include "..."" search chain as normal πŸ”
    • if multiple -include options are given, the files are included in the order they appear on the command line πŸ“š
  • -imacros file β€” exactly like -include, except any output produced by scanning file is thrown away πŸ—‘οΈ the macros it defines remain defined. this lets you grab all the macros from a header without also processing its declarations πŸ’œ
    • all files specified by -imacros are processed before all files specified by -include πŸ‘‘
  • -undef β€” don't predefine any system-specific or GCC-specific macros. the standard predefined macros remain defined 🌈
  • -pthread β€” defines additional macros required for using the POSIX threads library 🧡 use this option consistently for both compilation and linking ⭐ it's supported on GNU/Linux targets, most other Unix derivatives, and also on x86 Cygwin and MinGW targets πŸ¦‹
  • -fpreprocessed β€” okay so this one tells the preprocessor "hey, this file is already preprocessed, don't redo the work" πŸ’… It skips stuff like macro expansion, trigraph conversion, escaped newline splicing, and processing of most directives. It still recognizes and removes comments, so you can pass a file preprocessed with -C to the compiler with zero drama 🫢 In this mode the integrated preprocessor is basically just a tokenizer for the front ends 🧚

-fpreprocessed is implicit if the input file has one of the extensions .i, .ii or .mi. Those are the extensions GCC uses for preprocessed files made by -save-temps 🌸

  • -fdirectives-only β€” when preprocessing, it handles directives but does NOT expand macros πŸ¦‹ How it behaves depends on whether you also use -E or -fpreprocessed:
    • With -E, preprocessing is limited to directives like #define, #ifdef, and #error. Other stuff like macro expansion and trigraph conversion doesn't happen. The -dD option also gets switched on automatically ✨
    • With -fpreprocessed, predefinition of command line and most builtin macros is turned off. Contextual macros like __LINE__ still get handled normally. This lets you compile files that were previously preprocessed with -E -fdirectives-only πŸ’–
    • With both -E and -fpreprocessed, the -fpreprocessed rules win. This gives you full preprocessing of files that were previously preprocessed with -E -fdirectives-only πŸ‘‘
  • -fdollars-in-identifiers β€” accepts $ in identifiers πŸ’œ
  • -fextended-identifiers β€” accepts universal character names in identifiers 🌈 This one is on by default for C99 (and later C standard versions) and C++, so you're probably already getting it.
  • -fno-canonical-system-headers β€” when preprocessing, system header paths do NOT get shortened with canonicalization πŸ™ˆ
  • -ftabstop=width β€” sets the distance between tab stops πŸ“ This helps the preprocessor report correct column numbers in warnings or errors, even when tabs are sitting on the line. If the value is less than 1 or greater than 100, the option gets ignored. The default is 8.
  • -ftrack-macro-expansion[=level] β€” tracks where tokens are located across macro expansions πŸ”₯ This lets the compiler show you the current macro expansion stack when a compile error happens inside a macro expansion. Heads up though, using it makes the preprocessor and the compiler eat more memory πŸ’€ The level parameter lets you pick how precisely tokens get tracked, so you can cut memory use if you need to:
    • Value 0 of level turns this option off.
    • Value 1 tracks token locations in a degraded mode to keep memory overhead minimal. In this mode, every token that comes from expanding an argument of a function-like macro has the same location.
    • Value 2 tracks token locations completely. This is the most memory hungry value 🐘
    • When this option is given with no argument, the default parameter value is 2.

Note that -ftrack-macro-expansion=2 is activated by default ✨

  • -fmacro-prefix-map=old=new β€” when preprocessing files living in directory old, the __FILE__ and __BASE_FILE__ macros expand as if those files lived in directory new instead πŸ—ΊοΈ You can use this to turn an absolute path into a relative one by using . for new, which can lead to more reproducible, location independent builds 🫢 This option also affects __builtin_FILE() during compilation. See also -ffile-prefix-map.
  • -fexec-charset=charset β€” sets the execution character set, which gets used for string and character constants 🌸 The default is UTF-8. charset can be any encoding your system's iconv library routine supports.
  • -fwide-exec-charset=charset β€” sets the wide execution character set, used for wide string and character constants πŸ’… The default is UTF-32 or UTF-16, whichever matches the width of wchar_t. Same as -fexec-charset, charset can be any encoding your system's iconv library routine supports. However, you'll run into problems with encodings that don't fit exactly in wchar_t 😭
  • -finput-charset=charset β€” sets the input character set, which is used to translate from the input file's character set to the source character set GCC uses πŸ¦‹ If the locale doesn't specify it, or GCC can't get the info from the locale, the default is UTF-8. This can be overridden by either the locale or this command-line option. Right now the command-line option wins if there's a conflict. charset can be any encoding your system's iconv library routine supports.
  • -fworking-directory β€” turns on linemarkers in the preprocessor output that tell the compiler the current working directory at preprocessing time 🧭 When this is enabled, the preprocessor emits a second linemarker after the initial one, containing the current working directory followed by two slashes. When that directory shows up in the preprocessed input, GCC uses it as the working directory in some debugging information formats. This option is implicitly enabled if debugging information is enabled, but you can block that with the negated form -fno-working-directory. If the -P flag is in your command line, this option does nothing, since no #line directives get emitted whatsoever πŸ™…β€β™€οΈ
  • -A predicate=answer β€” makes an assertion with the predicate predicate and the answer answer πŸŽ€ This form is preferred over the older -A predicate(answer) form. That one is still supported, but this one is better because it doesn't use shell special characters.
  • -A -predicate=answer β€” cancels an assertion with the predicate predicate and the answer answer 🚫
  • -C β€” do NOT discard comments 🧚 All comments get passed through to the output file, except for comments inside processed directives, which get deleted along with the directive.
Be prepared for side effects when you use -C! It makes the preprocessor treat comments as tokens in their own right. For example, a comment at the start of what would be a directive line turns that line into an ordinary source line, since the first token on the line is no longer a # 😬
  • -CC β€” do NOT discard comments, including during macro expansion πŸ’œ This is like -C, except comments inside macros also get passed through to the output file wherever the macro is expanded.

In addition to the side effects of -C, -CC converts all C++-style comments inside a macro into C-style comments. This stops a later use of that macro from accidentally commenting out the rest of the source line πŸ™Œ

-CC is generally used to support lint comments.

  • -P β€” stops the preprocessor from generating linemarkers in its output 🌈 This might be useful when you're running the preprocessor on something that isn't C code and will be sent to a program that could get confused by the linemarkers.
  • -traditional, -traditional-cpp β€” tries to imitate the behavior of pre-standard C preprocessors, as opposed to ISO C preprocessors πŸ•°οΈ
GCC doesn't otherwise try to emulate a pre-standard C compiler, and these options only work with the -E switch or when you invoke CPP explicitly. Don't expect them to do anything else!
  • -trigraphs β€” supports ISO C trigraphs πŸ”Ί These are three-character sequences that all start with ??, and ISO C defines them to stand for single characters. For example, ??/ stands for \, so '??/n' is a character constant for a newline ✨

By default GCC ignores trigraphs, but in standard-conforming modes it converts them. Check out the -std and -ansi options for those modes.

  • -remap β€” turns on special code to work around file systems that only allow super short file names, like MS-DOS πŸ’Ύ
  • -H β€” prints the name of each header file used, along with all the other normal stuff πŸ“œ Each name gets indented to show how deep it sits in the #include stack. Precompiled header files get printed too, even if they turn out to be invalid. An invalid precompiled header file is printed with ...x, and a valid one with ...! .
  • -dletters β€” makes debugging dumps during compilation, as specified by letters πŸ” The flags documented here are the ones relevant to the preprocessor. Other letters are either interpreted by the compiler proper, or reserved for future GCC versions, so they're silently ignored. If you specify letters whose behavior conflicts, the result is undefined πŸ’€
    • -dM β€” instead of the normal output, generates a list of #define directives for all the macros defined during the execution of the preprocessor, including predefined macros. This is a great way to find out what's predefined in your version of the preprocessor πŸ‘€
    • -dD β€” like -dM, except it does NOT include the predefined macros, and it outputs both the #define directives and the result of preprocessing. Both kinds of output go to the standard output file πŸ“€
    • -dN β€” like -dD, but only emits the macro names, not their expansions 🫧
    • -dI β€” outputs #include directives in addition to the result of preprocessing πŸ“¦
    • -dU β€” like -dD, except only macros that get expanded, or whose definedness gets tested in preprocessor directives, are output. The output is delayed until the macro is used or tested, and #undef directives are also output for macros that were tested but were undefined at the time 🧠

For -dM, assuming you have no file foo.h, the command below shows all the predefined macros ✨

touch foo.h; cpp -dM foo.h
  • -fdebug-cpp β€” this one is only useful for debugging GCC πŸ› When used from CPP or with -E, it dumps debugging information about location maps. Every token in the output gets preceded by the dump of the map its location belongs to. When used from GCC without -E, this option has no effect.
  • -I dir, -iquote dir, -isystem dir, -idirafter dir β€” adds the directory dir to the list of directories searched for header files during preprocessing πŸ“‚

If dir begins with = or $SYSROOT, then the = or $SYSROOT gets replaced by the sysroot prefix. Check out --sysroot and -isysroot for more on that πŸ‘‘

Okay so, directories you pass with -iquote only matter for the quote form, #include "file". Directories from -I, -isystem, or -idirafter count for both #include "file" and #include <file> πŸ’…

You can stack as many of these options as you want to search several directories for headers. The lookup order goes like this πŸ—ΊοΈ

  1. For the quote form, the directory of the current file gets checked first.
  2. For the quote form, -iquote directories get searched left-to-right, in the order they show up on the command line.
  3. -I directories get scanned left-to-right.
  4. -isystem directories get scanned left-to-right.
  5. Standard system directories get scanned.
  6. -idirafter directories get scanned left-to-right.

Using -I to override a system header is iconic, since those directories get searched before the standard system header directories. Swap in your own version and slay πŸ¦‹ But bestie, don't use -I to add directories with vendor-supplied system headers. Use -isystem for that.

-I is not the move for vendor system headers. Reach for -isystem instead.

-isystem and -idirafter also mark the directory as a system directory, so it gets the same special treatment as the standard system directories ✨

If a standard system include directory, or a directory given with -isystem, also shows up with -I, the -I gets ignored. The directory still gets searched, but as a system directory at its normal spot in the system include chain. This keeps GCC's procedure for fixing buggy system headers and the ordering for #include_next from getting changed by accident πŸ™ˆ If you truly need to change the search order for system directories, use -nostdinc and/or -isystem.

  • -I- β€” Split the include path. This one's deprecated, so please don't use it 😭 Switch to -iquote for the -I directories that come before -I-, and remove the -I- option.

Directories given with -I before -I- only get searched for #include "file". They do not get searched for #include <file>. Any -I directories placed after -I- get searched for all #include directives. On top of that, -I- stops the directory of the current file from being the first search directory for #include "file", and there's no way to undo that effect πŸ’…

  • -iprefix prefix β€” Sets prefix as the prefix for later -iwithprefix options. If the prefix is a directory, include the final /.
  • -iwithprefix dir, -iwithprefixbefore dir β€” Tacks dir onto the prefix you set earlier with -iprefix, then adds the result to the include search path. -iwithprefixbefore puts it where -I would go, and -iwithprefix puts it where -idirafter would go 🌸
  • -isysroot dir β€” This one is like --sysroot, but it only applies to header files. Darwin targets are the exception, where it applies to both header files and libraries. Check out --sysroot for more info.
  • -imultilib dir β€” Uses dir as a subdirectory of the directory that holds target-specific C++ headers 🧚
  • -nostdinc β€” Skips the standard system directories when searching for headers. Only the directories given with -I, -iquote, -isystem, and/or -idirafter get searched, plus the directory of the current file if it applies.
  • -nostdinc++ β€” Skips the C++-specific standard directories, but still searches the other standard directories. (This is used when building the C++ library, so it's a builder's flex πŸ› οΈ)
  • -Wcomment, -Wcomments β€” Warns whenever a comment-start sequence /* shows up inside a /* */ comment, or whenever a backslash-newline shows up in a // comment. This warning is enabled by -Wall.
  • -Wtrigraphs β€” Warns if any trigraphs get encountered that might change the meaning of the program. Trigraphs inside comments don't get warned about, except the ones that would form escaped newlines.

This option is implied by -Wall. If -Wall isn't given, this option is still enabled unless trigraphs are enabled. To get trigraph conversion without the warnings, but still get the other -Wall warnings, use -trigraphs -Wall -Wno-trigraphs ✨

  • -Wundef β€” Warns if an undefined identifier gets evaluated in an #if directive. Those identifiers get replaced with zero.
  • -Wexpansion-to-defined β€” Warns whenever defined shows up in the expansion of a macro, including when the macro gets expanded by an #if directive. This usage isn't portable, so it's a no-no πŸ™… This warning is also enabled by -Wpedantic and -Wextra.
  • -Wunused-macros β€” Warns about macros defined in the main file that are unused. A macro counts as used if it gets expanded or tested for existence at least once. The preprocessor also warns if the macro hasn't been used by the time it gets redefined or undefined.

Built-in macros, macros defined on the command line, and macros defined in include files don't get warned about πŸ’œ

Note: If a macro is actually used, but only inside skipped conditional blocks, then the preprocessor reports it as unused. To dodge the warning in that case, you might improve the scope of the macro's definition by, for example, moving it into the first skipped block. Or you could give it a dummy use with something like:
#if defined the_macro_causing_the_warning
#endif
  • -Wno-endif-labels β€” Stops the warning for when an #else or an #endif is followed by text. This happens sometimes in older programs with code shaped like this:
#if FOO
...
#else FOO
...
#endif FOO

The second and third FOO should be in comments. This warning is on by default 🫢


✨ ENVIRONMENT ✨

This section is all about the environment variables that change how CPP operates. You can use them to specify directories or prefixes to search for include files, or to control dependency output 🌈

You can also specify places to search with options like -I, and control dependency output with options like -M. These take precedence over environment variables, which in turn take precedence over the configuration of GCC. So the command line wins, then the env vars, then GCC's config πŸ‘‘

CPATH, C_INCLUDE_PATH, CPLUS_INCLUDE_PATH, OBJC_INCLUDE_PATH

Each of these variables holds a list of directories separated by a special character, a lot like PATH, and that's where the headers get looked up. The special character, PATH_SEPARATOR, depends on the target and gets set when GCC is built. For Microsoft Windows-based targets it's a semicolon, and for almost every other target it's a colon.

CPATH specifies a list of directories to search as if they were given with -I, but after any paths given with -I options on the command line. This variable gets used no matter which language is being preprocessed 🌸

The remaining variables only apply when preprocessing the specific language they're named for. Each one specifies a list of directories to search as if they were given with -isystem, but after any paths given with -isystem options on the command line.

In all of these variables, an empty element tells the compiler to search its current working directory. Empty elements can show up at the beginning or end of a path. For example, if the value of CPATH is ":/special/include", that has the same effect as -I. -I/special/include 🧚

DEPENDENCIES_OUTPUT

If this variable is set, its value tells GCC how to output dependencies for Make, based on the non-system header files the compiler processes. System header files get ignored in the dependency output πŸ’…

The value of DEPENDENCIES_OUTPUT can be just a file name. In that case the Make rules get written to that file, and the target name gets guessed from the source file name. Or the value can take the form file target, where the rules go to file and use target as the target name.

In other words, this environment variable is equivalent to combining the options -MM and -MF, with an optional -MT switch too ✨

SUNPRO_DEPENDENCIES

This variable is the same as DEPENDENCIES_OUTPUT (see above), with one twist: system header files don't get ignored. So it implies -M rather than -MM. However, the dependence on the main input file is left out πŸ™ˆ

SOURCE_DATE_EPOCH

If this variable is set, its value specifies a UNIX timestamp to use in place of the current date and time in the __DATE__ and __TIME__ macros. That way the embedded timestamps become reproducible πŸ•°οΈ

The value of SOURCE_DATE_EPOCH must be a UNIX timestamp, which is the number of seconds (excluding leap seconds) since 01 Jan 1970 00:00:00, written in ASCII. It's identical to the output of @command{date +%s} on GNU/Linux and other systems that support the %s extension in the date command.

The value should be a known timestamp, like the last modification time of the source or package, and the build process should set it 🫢


✨ SEE ALSO ✨

gpl(7), gfdl(7), fsf-funding(7), gcc(1), and the Info entries for cpp and gcc πŸ“š

Copyright (c) 1987-2019 Free Software Foundation, Inc. πŸ’–

Permission is granted to copy, distribute, and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation. A copy of the license is included in the man page gfdl(7). This manual has no Invariant Sections. The Front-Cover Texts are (a) (see below), and the Back-Cover Texts are (b) (see below).

(a) The FSF's Front-Cover Text is:

A GNU Manual

(b) The FSF's Back-Cover Text is:

You have freedom to copy and modify this GNU Manual, like GNU software. Copies published by the Free Software Foundation raise funds for GNU development.

That's a wrap on CPP(1), bestie πŸŽ€ Pulled from gcc-9 and dated 2022-05-27, so now you're basically a preprocessor pro πŸ‘‘πŸ’…

πŸ“¦ where this page comes from