CPP(1)

πŸ“¦
version 11.5.0 Β· 6 versions
, in ubuntu questing, ubuntu resolute, ubuntu stonking

πŸŽ€ cpp(1) πŸŽ€

Section 1 · GNU 🌈

✨ NAME ✨

Okay so the whole name is just cpp, which is The C Preprocessor πŸ’…

πŸ“œ SYNOPSIS πŸ“œ

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

Only the most useful options made it into the synopsis, 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 go read the GCC documentation for the full scoop β˜•.

πŸ’– DESCRIPTION πŸ’–

The C preprocessor, often known as cpp, is a macro processor that the C compiler uses 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 meant to be used only with C, C++, and Objective-C source code πŸ’…. In the past it got abused as a general text processor πŸ’€, and it will choke on input that doesn't follow C's lexical rules. For example, apostrophes get read as the start of character constants and cause errors πŸ™ˆ. You also can't count on it keeping characteristics of the input that don't matter to C-family languages. If a Makefile gets preprocessed, all the hard tabs get removed and the Makefile won't work 😭.

Having said that, you can lowkey get away with running cpp on things that aren't C 🌸. Other Algol-ish programming languages are often safe (Ada, etc.). Assembly is fine too, with caution. -traditional-cpp mode preserves more white space and is otherwise more permissive. Most of the problems can be avoided by writing C or C++ style comments instead of native language comments, and by keeping macros simple 🫢.

Wherever possible, use a preprocessor geared to the language you're writing in, periodt πŸ’…. Modern versions of the GNU assembler have macro facilities. Most high-level programming languages have their own conditional compilation and inclusion mechanism. If all else fails, try a true general text processor, such as GNU M4 🧚.

C preprocessors vary a bit in the details 🌈. This manual is about the GNU C preprocessor, which gives you a small superset of the features of ISO Standard C. In its default mode, the GNU C preprocessor skips a few things the standard requires.

Heads up, bestie: these skipped 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, wherever the ISO preprocessor's behavior doesn't conflict with traditional semantics, the traditional preprocessor should behave the same way πŸ¦‹. The differences that do exist are detailed in the section Traditional Mode.

For clarity, unless noted otherwise, any references to CPP in this manual mean GNU CPP πŸ’œ.

πŸŽ€ OPTIONS πŸŽ€

The cpp command expects two file names as arguments, infile and outfile πŸ“. The preprocessor reads infile together with any other files it pulls in with #include, and all the output from the combined input files gets written into outfile ✨.

Either infile or outfile can be -. As infile, that means read from standard input, and as outfile, it means write to standard output 🌸. If either file is left out, it's the same as if - had been given for that file. You can also use the -o outfile option to specify the output file πŸŽ€.

Unless otherwise noted, or the option ends in =, every option that takes an argument can have that argument right after the option, or with a space between the two. So -Ifoo and -I foo have the same effect πŸ¦‹.

Lots of options have multi-letter names, so you can't group multiple single-letter options together. -dM is very different from -d -M πŸ™ˆ.

  • -D name β€” Predefines name as a macro with the definition 1, no thoughts needed πŸ’–.
  • -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 important bit: the definition gets truncated by embedded newline characters βœ‚οΈ. If you're invoking the preprocessor from a shell (or something shell-like), you might need the shell's quoting syntax to protect characters like spaces that have a meaning in shell syntax 🐚. Want a function-like macro on the command line? Write its argument list with surrounding parentheses before the equals sign (if any). Parentheses mean something to most shells, so quote the option. With sh and csh, -D'name(args...)=definition' works 🧚. The -D and -U options are processed in the order they're given on the command line, and all -imacros file and -include file options are processed after all the -D and -U options πŸ‘‘.
  • -MMD β€” okay so this is basically -MD, but chill πŸ¦‹ it only mentions your own header files and skips the system ones entirely πŸŽ€
  • -fpreprocessed β€” bestie, this tells the preprocessor "hey, this file is already preprocessed, don't redo it" πŸ’… so macro expansion, trigraph conversion, escaped newline splicing, and most directive processing all get skipped ✨ Comments still get recognized and removed, so you can pass a file made with -C to the compiler without any drama. In this mode the integrated preprocessor is basically just a tokenizer for the front ends 🧚

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

  • -fdirectives-only β€” when preprocessing, handle directives but do not expand macros 🌷 What it does depends on -E and -fpreprocessed:
    • With -E, preprocessing is limited to directives like #define, #ifdef, and #error. Other stuff like macro expansion and trigraph conversion is not performed. The -dD option is also implicitly enabled πŸ’–
    • With -fpreprocessed, predefinition of command line and most builtin macros is disabled. Contextually dependent macros like __LINE__ are handled normally. This lets you compile files that were previously preprocessed with -E -fdirectives-only 🫢
    • With both -E and -fpreprocessed, the -fpreprocessed rules take precedence. This lets you do full preprocessing of files that were previously preprocessed with -E -fdirectives-only πŸ‘‘
  • -fdollars-in-identifiers β€” accept $ in identifiers πŸ’…
  • -fextended-identifiers β€” accept universal character names and extended characters in identifiers ✨ This one is on by default for C99 (and later C standards) and C++ 🌈
  • -fno-canonical-system-headers β€” when preprocessing, don't shorten system header paths with canonicalization πŸ™ˆ
  • -fmax-include-depth=depth β€” sets the maximum depth of nested #include 🧢 The default is 200.
  • -ftabstop=width β€” sets the distance between tab stops πŸ“ This helps the preprocessor report correct column numbers in warnings or errors, even when tabs show up on the line. If the value is less than 1 or greater than 100, the option is ignored. The default is 8.
  • -ftrack-macro-expansion[=level] β€” tracks token locations across macro expansions πŸ” This lets the compiler print the current macro expansion stack when an error happens inside a macro. Be warned: using it makes the preprocessor and the compiler eat more memory πŸ’€ The level parameter picks how precise the tracking is, so you can drop the memory usage if you need to:
    • Level 0 turns this option off.
    • Level 1 tracks token locations in a degraded mode for minimal memory overhead. In this mode, every token from expanding an argument of a function-like macro has the same location.
    • Level 2 tracks token locations completely. This is the most memory hungry one 🐷

If you give this option no argument, the default level is 2.

Also, -ftrack-macro-expansion=2 is activated by default πŸ‘€

  • -fmacro-prefix-map=old=new β€” when preprocessing files living in directory old, expands __FILE__ and __BASE_FILE__ 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 make builds more reproducible and location independent 🌱 This option also affects __builtin_FILE() during compilation. See also -ffile-prefix-map.
  • -fexec-charset=charset β€” sets the execution character set, used for string and character constants πŸ”€ The default is UTF-8. charset can be any encoding the 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 one of UTF-32BE, UTF-32LE, UTF-16BE, or UTF-16LE, whichever matches the width of wchar_t and the big-endian or little-endian byte order used for code generation. Like -fexec-charset, charset can be any encoding the system's iconv routine supports.
Heads up bestie ⚠️ you'll have problems with encodings that don't fit exactly in wchar_t.
  • -finput-charset=charset β€” sets the input character set, used for translating from the input file's character set into the source character set GCC uses πŸ“₯ If the locale doesn't say, or GCC can't get that info from the locale, the default is UTF-8. The locale or this command-line option can override that, but if they conflict, the command-line option wins πŸ’ͺ charset can be any encoding the system's iconv 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 on, the preprocessor emits a second linemarker after the initial one, containing the current working directory followed by two slashes. GCC uses that directory, when it's in the preprocessed input, as the working directory in some debugging information formats πŸ› This option is implicitly enabled if debugging information is enabled, but you can stop that with the negated form -fno-working-directory. If -P is in the 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, which is still supported, 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 comments in processed directives, which get deleted along with the directive.
Be prepared for side effects with -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 🌈

On top of the -C side effects, -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 πŸ™…β€β™€οΈ

The -CC option is mostly used to support lint comments 🧹

  • -P β€” inhibits linemarkers in the preprocessor output πŸš«πŸ“ This might come in handy when running the preprocessor on something that isn't C code and will be sent to a program that might get confused by the linemarkers 😡
  • -traditional and -traditional-cpp β€” try to imitate the behaviour of pre-standard C preprocessors, instead of ISO C preprocessors πŸ¦• GCC doesn't otherwise try to emulate a pre-standard C compiler, and these options are only supported with the -E switch, or when invoking CPP explicitly πŸ™ƒ
  • -trigraphs β€” supports ISO C trigraphs πŸ”Ί These are three-character sequences that all start with ??, which ISO C defines 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. See the -std and -ansi options.

  • -remap β€” enables 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 everything else it normally does πŸ“œ Each name is 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 gets 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 behaviour conflicts, the result is undefined πŸ’€
    • -dM β€” instead of the normal output, generates a list of #define directives for all the macros defined during preprocessing, including predefined macros πŸ“‹ This is a great way to find out what's predefined in your version of the preprocessor πŸ”Ž Assuming you have no file foo.h, the command below shows all the predefined macros:
touch foo.h; cpp -dM foo.h
  • -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 are expanded, or whose definedness is tested in preprocessor directives, get output 🎯 The output is delayed until the macro is used or tested, and #undef directives are also output for macros that were tested but undefined at that time 🧠
  • -fdebug-cpp β€” this option 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 is 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 β€” okay bestie, these four all do the same core job: they slide dir onto the list of folders CPP digs through for header files during preprocessing πŸ“‚βœ¨
    • If dir begins with = or $SYSROOT, that = or $SYSROOT gets swapped out for the sysroot prefix. Peep --sysroot and -isysroot for the deets πŸ—ΊοΈ
    • -iquote only applies to the quote form of the directive, #include "file". -I, -isystem, and -idirafter cover lookups for both #include "file" and #include <file> πŸ’…
    • You can stack any number or combo of these on the command line to search a bunch of folders. The lookup order goes like this:
      1. For the quote form of the include directive, the directory of the current file gets searched first.
      2. For the quote form, the -iquote dirs get searched left-to-right, in the order they appear on the command line.
      3. The -I dirs get scanned left-to-right.
      4. The -isystem dirs get scanned left-to-right.
      5. The standard system directories get scanned.
      6. The -idirafter dirs get scanned left-to-right.
    • Want to override a system header with your own version? Use -I, since those folders get searched before the standard system header directories πŸ¦‹ But please don't use it to add folders full of vendor-supplied system headers. Use -isystem for that 🚫
    • -isystem and -idirafter also mark the directory as a system directory, so it gets the same VIP treatment as the standard system directories πŸ‘‘
    • If a standard system include directory, or a directory given with -isystem, is also passed with -I, the -I option gets ignored πŸ™ƒ The directory is still searched, but as a system directory at its normal position in the system include chain. This keeps GCC's procedure for fixing buggy system headers and the ordering for the #include_next directive from getting accidentally changed πŸ› οΈ If you really need to change the search order for system directories, reach for the -nostdinc and/or -isystem options instead.
  • -I- β€” splitting the include path, but this option is deprecated 😭 Use -iquote instead for the -I dirs that come before -I-, and then remove the -I- option.
    • Any dirs given with -I before -I- only get searched for headers requested with #include "file". They're not searched for #include <file>. If extra dirs are given with -I after the -I-, those get searched for all #include directives πŸ’–
    • -I- also stops the directory of the current file from being the first search directory for #include "file". There's no way to override this effect of -I- 🚫
