Search

2.9 — Const, constexpr, and symbolic constants

Const variables

So far, all of the variables we’ve seen have been non-constant -- that is, their values can be changed at any time. For example:

However, it’s sometimes useful to define variables with values that can not be changed. For example, consider the value of gravity on Earth: 9.8 meters/second^2. This isn’t likely to change any time soon. Defining this value as a constant helps ensure that this value isn’t accidentally changed.

To make a variable constant, simply put the const keyword either before or after the variable type, like so:

Although C++ will accept const either before or after the type, we recommend using it before the type because it better follows standard English language convention where modifiers come before the object being modified (e.g. a green ball, not a ball green).

Const variables must be initialized when you define them, and then that value can not be changed via assignment.

Declaring a variable as const prevents us from inadvertently changing its value:

Defining a const variable without initializing it will also cause a compile error:

Note that const variables can be initialized from non-const values:

Const is used most often with function parameters:

Making a function parameter const does two things. First, it tells the person calling the function that the function will not change the value of myValue. Second, it ensures that the function doesn’t change the value of myValue.

With parameters passed by value, like the above, we generally don’t care if the function changes the value of the parameter (since it’s just a copy that will be destroyed at the end of the function anyway). For this reason, we usually don’t const parameters passed by value. But later on, we’ll talk about other kinds of function parameters (where changing the value of the parameter will change the value of the argument passed in). For these types of parameters, judicious use of const is important.

Compile time vs runtime

When you are in the process of compiling your program, that is called compile time. During compile time, the compiler ensures your code is syntactically correct, and converts your code into object files.

When you are in the process of running your application, that is called runtime. During runtime, your program executes line by line.

Constexpr

C++ actually has two different kinds of constants.

Runtime constants are those whose initialization values can only be resolved at runtime (when your program is running). Variables such as usersAge and myValue above are runtime constants, because the compiler can’t determine their values at compile time. usersAge relies on user input (which can only be given at runtime) and myValue depends on the value passed into the function (which is only known at runtime).

Compile-time constants are those whose initialization values can be resolved at compile-time (when your program is compiling). Variable gravity above is an example of a compile-time constant. Whenever gravity is used, the compiler can simply substitute the identifier gravity for the literal double 9.8.

In most cases, it doesn’t matter whether a constant value is runtime or compile-time. However, there are a few odd cases where C++ requires a compile-time constant instead of a run-time constant (such as when defining the length of a fixed-size array -- we’ll cover this later). Because a const value could be either runtime or compile-time, the compiler has to keep track of which kind of constant it is.

To help provide more specificity, C++11 introduced new keyword constexpr, which ensures that the constant must be a compile-time constant:

Rule: Any variable that should not change values after initialization and whose initializer is known at compile-time should be declared as constexpr.
Rule: Any variable that should not change values after initialization and whose initializer is not known at compile-time should be declared as const.

Note: Many of these tutorials were written before constexpr existed and have not been fully updated. In practical terms, this isn’t an issue, as constexpr is only required in specific situations.

Naming your const variables

Some programmers prefer to use all upper-case names for const variables. Others use normal variable names with a ‘k’ prefix. However, we will use normal variable naming conventions, which is more common. Const variables act exactly like normal variables in every case except that they can not be assigned to, so there’s no particular reason they need to be denoted as special.

Symbolic constants

In the previous lesson 2.8 -- Literals, we discussed “magic numbers”, which are literals used in a program to represent a constant value. Since magic numbers are bad, what should you do instead? The answer is: use symbolic constants! A symbolic constant is a name given to a constant literal value. There are two ways to declare symbolic constants in C++. One of them is good, and one of them is not. We’ll show you both.

Bad: Using object-like macros with a substitution parameter as symbolic constants

We’re going to show you the less desirable way to define a symbolic constant first. This method was commonly used in a lot of older code, so you may still see it.

In lesson 1.10 -- A first look at the preprocessor, you learned that object-like macros have two forms -- one that doesn’t take a substitution parameter (generally used for conditional compilation), and one that does have a substitution parameter. We’ll talk about the case with the substitution parameter here. That takes the form:

#define identifier substitution_text

Whenever 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:

When you compile your code, the preprocessor replaces all instances of MAX_STUDENTS_PER_CLASS with the literal value 30, which is then compiled into your executable.

You’ll likely agree that this is much more intuitive than using a magic number for a couple of reasons. MAX_STUDENTS_PER_CLASS provides context for what the program is trying to do, even without a comment. Second, if the number of max students per classroom changes, we only need to change the value of MAX_STUDENTS_PER_CLASS in one place, and all instances of MAX_STUDENTS_PER_CLASS will be replaced by the new literal value at the next compilation.

Consider our second example, using #define symbolic constants:

In this case, it’s clear that MAX_STUDENTS_PER_CLASS and MAX_NAME_LENGTH are intended to be independent values, even though they happen to share the same value (30).

So why not use #define to make symbolic constants? There are (at least) two major problems.

First, because macros are resolved by the preprocessor, which replaces the symbolic name with the defined value, #defined symbolic constants do not show up in the debugger (which shows you your actual code). So although the compiler would compile int max_students = numClassrooms * 30;, in the debugger you’d see int max_students = numClassrooms * MAX_STUDENTS_PER_CLASS;. You’d have to go find the definition of MAX_STUDENTS_PER_CLASS in order to know what the actual value was. This can make your programs harder to debug.

Second, #defined values always have file scope (which we’ll talk more about in the section on local and global variables). This means a value #defined in one piece of code may have a naming conflict with a value #defined with the same file later.

For example:

In the above code, we #define x in function a(), intending it to be used inside function a(). However, #define value x can actually be used anywhere beyond that point in the same file. So when function b() #defines its own x, we get a naming conflict. Even if function b() used a variable named x instead of a #define value named x, we would still have issues, as the preprocessor would try to replace variable name x with the value 5.

Rule: Avoid using #define to create symbolic constants

A better solution: Use const variables

A better way to create symbolic constants is through use of const (or better, constexpr) variables:

These will show up in the debugger, and follow all of the normal variable rules around scope.

Rule: use const variables to provide a name and context for your magic numbers.

Using symbolic constants throughout a program

In many applications, a given symbolic constant needs to be used throughout your code (not just in one location). These can include physics or mathematical constants that don’t change (e.g. pi or avogadro’s number), or application-specific “tuning” values (e.g. friction or gravity coefficients). Instead of redefining these every time they are needed, it’s better to declare them once in a central location and use them wherever needed. That way, if you ever need to change them, you only need to change them in one place.

There are multiple ways to facilitate this within C++, but the following is probably easiest:

1) Create a header file to hold these constants
2) Inside this header file, declare a namespace (we’ll talk more about this in lesson 4.3b -- Namespaces)
3) Add all your constants inside the namespace (make sure they’re const)
4) #include the header file wherever you need it

e.g. constants.h:

Use the scope resolution operator (::) to access your constants in .cpp files:

If you have both physics constants and per-application tuning values, you may opt to use two sets of files -- one for the physics values that will never change, and one for your per-program tuning values that are specific to your program. That way you can reuse the physics values in any program.

Note: In lesson 4.2 -- Global variables and linkage, we show a more efficient way to do symbolic constants using global variables.

2.10 -- Chapter 2 comprehensive quiz
Index
2.8 -- Literals

135 comments to 2.9 — Const, constexpr, and symbolic constants

  • boredprogrammer

    Unfortunately, symbolic constants (#define ...) cannot be replaced by const expressions, always. C++ is not only used for desktop developtment but for embedded systems, among others. Using embedded systems will change things, since the compiler will store the "const" variables in Flash memory. It uses valuable and scarced resources (a Cortex-M3 STM32L100 can have from 4 to 256kB Flash, PIC18 from 2 to 128kb). Furhtermore the const variables stored in Flash add a penalty when accessed, due to the special sequence of instructions required to access the flash for reading during running, increasing code x4 many times.

    So, for embedded systems, using const can be a bad choice for variable declaration. Yes, you gain readability, but you loose resources and performance. Using symbolic constants won't hurt and they still keep the readability. It depends on your project requeriments of course, a couple of const variables won't hurt, but a few of them being accesed in loops (as limiters for example), could increase the cost of the loop execution. When you deal with the nano or microsecond world, 1 instruction matters. For example, a SD card interface uses by default, and mostly, 512 bytes sector. If you use that as a const variable, surely your loops will suffer from the access to flash instead ram (if symbolic constant was used), as result, your reading/writting to the SD card could be 4 times slower than using the symbolic constant. Of course with some care programming you could use both, reading the variable only once, but then you are ofuscating your code to avoid an implementation (const stored in flash) behaviour, easily avoidable using symbolic constants without ofuscating your code.

    In desktop systems, how the const variable is implemented is hidden and due to the vast ammount of reources no programmer will worry about this, but with small devices things are very different, as stated above.

    Perhaps the most important thing about not using const, is that type checking is lost.

    Anyway, if you really have true constants, and can live with the limitations the compiler implementation will do, use const/constexpr because the type checking of real variabels is important and saves headaches.

    • Alex

      Good point. I should probably mention somewhere that embedded systems typically have specific exceptions to some of the rules due to comparatively low performance/memory.

      However, as I'm not a programmer of embedded devices, I'm not qualified to say what those exceptions are. So I thank you for sharing your knowledge.

  • Jose

    Dear Alex,

    I have two questions:

    1.- When I try to compile the main.cpp with the header Constants.h, my terminal shows: error: unknown type name 'constexpr'. Why?

    2.- What is the difference between header.h and header.hpp?

    Thank you so much.

    • nascardriver

      Hi Jose!

      1.-
      constexpr wasn't part of c++ up until c++11. You need to set the c++ version in your project settings. How this is done varies between compilers and IDEs.

      2.-
      They're the same. Some people prefer .hpp to make clear that this is a c++ header file (and not c), pp standing for plus plus.

  • Which chapter should I refer to if I wanna to make a chessboard game, using a char array in 2 dimensions and the size of this array is determined by the player? Normal array won't work because the size of the array must be compile-time constant. I have tried something, like

    but it is still not working because I totally understand although a const is put before the gameboardsize variable, it is still not a compile-time constant.       I don't want to use the dynamic array because when passing this array to function as the only parameter, there is no way to know the size of this dynamic array so that the function needs to have 2 more parameter which is the size of this dynamic array.
    Is there any solution to this?

    • nascardriver

      Hi Aron!

      The code you've posted might work on some compilers if you remove the 'const', however, you should not do this and you'll have the same problem as you have with dynamic arrays.
      The best solution for now is to pass the size of the game board along with the array itself (You'll only need 1 additional parameters, because the board is a square). If you really don't want to do this you can create a namespace containing a variable describing the board size (This is bad practice).

      More convenient data types aren't introduced until chapter 6, you'd be missing out on too much to make that jump.

      • Alex

        Working with dynamic 2 dimensional arrays is a pain, and error prone. If you're going to do something with a rectangular 2d array (which is the case most of the time), it's much easier (and safer) to define a 1d array (you can use a std::vector) and a function to convert a 2d (row, column) index to a single 1d index.

        I discuss how to do that in lesson 6.14.

  • Hi Alex,

    You've mentioned  - "the value of gravity on Earth: 9.8 meters/second^2". But I think, it should be - "The value of acceleration due to gravity is 9.8 m/s^2". Because, gravity has no units, it's the acceleration that has units. Correct me if I am wrong.

    Thank you for these amazing tutorials.

    Regards.

  • Andre

    Hello Alex. You say we should use constexpr for magic numbers instead of macros. Looking at different apis like windows or glew, they mostly tend to use macros like WM_CLOSE, WM_QUIT, CW_USEDEFAULT, MEM_COMMIT, GL_TRUE, GL_BLEND, etc. instead of constexpr. Does this have other reasons? Can you please explain why they tend to still use macros?

    • nascardriver

      Hi Andre!

      The Windows API is terrible imo. Don't do anything you see in there.
      I never used GLEW, it has probably been written before constexpr has been introduced and updating it could potentially break old source code.
      There are still some cases where you can't avoid using macros, those cases are rare, macros should be avoided whenever possible.

    • Alex

      Nascardriver nails it. A lot of the windows code goes back 10 or 20 years, before a lot of these best practices were established. They won't change it now because they're afraid it will inadvertently break something.

      You know the old phrase, "if it ain't broke, don't fix it"? That definitely applies to programming.

  • Nimbok

    If you #include "constants.h" in more than one file, won't it violate the one definition rule and cause a compilation error?

    • Friendly Programmer

      Don't know if it is too late to respond however it will not violate the one definition rule. The reason being is that the header file uses a header guard.

      #ifndef CONSTANTS_H
      #define CONSTANTS_H

      //some code here

      #endif

      Header guards are explained in a previous chapter however simply put, the preprocessor will always check to see if CONSTANTS_H has been defined. And it will only run that code if it hasn't. So after the first time the preprocessor defines CONSTANTS_H and runs the code it will not do that again as the conditional #ifndef statement will return false and not run the code in between #ifndef and #endif. The reason you need to include it into multiple files is so that the file that you put #include will gain access to the variables inside the .h file.

  • My dear Teacher, please tell me whether following regarding your rules for "const" and "constexpr" is correct.
    1st rule: Any variable that should not change values after initialization and whose initializer is known at compile-time should be declared as constexpr. In a context that requires a compile-time constant, only "constexpr" can be used.
    2nd rule: Any variable that should not change values after initialization and whose initializer is not known at compile-time must be declared as const.

  • Haariger Harry

    Isn't it better to use constexpr instead of const?

  • Dear Teacher, please let me 2 comments. I think
    1. It is more intuitive
    #define MAX_STUDENTS_PER_CLASS = 30
    2. In snippet using #define symbolic constants,
    setMax(MAX_NAME_LENGTH) should be declared.
    Regards.

    • Alex

      1) More intuitive than what?
      2) This is a snippet, not a full program. I don't define what numClassrooms is either, because it's not really relevant to the example. You're welcome to turn this into a full program if you like, but the point is to illustrate a principle, not have something that compiles.

      • Dear Teacher, please let me
        1. Answer your question: It is more intuitive than without equal (=) sign.
        2. With my pleasure I turn snippet into a full program:

        By the way could you please tell me your opinion for compiler online
        https://www.onlinegdb.com/online_c++_compiler ?
        Regards.

        • Alex

          1) Perhaps, but it's also wrong. If you #define MAX_STUDENTS_PER_CLASS = 30, then MAX_STUDENTS_PER_CLASS will be substituted with "= 30" (no quotes) which is not what it is expected.
          2) This one actually looks pretty good. It supports multiple files and integrated debugging, which are two of the common omissions of online compilers. The integrated debugger isn't quite as good as the ones you'll get with an installed IDE, but is much better than nothing!

          • Dear Teacher, please let me express my sincere gratitude for you replied my message.
            1. I mean standard C++ should incorporate the equal sign. Compilers do not compile it. However I learned from this lesson that for constant values I have to use word const.
            2. Because I'm homeless (a Greek in France) and forced to travel from town to town and get internet access in municipal libraries or social services under many restrictions in downloading, I'm forced to use compilers online. I asked your opinion about compiler online onlinegdb just for learn its use. Many thanks for you expressed your opinion.
            3. And a comment. Phrase "However, it’s sometimes useful to define variables with values that can not be changed." is contradictory on that values that can not be changed are not variable, are constant. My english are bad but as long I understand them words "constant" and "variable" are antonyms.
            Regards.

            • Alex

              1) Remember that the preprocessor is a text processor, not a syntax processor. A #defined value can be substituted with any text string. If the syntax included an equals sign, then you'd get weird looking stuff like this:
              #define EQUALS_30 = = 30
              That looks really bizarre, and makes it less clear what value is actually being substituted.
              2) You're welcome.
              3) In common language, they are definitely antonyms. However, in programming, a variable is simply a named piece of memory. The const modifier controls whether we're allowed to change the value in that memory address or not. It probably would have been better to use a different name than "variable", but when variables were first introduced, the concept of const didn't exist yet. That came along later.

  • Dear Teacher, please permit me the question: Does "constexpr" stands for "constant expression"? Regards.

  • himanshu

    Hey alix
    i want to know something
    IS this site created with c++

  • Marius

    Hello, Alex!

    Typo:

    The line over "Compile time vs runtime":

    "For theses types of parameters, judicious use of const is important."

    "theses" should be "these" I believe 🙂

  • my_gravity(9.2) is wrong. According to the theory, value of g = 9.8 m/s^2. So please provide the correct constant values for universal constants.

  • Hema

    A non-constant variable can be converted into a contant varialbe. Can the reverse be possible?

    • Alex

      Yes, you can use a const_cast to get rid of a const, but:
      1) You shouldn't do this.
      2) It's only reliable if the original object was non-const in the first place.
      3) You shouldn't do this.

  • Simon

    Const is most useful (and most often used) with function parameters:

    Making a function parameter const does two things. First, it tells the person calling the function that the function will not change the value of myValue. Second, it ensures that the function doesn’t change the value of myValue.

    I think this is misleading. As a caller of the function printInteger I don't care if the (copied value of) myValue is being changed within the function's body. From the caller's perspective, there is no difference in behavior between the prototype with and without 'const'.

    It's different if the argument is passed by reference, though.

  • gary wang

    Hi, Alex. I have a small question about the symbolic constant.
    As you said, there are two ways to do the symbolic constant. First is by using #defiine and the second one is just use const keyword.

    My question is, I am sure that the second one would take up memory for the program, but what about the first one by using #define. does it take the memory to store the symbolic constant? thank you!~

    • Alex

      In theory, yes, but two thoughts here:
      1) You shouldn't be optimizing to save a few bytes.
      2) The compiler would probably optimize away the variable anyway.

      Even if it doesn't, you should still use the const version. It's easier to debug.

      • Alex

        I'm re-learning C++ after over 20 years working with higher level languages. These tutorials have been invaluable. Thank you.

        There is a popular use case you might want to consider. Internet of things and wearable technology. Things like the Arduino and ESP range of processors.

        The ESP8266, for example, has only about 80kBytes of RAM available for variable storage.

        It is therefore very important to minimise the scope of any variable. In some cases global variables are desirable but global constants are a significant waste of precious RAM.

        I use #define replacement values at the start of my code to make it easier to tweak while still avoiding 'magic numbers.' They can still be assigned to an appropriately named variable but only within the scope they are needed.

        Is that an acceptable way to do that or is there a better way?

        Thank you.

  • Shashank

    Hi,

    If I call a() in b(), shouldn't the output be 555 instead of 556? Once a() is called inside b(), shouldn't x start getting substituted by 5 again from this point onward.

    [void b()
    {
        // Even though we're intending this x to be local to function b()
        // it conflicts with the x we defined inside function a()
    #define x 6
        a();
        std::cout << x;
    }]

  • Rohan

    Respected Sir,
    I have a doubt. In previous section you mentioned that multiple definition raise an error. Please look at the below 2 sets of code both compiled individually. Second one runs just fine but the first doesn't. I was wondering why is that?

    Code1:

    square.h:

    #ifndef SQUARE_H
    #define SQUARE_H

    int getSquareSides()
    {
        return 4;
    }

    int getSquarePerimeter(int sideLength);

    #endif

    square.cpp:

    #include "square.h"

    int getSquarePerimeter(int sideLength)
    {
        return sideLength * getSquareSides();
    }

    main.cpp:

    #include <iostream>
    #include "square.h"

    int main()
    {
        std::cout << "a square has " << getSquareSides() << " sides" << std::endl;
        std::cout << "a square of length 5 has perimeter length " << getSquarePerimeter(5) << std::endl;

        return 0;
    }

    Code 2:

    constants.h:

    #ifndef CONSTANTS_H
    #define CONSTANTS_H

    void print();

    namespace constants
    {
        const double pi(3.14159);
        const double avogadro(6.0221413e23);
        const double my_gravity(9.2);
    }

    #endif

    Function.cpp

    #include <iostream>
    #include "constant.h"

    void print()
    {  
        std::cout << "Printing";
    }

    main.cpp

    #include <iostream>
    #include "constant.h"

    int main()
    {
        print();
        return 0;
    }

    According to what I have understood the first code is not working because of multiple definitions of 'getSquaresides'. But in second code similarly there are multiple definitions of variable inside constant namespace but still the code works fine. If in the first code if I modify the header file by removing the definition of 'getSquaresides' and keeping only declaration and adding the definitions of different variables then again the program runs fine (After defining 'getSquaresides' in some other file). Why is this happening? I have even confirmed that multiple definitions of neither functions nor variables are allowed. Then why just by replacing function by variable the code is working? I am really very confused. I will be obliged to receive any response that will take me out of this confusing situation. Thank you.

    • Alex

      Good question. The short answer is that functions (by default) have external linkage, which means a function defined in one file can be accessed in another file (this is why function forward declarations work). It also means if a function is defined in more than one file, those definitions will conflict. In contrast, const variables (by default) have internal linkage, meaning they're only visible in the file in which they are defined. That means that they won't conflict with variables of the same name in other files.

      This is discussed in more detail at the start of chapter 4.

      • MSteven

        Hi Alex How getSquareSides() function is defined multiple times in these files?

        • Alex

          #including a header simply copies the header file into the including file at the point of inclusion. Since square.h includes a definition for getSquareSides(), that definition is copied into any file that #includes square.h. In this program, two files include square.h: main.cpp and square.cpp. So two files end up with a definition for getSquareSides(), and that causes issues.

          • MSteven

            You stated <a href="http://www.learncpp.com/cpp-tutorial/1-10a-header-guards/">here</a> that:
            <i>Note that even though square.h has header guards, the contents of square.h are included once in square.cpp and once in main.cpp.

            Let’s examine why this happens in more detail. When square.h is included from square.cpp, SQUARE_H is defined until the end of square.cpp. This define prevents square.h from being included into square.cpp a second time (which is the point of header guards). However, once square.cpp is finished, SQUARE_H is no longer considered defined. This means that when the preprocessor runs on main.cpp, SQUARE_H is not initially defined in main.cpp.

            The end result is that both square.cpp and main.cpp get a copy of the definition of getSquareSides(). This program will compile, but the linker will complain about your program having multiple definitions for identifier getSquareSides!</i>

            In my computer It's at compile time that i have an error on multiple definitions and I'd thought that even though square.h has header guards, the contents of square.h are included once in square.cpp and once in main.cpp like you mentionned in 1.10 section.

    • Rohan

      Thank you! it has helped a lot.

  • Sean Kelly

    When it comes to symbolic constants would you say the enumerators are a good way to work with them as well?
    This just came to mind when I was I had to look back as to why #define is not the best way to create
    symbolic constants.

  • Kate

    Const is most useful (and most often used) with function parameters:

    Making a function parameter const does two things. First, it tells the person calling the function that the function will not change the value of myValue. Second, it ensures that the function doesn’t change the value of myValue.

    That was from the tutorial. I don't understand how when using const in a parameter how you will later be able to use it an argument? Does this imply that the value of myValue was declared in main? Thank you so much for this wonderful tutorial.

    • Alex

      Not sure I understand your concern. You can pass a non-const argument to a function with a const parameter, and the function will treat the parameter as const. This doesn't affect the argument.

      For example:

  • Alan

    Hi, Alex
    First, thank you for the answer to my question asked in section 2.3 - Variable sizes and the sizeof operator two days ago.
    After I tried

    on my Code::Blocks, it showed a warning that stated "x" being redefined, no errors. As I run this program, it worked fine and it printed "56". I got two questions for this issue.

    1. Can the constants defined via object-like macros with substitution parameter be (inadvertently) changed in this way or is it just another example that some compilers don't follow the rule of standard C++?

    2. Suppose that the answer to the previous question is a yes and some compilers DO do (awkward) things like that. I remembered that in section 1.10 - A first look at the preprocessor, you said, "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." If I understand correctly, this states that before compilation, the computer (or something else that execute the preprocessor) scans all the preprocessor in the file while ignoring the actual codes. As a result, by the time both of them in this program are scanned, x shall be 6, for the second one says so. However the program printed out a 5 and a 6. Is there anything that I misunderstand?

    • Alex

      1) I think this is a case where your preprocessor is being permissive. You can undefine a macro value and then redefine it -- your preprocessor is probably assuming you wanted to do this and acting on your behalf.
      2) The preprocessor is just a text editor. So first it sees that you've defined x as 5. Then it encounters x in the next statement, and literally replaces x with '5'. Then later it sees that you've redefined x to 6. So when it encounters the next x, it literally replaces x with 6.

      Therefore, when the compiler compiles your code, it's compiling code that prints 5 in the first case, and 6 in the second case.

  • Charles

    Thanks for your website...nice job explaining C++.  But I do have a question...  In this section (2.9) you state:

    "Second, #defined values always have global scope (which we’ll talk more about in the section on local and global variables). This means a value #defined in one piece of code may have a naming conflict with a value #defined with the same name in another piece of code."

    Notice in particular the "GLOBAL SCOPE" you mention.

    But in section 1.10 you state:

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

    This seems to be saying that they are LOCAL in scope...I am confused by this, so if you could clarify please do.

    Thanks!

    • Alex

      Good question -- this is partly due to ambiguity around the term "global scope" and partly due to poor wording on my part. It's probably more intuitive to say #define values have file scope (they can be seen until the end of the file). When I said "another piece of code", I didn't mean another file, I meant code later in the same file.

      I've updated the lesson, clarified the wording, and added an example of how a conflict may occur within the same file. Hopefully this will make it clearer.

      Thanks for pointing this out!

  • Jarred

    Earlier in these lessons, weren't they saying to never define anything inside of a header file because it would make the program excessively large due to how the preprocessor functions? But, if that is the case, why did they define the constants, instead of having a forward declaration to define them in some other location?

    • Alex

      A few reasons:
      1) To keep the examples simple
      2) We haven't covered some of the basics needed to do this effectively. We talk more about this in chapter 4, once we've covered global variables.

      • Jarred

        So, to clarify, it really still is bad practice to do that, you just have to do that at this point because it would be too much to explain at this point?

        • Alex

          No, not bad practice. Just in doing so, you need to be aware of the tradeoffs. If you put your constant definitions in a header file, they will get recompiled into every code file the header is included from. That can lead to code bloat if there are a lot of constants and/or those constants are large. But assuming that's not the case, putting them in the header is easier, and sufficient.

  • maria

    in the code for calculating the circumference of a circle......what do i include in the main.cpp file? i mean mean the 2nd part of the code goes to the constant.cpp  file if i am not mistaken..   .....once again sorry for my stupid question new to programming

    Talking about this part of code....

    • Alex

      I'm not sure I understand your question. The constants you need are in constants.h, so by doing #include "constants.h", you're bringing those constants into your main.cpp file for use. That allows you to use constants::pi.

      • Maria

        But whenever I create a class file it automatically creates 3 files...main.cpp constant.h. And constant.cpp

        • Alex

          I'm not sure I understand what you mean by "class file".

          If you're asking me whether you need a main.cpp if you have a constant.cpp, the answer is no -- you really only need a main.cpp if you want to have one (and if it makes more sense to put your main function there than somewhere else).

  • TripleJski

    "Const variables act exactly like normal variables in every case except that they can not be assigned to, so there’s no particular reason they need to be denoted as special."  When I read this line, I can't help but think something's incomplete or a typo.  If not, please explain in detail if you could the thought process, because I'm just not understanding this.  As an aside, love the site, and I'm enjoying learning from you, keep up the good work Alex!

  • AmnesiaMaster28

    There's a typo that's bothering me.
    "When you are in the process of running your application, that is called runtime. During runtime, your program executes line by "
    I think you forgot a word there. 😛

Leave a Comment

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