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). A code file with translations applied to it is called a translation unit.

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.

As an aside...

Using directives (introduced in lesson 2.9 -- Naming collisions and an introduction to namespaces) are not preprocessor directives (and thus are not processed by the preprocessor). So while the term directive usually means a preprocessor directive, this is not always the case.

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 contents of the included file. The included contents are then preprocessed (along with the rest of the file), and 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 allows 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\n" will be compiled. Because PRINT_BOB has not been #defined, the line cout << "Bob\n" 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.

In place of #ifdef PRINT_BOB and #ifndef PRINT_BOB, you’ll also see #if defined(PRINT_BOB) and #if !defined(PRINT_BOB). These do the same, but use a slightly more C++-style syntax.

#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 collisions and an introduction to namespaces

250 comments to 2.10 — Introduction to the preprocessor

  • noobie

    "Macros only cause text substitution for normal code. Other preprocessor commands are ignored."

    How is the value of TWO resolved in the below scenario?

  • srt1104

    For this program:

    When run, it doesn't print "Bob" on the console.
    But if the preprocessor runs before the compiler and doesn't understand C++ constructs then how does it recognize single-line comments?

    • jr_riar

      the line "// #define BOB" begins with "//" therefore it is no longer a preprocessor directive.  Preprocessor directives have to start with "#".

  • Yousuf

    "Preprocessor directives (often just called directives) are instructions that start with a # symbol and end with a newline (NOT a semicolon)" --- I don't understand the "end with a newline" part here. If preprocessor directives end with newline, aren't we supposed to add a newline character here like '\n'?

  • 3LI M 3alim

    is there any particular reason why numbers in directives don't need "Quotation" mark?

    also, why using (#ifdef, #ifndef) as while you write your code, the compiler 'codeblocks 20.3' shows you whether the condition is true or false?

    • nascardriver

      Macros get replaced by their literal value. If you'd use quotation marks, the 9 would be a string (text), not a number.

      I don't understand your question about #ifdef

      • 3li Mot 3alim

        Does it matter whether it is 'single' or "double" quotation mark?

        The double prints the same number!

        #ifdef & #ifndef
        While I write this condition on code::blocks, I can obviously notice whether the condition is true or false.

        When the condition is false, statements colour is so faint.otherwise it is normal.

        • nascardriver

          Single quotation marks are used for characters, double quotation marks for strings (More than 1 character). We talk more about this later.
          Codeblocks highlights your macros so you can see which parts of you code are or aren't compiled.

          • 3LI M 3alim

            Thank you very much..
            what you said about quotation mark is clear, it is even clearer when I observe std::cout, double qoutes for a text "Hello world", and single quotes for a character '\n'.

            you said,"Macros get replaced by their literal value", when I printed FOO, I expected "500" to become something else other than 500 itself (text values I mean!!).
            ....
            regarding to (#ifdef & #ifndef)

            I'm not sure if I had explained it well..may be an example will help me.

            The body of the condition has a faint colour just like a comment, EVEN after compilation. The second I change #ifndef into #ifdef everything becomes normal!!
            From there I said if the condition is true it looks fine, else it looks just like a comment!!

            • minmin

              It is because the #ifndef ... #endif part is not compiled at the compilation phase (because CHECK_IT is defined). The faint colour is just syntax highlight indicating it is not compiled. On the contrary, #ifdef ... #endif is compiled because CHECK_IT is defined. I'm a beginner in C++, please point it out if something wrong.

  • Blair

    Why would someone use an Object-like macro with substitution text instead of a global variable?

    The given examples was:

    While this illustrates how it works, I imagine many people would think to do this instead:

    I realize pre-processor definitions cannot be re-defined (although we can also make global variables constant) and are executed at compilation, but it's not obvious to me what practical advantage this gives us. The provided example doesn't seem to demonstrate an advantage of Object-like macro with substitution text, or at least it is not explicitly stated.

    Unless I'm off base, I think this is a natural question a learner might have about Object-like macro with substitution text and would lead to a better understanding if it were addressed. Thank you so much for this whole resource. It's really helpful and I hope my inquiry was constructive.

  • phisk

    When running this example program from the above lesson, my Windows Defender identifies it as a trojan, namely "Ludicrouz.J". Any idea why this might happen?

    Thanks.

  • keshav

    The #if 0 paragraph,in the code,why does the syntax not change its colour from orange to green when:(in line 7 and 10)
    7    #if 0 // Don't compile anything starting here
    10   #endif // until this point
    even though there are slashes . why?

  • all defined identifiers

    "Once the preprocessor has finished, all defined identifiers from that file are discarded. "

    If I am right, I think symbol # was missing from "defined" above.

  • replacement output text

    " In C++, a macro is a rule that defines how input text is converted into replacement output text."

    Does the sentence above mean the input text is "MY_NAME" in this example and replacement output text is "ALEX"?

  • Josh

    In the last section, when dealing with multiple files (main.cpp and function.cpp), how would you get this situation to play out? This tutorial has been really good about showing the wrong and the right way, but this instance doesn't show the correct way. Would you break the formatting rule we just learned and put the #define PRINT in the void doSomething() function?

  • Lalit Sharma

    "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." - You initially told this.
    Later you told how preprocessor inserts contents of #include file and replaces text for #define directive? Please explain it with more clarity

    • nascardriver

      The preprocessor replaces #includes _in memory_. It doesn't modify the original file.

      • Abhishek

        It is still not clear.Does the preprocessor take original cpp file and after preprocessing create a file with extension ".i" ?

        • nascardriver

          If you've enabled output of ".i" files, yes. Otherwise is will load the file, do its replacements in memory, and give the new content to the compilers. There's no need to save the modified content to disk.

  • Uros

    Why does this program print the ifNdef text instead of ifdef text?

    • nascardriver

      The preprocessor doesn't care about control flow, it processes files from top to bottom. The #define in line 16 doesn't affect the #ifs.

      • Uros

        what does that mean exactly? what should i do to fix the problem?

        • nascardriver

    • Yousuf

      After running preprocessor on your file, here is what the output looks like, I discarded some codes for the sake of the clarity, only showing here the code you wrote on main.cpp:

      These are the codes[what you coded actually] that goes to compiler. From it we see it clearly what is going to output.

  • Ryan

    Quick typo! In the 'Conditional compilation' section- "The #ifdef preprocessor directive allow the preprocessor"; 'allow' instead of 'allows'.

  • Apaulture

    In regards to conditional compilation preprocessor directives,

    is useful for saving the entire process some overhead in the case that a MACRO is not defined since we wouldn't have to resort to phase 5-9 and eventually the compilation process.

    may be useful as a backup tool (to run the needed code) in the case a MACRO is not defined or missing.

  • aqib

    I could not understand this topic properly
    i just want to know if i do not remember things of this topic so can i use c++ without these things

  • koe

    The previous section on namespaces mentioned 'using' is a directive. Is this accurate, and how does the preprocessor handle 'using' instances?

  • Chayim

    Why does it print ‘Not printing’ ?
    If it’s in the same file function.cpp as ‘Printing’ that is not being compiled because it was not defined because define in main.pp does not have affect on function.pp file, so is ‘Not printing’ in this same file as ‘Printing’.

    • Alex

      PRINT is only #defined from the point of definition to the end of main.cpp.

      When function.cpp is being compiled, PRINT has not been #defined as far as function.cpp is concerned, so the #ifndef directive compiles.

      • Chayim

        My mistake was that I thought that the ‘Not printing’ is directed as #ifdef like ‘Printing’ but now I recognized that it’s  #ifndef PRINT.

  • hellmet

    I was just reviewing...to make sure I retained everything :)
    In the 'conditional compilation' section's example, the comment reads 'execute this code', which is somewhat misleading. Later on, the tutorial correctly points out the code is not compiled at all. I think the 'conditional compilation' example should be amended to show this fact.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]