Search

2.10 — Introduction to the preprocessor

Translation and the preprocessor

When you compile your code, you might expect that the compiler compiles the code exactly as you’ve written it. This actually isn’t the case.

Prior to compilation, the code file goes through a phase known as translation. Many things happen in the translation phase to get your code ready to be compiled (if you’re curious, you can find a list of translation phases here).

The most noteworthy of the translation phases involves the preprocessor. The preprocessor is best thought of as a separate program that manipulates the text in each code file.

When the preprocessor runs, it scans through the code file (from top to bottom), looking for preprocessor directives. Preprocessor directives (often just called directives) are instructions that start with a # symbol and end with a newline (NOT a semicolon). These directives tell the preprocessor to perform specific particular text manipulation tasks. Note that the preprocessor does not understand C++ syntax -- instead, the directives have their own syntax (which in some cases resembles C++ syntax, and in other cases, not so much).

The output of the preprocessor goes through several more translation phases, and then is compiled. 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 each time the code file is compiled.

In this lesson, we’ll discuss what some of the most common preprocessor directives do.

Includes

You’ve already seen the #include directive in action (generally to #include <iostream>). When you #include a file, the preprocessor replaces the #include directive with the preprocessed contents of the included file, which is then compiled.

Consider the following program:

When the preprocessor runs on this program, the preprocessor will replace #include <iostream> with the preprocessed contents of the file named “iostream”.

Since #include is almost exclusively used to include header files, we’ll discuss #include in more detail in the next lesson (when we discuss header files in more detail).

Macro defines

The #define directive can be used to create a macro. In C++, a macro is a rule that defines how input text is converted into replacement output 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 here, because their use is generally considered dangerous, and almost anything they can do can be done by a normal 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 directives (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 the identifier is replaced by substitution_text. The identifier is traditionally typed in all capital letters, using underscores to represent spaces.

Consider the following program:

The preprocessor converts the above into the following:

Which, when run, prints the output My name is: Alex.

We recommend avoiding these kinds of macros altogether, as there are better ways to do this kind of thing. We discuss this more in lesson 4.13 -- 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 useless 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. There are quite a few different conditional compilation directives, but we’ll only cover the three that are used by far the most here: #ifdef, #ifndef, and #endif.

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

Consider the following program:

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 be ignored.

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

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

#if 0

One more common use of conditional compilation involves using #if 0 to exclude a block of code from being compiled (as if it were inside a comment block):

The above code only prints “Joe”, because “Bob” and “Steve” were inside an #if 0 block that the preprocessor will exclude from compilation.

This provides a convenient way to “comment out” code that contains multi-line comments.

Object-like macros don’t affect other preprocessor directives

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:

In actuality, the output of the preprocessor contains no directives at all -- they are all resolved/stripped out before compilation, because the compiler wouldn’t know what to do with them.

The scope of defines

Directives are resolved before compilation, from top to bottom on a file-by-file basis.

Consider the following program:

Even though it looks like #define MY_NAME “Alex” is defined inside function foo, the preprocessor won’t notice, as it doesn’t understand C++ concepts like functions. Therefore, this program behaves identically to one where #define MY_NAME “Alex” was defined either before or immediately after function foo. For general readability, you’ll generally want to #define identifiers outside of functions.

Once the preprocessor has finished, all defined identifiers 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 any of the code in function.cpp (PRINT is only #defined from the point of definition to the end of main.cpp). This will be of consequence when we discuss header guards in a future lesson.


2.11 -- Header files
Index
2.9 -- Naming conflicts and the std namespace

174 comments to 2.10 — Introduction to the preprocessor

  • Lawrence

    How come it says FUN(x + y) even though in the directive it says FUN(x) x*x. Shouldn't it say FUN(x)(x + y)? Why is the (x) next to FUN removed?

  • alfonso

    As I expected, if we include function.cpp in main.cpp

    , and compile only main.cpp, the output will be "Printing!"

    But yes, I know #include "file.cpp" should be avoided and compile each file instead.

    Under section "Conditional Compilation", the program uses std::cout (explicit namespace name) but the comment after that does not match the program:

    "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 be ignored."

    Not too big problem if you'll ask me, but I thought you may want to know about that.

    Thank you for the tutorial!

  • Prateek

    In the last example :
    What if we compile main.cpp linking it further with function.cpp

    Will Output be  the following?

    Printing !

  • bryan chance

    Love thie detailed information on your site. Thank you.
    I was looking for Macros and in particular "function-like" macros.

    The comment indicates: "Function-like macros act like functions, and serve a similar purpose. We will not discuss them here, because their use is generally considered dangerous, and almost anything they can do can be done by a normal function."

    Are you saying that you won't discuss it because it is dangerous or because normnal functions can do job just as effectively.
    Regardless either case, don't hold back just because something is "consider dangerous" by some people.  There's a reason why it is a feature and a scenario where it is dangerous by useful if you know how to use it properly.  And if you dont' discuss it, i wouldn't know how to use it at all.   ;p

    thank you...:p

    • Hi Bryan!

      > Are you saying that you won't discuss it because it is dangerous or because normnal functions can do job just as effectively.
      Both. Function-like macros were discussed at some point, but have been removed, because there's no longer a reason to use them. Modern C++ offers enough ways of achieving the same without using the preprocessor.

      > There's a reason why it is a feature
      Features are rarely removed from the language, because doing so would break old code.

      I'm not the author of this article, @Alex might have a different point of view.

      • Alex

        You nailed it.

        Most of the cases that macros were useful for have been replaced by options that are strictly better. There are still a few "legitimate" use cases (I'm thinking about __LINE__ and __FILE__) but even those have replacements in the works (https://en.cppreference.com/w/cpp/experimental/source_location).

  • Red Lightning

    "Note that the preprocessor is not smart -- it does not understand C++ syntax."
    So most of the people in the world are not smart.

    • Alex

      In context, the preprocessor is pretty dumb compared to the compiler. That said, I've updated the wording so to ensure no learners inadvertently take offense.

  • Juan

    I have read tutorials but here I am learning a lot!

  • Heisenberg

    Having studying these chapter, can you please tell about some online platform where I can practice problems in parallel?
    or
    It will be better if I complete all the chapters and then start practising?

    • Hi!

      There are quizzes at the end of every chapter and sometimes at the end of lessons. Please solve them and post your solutions for review.
      You'll get the best proactive by working on the project you're interested it, though, coming up with an idea can be hard.

  • GG

    Last sentence : (PRINT is only defined from the point of definition to the end of function.cpp).    should be: to the end of main.cpp

    In Include paragraph:  Since #include is used to almost exclusively used to include header files,     ...used to   ...used to  typo

  • 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

    • Kwan

      your functions Joe and Bob both are in int type. Therefore when you execute your program, it will print Bob and 0 ( the return value for your "int Joe" function)

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

      • Hassan

        Hi everyone,
        i think the problem can be solved by adding extra parenthesis around the FUN(x) body like this:

        • Louis Cloete

          Even better would be this:

          This is to enable you to use FUN() as input to another operator with higher precedence than operator*

          It is still not a good idea, though, because type checking is suspended for function-like macros. That means you can do this

          which will compile fine, but result in undefined behaviour.

          Rather use a function or an inline function for this. It will enforce the type and prevent such errors.

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