Heads up, bestie: -I- is deprecated, so swap your pre--I- -I dirs over to -iquote and drop -I- altogether ⚠️
  • -iprefix prefix β€” set prefix as the prefix for any -iwithprefix options that come after it 🧚
    • If the prefix represents a directory, you should include the final / πŸ“
  • -iwithprefix dir, -iwithprefixbefore dir β€” tack dir onto the prefix you set earlier with -iprefix, then add the resulting directory to the include search path 🌈
    • -iwithprefixbefore drops it in the same spot -I would. -iwithprefix puts it where -idirafter would πŸ“
  • -isysroot dir β€” this is like the --sysroot option, but it only applies to header files (except on Darwin targets, where it covers both header files and libraries). Check out the --sysroot option for more info 🌸
  • -imultilib dir β€” use dir as a subdirectory of the directory that holds the target-specific C++ headers 🧁
  • -nostdinc β€” don't search the standard system directories for header files πŸ™… Only the dirs explicitly given with -I, -iquote, -isystem, and/or -idirafter options (plus the directory of the current file, if appropriate) get searched πŸ”
  • -nostdinc++ β€” skip the C++-specific standard directories when hunting for headers, but still search the other standard directories. (This one's used when building the C++ library πŸ› οΈ)
  • -Wcomment, -Wcomments β€” warn 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 β€” warn if any trigraphs pop up 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 πŸ’…
    • Want trigraph conversion without the warnings, but still want the other -Wall warnings? Use -trigraphs -Wall -Wno-trigraphs πŸ¦‹
  • -Wundef β€” warn if an undefined identifier gets evaluated in an #if directive. Those identifiers get replaced with zero 🫠
  • -Wexpansion-to-defined β€” warn whenever defined shows up in the expansion of a macro (including when the macro gets expanded by an #if directive). That usage isn't portable 🚫 This warning is also enabled by -Wpedantic and -Wextra ✨
  • -Wunused-macros β€” warn 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 at the time it gets redefined or undefined 🧹
    • Built-in macros, macros defined on the command line, and macros defined in include files are not warned about πŸ™ˆ
Note, bestie: if a macro is actually used, but only used in skipped conditional blocks, then the preprocessor reports it as unused 😭 To avoid 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. Alternatively, you could provide a dummy use with something like:
#if defined the_macro_causing_the_warning
#endif
  • -Wno-endif-labels β€” don't warn whenever an #else or an #endif is followed by text. This sometimes happens in older programs with code of the form:
#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 when looking for include files, or to control dependency output 🧚

Note that you can also specify places to search using options such as -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 πŸ‘‘

CPATH, C_INCLUDE_PATH, CPLUS_INCLUDE_PATH, OBJC_INCLUDE_PATH

Each variable's value is a list of directories separated by a special character, much like PATH, and that's where they go looking for header files πŸ“‚ The special character, PATH_SEPARATOR, is target-dependent and gets determined at GCC build time πŸ› οΈ For Microsoft Windows-based targets it's a semicolon, and for almost all other targets it's a colon πŸ’œ

CPATH specifies a list of directories to be searched as if they were specified with -I, but after any paths given with -I options on the command line. This environment variable is used regardless of which language is being preprocessed 🌈

The remaining environment variables apply only when preprocessing the particular language they're tied to. Each one specifies a list of directories to be searched as if they were specified with -isystem, but after any paths given with -isystem options on the command line πŸ“

In all these variables, an empty element instructs the compiler to search its current working directory. Empty elements can appear at the beginning or end of a path. For instance, 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 specifies how to output dependencies for Make, based on the non-system header files processed by the compiler. System header files are ignored in the dependency output πŸ™ˆ

The value of DEPENDENCIES_OUTPUT can be just a file name, in which case the Make rules are written to that file, with the target name guessed from the source file name. Or the value can have the form file target, in which case the rules are written to file, using 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), except that system header files are not ignored, so it implies -M rather than -MM. However, the dependence on the main input file is omitted 🫠

SOURCE_DATE_EPOCH

If this variable is set, its value specifies a UNIX timestamp to be used in place of the current date and time in the __DATE__ and __TIME__ macros, so the embedded timestamps become reproducible ⏰

The value of SOURCE_DATE_EPOCH must be a UNIX timestamp, defined as the number of seconds (excluding leap seconds) since 01 Jan 1970 00:00:00 represented in ASCII. It's identical to the output of date +%s on GNU/Linux and other systems that support the %s extension in the date command πŸ“…

The value should be a known timestamp, such as the last modification time of the source or package, and it should be set by the build process πŸ› οΈ


✨ SEE ALSO ✨

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

Copyright (c) 1987-2021 Free Software Foundation, Inc. πŸ’– Okay so this is the legal part, bestie, but it's actually giving open-source queen energy πŸ‘‘

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 lives in the man page gfdl(7), so go check it out if you're curious ✨

Also, no cap, 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.
Ngl the back-cover text is lowkey the sweetest thing ever. Freedom to copy and modify, and your copies help fund GNU development πŸ’œ

That's the whole page, bestie! πŸŽ€ It's from gcc-11, dated 2024-07-19, and it's the iconic CPP(1) page πŸ’…πŸŒˆ

πŸ“¦ where this page comes from