1.10 — A first look at the preprocessor

The preprocessor is perhaps best thought of as a separate program that runs just before the compiler when you compile your program. When the preprocessor runs, it simply scans through each code file from top to bottom, looking for directives. Directives are specific instructions that start with a # symbol and end with a newline (NOT a semicolon). There are several different types of directives, which we will cover below.

The preprocessor is not smart -- it does not understand C++ syntax; rather, it simply manipulates text before the compiler runs. The output of the preprocessor is then sent to the compiler. Note that the preprocessor does not modify the original code files in any way -- rather, all text changes made by the preprocessor happen temporarily in-memory.


You’ve already seen the #include directive in action. When you #include a file, the preprocessor copies the contents of the included file into the including file at the point of the #include directive. This is useful when you have information that needs to be included in multiple places (as forward declarations often are).

The #include command has two forms:

#include <filename> tells the preprocessor to look for the file in a special place defined by the operating system where header files for the C++ runtime library are held. You’ll generally use this form when you’re including headers that come with the compiler (e.g. that are part of the C++ standard library).

#include "filename" tells the preprocessor to look for the file in the directory containing the source file doing the #include. If it doesn’t find the header file there, it will check any other include paths that you’ve specified as part of your compiler/IDE settings. That failing, it will act identically to the angled brackets case. You’ll generally use this form for including your own header files.

Macro defines

The #define directive can be used to create a macro. A macro is a rule that defines how an input sequence (e.g. an identifier) is converted into a replacement output sequence (e.g. some text).

There are two basic types of macros: object-like macros, and function-like macros.

Function-like macros act like functions, and serve a similar purpose. We will not discuss them, because their use is generally considered dangerous, and almost anything they can do can be done by an (inline) function.

Object-like macros can be defined in one of two ways:

#define identifier
#define identifier substitution_text

The top definition has no substitution text, whereas the bottom one does. Because these are preprocessor declarations (not statements), note that neither form ends with a semicolon.

Object-like macros with substitution text

When the preprocessor encounters this directive, any further occurrence of ‘identifier’ is replaced by ‘substitution_text’. The identifier is traditionally typed in all capital letters, using underscores to represent spaces.

Consider the following snippet:

The preprocessor converts this into the following:

Which, when run, prints the output My favorite number is: 9.

We discuss this case (and why you shouldn’t use it) in more detail in section 2.9 -- Const, constexpr, and symbolic constants.

Object-like macros without substitution text

Object-like macros can also be defined without substitution text.

For example:

Macros of this form work like you might expect: any further occurrence of the identifier is removed and replaced by nothing!

This might seem pretty useless, and it is for doing text substitution. However, that’s not what this form of the directive is generally used for. We’ll discuss the uses of this form in just a moment.

Unlike object-like macros with substitution text, macros of this form are generally considered acceptable to use.

Conditional compilation

The conditional compilation preprocessor directives allow you to specify under what conditions something will or won’t compile. The only conditional compilation directives we are going to cover in this section are #ifdef, #ifndef, and #endif.

The #ifdef preprocessor directive allow the preprocessor to check whether a value has been previously #defined. If so, the code between the #ifdef and corresponding #endif is compiled. If not, the code is ignored.

Consider the following snippet of code:

Because PRINT_JOE has been #defined, the line cout << "Joe" << endl; will be compiled. Because PRINT_BOB has not been #defined, the line cout << "Bob" << endl; will not be compiled.

#ifndef is the opposite of #ifdef, in that it allows you to check whether a name has NOT been defined yet.

This program prints “Bob”, because PRINT_BOB was never #defined.

Conditional compilation is used quite a bit in the form of header guards. We’ll take a look at those in the next lesson.

Now you might be wondering:

Since we defined PRINT_JOE to be nothing, how come the preprocessor didn’t replace PRINT_JOE in #ifdef PRINT_JOE with nothing? Macros only cause text substitution for normal code. Other preprocessor commands are ignored. Consequently, the PRINT_JOE in #ifdef PRINT_JOE is left alone.

For example:

The scope of defines

Directives are resolved before compilation, from top to bottom on a file-by-file basis. Once the preprocessor has finished, all directives from that file are discarded.

This means that directives are only valid from the point of definition to the end of the file in which they are defined. Directives defined in one code file do not have impact on other code files in the same project.

Consider the following example:



The above program will print:

Not printing!

Even though PRINT was defined in main.cpp, that doesn’t have any impact on anything in function.cpp. This will be of consequence in the next lesson on header guards.

Finally, note that directives defined in a header file can be #included into multiple code files. This provides a way for directives to be defined in one place and used in multiple code files. We’ll see an example of this in the next lesson as well.

1.10a -- Header guards
1.9 -- Header files

126 comments to 1.10 — A first look at the preprocessor

  • Connor

    Hi Alex, thanks again for all of your help to date. I have some questions about MACROs and DIRECTIVES if you are willing to help. A lot of libraries I’m coming across tend to use them, so to further my understanding, I have a couple questions with respect to the pre-processor. This will be kind of long winded – sorry.





    1.    Why doesn’t the order that files are #included in Main.cpp matter? Since Multiply.h is included before AddDefault.h how can it see the function-like MACRO ADD(x, y) (x+y)?

    2.    If I want to override the AddDefaut.h ADD(x, y) definition (but keep the other #defines) can I somehow get AddDefault.h to check Main.cpp and see if it has AddOveride.h? Below is one approach I have made – It seems to work but I can’t figure out why- how does AddDefault know to look in Main.cpp? What if placed #ifdef ADDOVERIDE_H after #ifndef ADD(x, y)?



    3.    I would like my Main.cpp function to display whether or not there is an AddOveride.h #included; can this be done through either std::cout or another method, i.e.


    4.    Are there any clear and simple pre-processor books you’d recommend?

    Thanks in advance for your help.

    • Alex

      1) Macros are expanded inline where they are used (not evaluated they are defined). So it isn’t until MULTIPLY is _called_ that the MULTIPLY macro is expanded. Only at that point, ADD needs to have already been defined.
      2) The following doesn’t make sense to me:

      ADDOVERRIDE_H can only be defined if AddOverride.h has already been included, so why re-include it? You’ll just hit the header guards of AddOverride.h and it won’t include anything.

      3) Yes, just move the #ifdef/#ifndef ADDOVERRIDE_H lines into your main function where you have the comment asking the question about how to do this.
      4) No. My recommendation would be to avoid the preprocessor as much as possible. Personally, I only use it for includes and header guards.

  • Camron

    Every time i try to build i get these errors

    LNK1120    1 unresolved externals


    LNK2011    precompiled object not linked in; image may not run

  • Rishi

    Could you please explain this program step by step:



    • Alex

      function.cpp and main.cpp are compiled separately.

      When main.cpp is compiled, PRINT is #defined, but never used.
      When function.cpp is compiled, #ifdef PRINT is false (because PRINT wasn’t #defined in this file above this point), so the code between the #ifdef and #endif is not compiled. Relatedly, #ifndef PRINT is true, so the code between the #ifndef and #endif is compiled.

      Therefore, function doSomething() ends up looking like this:

      And when it's called from main(), that single line is executed.

  • aditya

    what is the use of directives? can you please give a brief idea.

    • Alex

      Directives generally tell the preprocessor to include something, exclude something, or substitute one bit of text for another. We cover how a few of them work in this article…

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter