Search

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.

Includes

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:

function.cpp:

main.cpp:

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
Index
1.9 -- Header files

154 comments to 1.10 — A first look at the preprocessor

  • Dominilk

    Hello i just wanted to ask a question.
    So here is my main.cpp

    here is my add.h

    here is my define.h

    And my output is:
    The sum of number 3 and 4 is = 7
    Bob
    0
    Joe
    0

    my question is : How can i get my output without zero's.
    Thank you and im sorry for my lack of knowledge.

    ::::::EDIT::::::
    basically i realised
    in define.h i had to change the function type from INT to VOID

    and in main.cpp i just had to do few tweaks...

    ....anyways, now i just want to thank you for the hard work you have done to provide us with this Godlike website <3

  • Silviu

    The output is 11.
    When i saw this problem, i didn't knew how to answer, because i didn't encounter something like this and I did read Alex lessons many times and did helped me a lot and thanks for that( the more you work, the more you get ). Can someone explain ? or redirect on better thinking.
    Thank you.

    • Hi Silviu!

      Preprocessor macros are expanded before any evaluation happens.

      Since multiplication takes precedence over addition, (y * x) is executed first, giving you 6. 2 + 6 + 3 = 11.

      • Silviu

        thank you, the problem looks simple, but a little bit weird(for me), because i'm used to more simple macros (not to say in general with c ), like in Alex examples..

      • ElectroCode

        But how  does it get expanded to

        std::cout << x + y * x + y;

        when its #define FUN(x) x*x
        And than it is
        Int x = 2;
        Int y = 3;

        I think it ....does   2*2 which is 4 and stores it into variable x and than
        It does

        Std::cout << x +y which results in 7 ....

        Please help

        • > I think it ....does   2*2 which is 4 and stores it into variable x
          No. Preprocessor macros don't do anything on their own. They can only be used to generate code. You're passing
          x + y
          as parameter x (this is another x, it's not a variable or parameter as in regular functions!) and your code is expanded to what I showed in the previous reply.

  • Le Hung

    Hi everyone.

    as far as I understand, preprocessor work like a text(code) processor eg: macro #define replace some code by some other code or nothing, but it not modify the text(code). I wonder how preprocessor like #define do its job.

    • nascardriver

      Hi Le Hung!

      The preprocessor processes your files and saves them internally or to a temporary location. The generated files are still C++ code but with all #defines and #includes replaced. G++ can permanently store the temporary files with the --save-temps option. I don't know about other compilers.

  • amigo

    But if you write:

    above

    IN the main.cpp file, the result would be "Printing", even though main.cpp and function.cpp are different files

  • Hasan Niyazi

    Hello,
    Do you have websites for other programming languages also?

  • Prabhat Kumar Roy

    "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." - If it is so, then why is it there(if at all exists)?

    Thank you Sir.

    Best regards.

    • nascardriver

      Hi Prabhat!

      They are still required in certain, rare occasions.
      But even if they weren't, a feature cannot just be removed from the language, because it would break old code.

  • J Gahr

    When writing a program, does it make any difference to the pre-processor, compiler, linker, etc. if some of the program files are created as resource files, and others as source files?

    I was getting an error just copy and pasting your example program into my IDE, and wondered if that could be a cause.

    • Alex

      Visual studio doesn't seem to care (when I put a .cpp file in the Resource Files part of the solution, the program still compiled fine). I can't say whether other IDES behave similarly.

  • #define PRINT_JOE

    #ifdef PRINT_JOE
    std::cout << "Joe" << std::endl;
    #endif

    #ifdef PRINT_BOB
    std::cout << "Bob" << std::endl;
    #endif

    Here, this piece of code is not compiled immediately, but after including the main() function it runs properly. I'm using DevC++ for running program, why is this happening?
    kindly provide an answer.Thank you.

    • nascardriver

      Hi Ritesh!

      You cannot have code outside of functions. If your program doesn't have a main function your computer doesn't know where to start executing from.

      • okay, then what actually happens when we write header(#include "add.h") files they can be written without including main() function

        • nascardriver

          What I meant by 'code' is code that actually does something. Declarations, includes etc. are fine outside of functions.

          If you have a program without a main function it won't do anything (Assuming we are talking about normal executables).

          • Alex

            In other words, preprocessor directives get processed by the preprocessor before the compiler even runs. The preprocessor follows different rules than the compiler, because it's a different process.

  • Dear Teacher, please let me ask you: In first paragraph, second sentence, you say: "it simply scans through each code file from top to bottom". In IDEs files are arranged from left to right. Do you mean: "from left to right"? Regards.

    • nascardriver

      Hi Georges!

      The IDE doesn't have any influence here, so the order of your tabs/windows doesn't matter. I suspect the preprocessor scans files in alphabetic order or it scans the include tree. It shouldn't make a difference though.

    • Alex

      > In IDEs files are arranged from left to right

      I'm not sure what you mean by this, whether you're referring to the files themselves or the contents within a file.

      But either way, the preprocessor scans each C++ file individually, starting at the top and working its way down line by line, scanning each line left to right.

      So basically, the same way we'd read a book.

  • 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.

    Suppose:

    AddDefault.h:

    Multiply.h:

    Main.cpp:

        
    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)?

    AddOveride.h:

    AddDefault.h:

    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.

    Main.cpp:

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

    Thanks in advance for your help.
    Connor

    • 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

    and

    LNK2011    precompiled object not linked in; image may not run

  • Rishi

    Could you please explain this program step by step:

    function.cpp:

    main.cpp:

    • 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.

    • Dolan

      If forward call the function inside the same CPP it does cout "Printing!"

      But

      Will give "Not printing!" so it seems is universal that the directives will be checked from top to bottom regardless of situation by program.

  • aditya

    sir,
    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 all code inside code tags: [code]your code here[/code]