Search

6.3 — Local variables

In lesson 2.4 -- Introduction to local scope, we introduced local variables, which are variables that are defined inside a function (including function parameters).

It turns out that C++ actually doesn’t have a single attribute that defines a variable as being a local variable. Instead, local variables have several different properties that differentiate how local variables behave from other kinds of (non-local) variables. We’ll explore these properties in this and upcoming lessons.

In lesson 2.4 -- Introduction to local scope, we also introduced the concept of scope. An identifier’s scope determines where an identifier can be accessed within the source code. When an identifier can be accessed, we say it is in scope. When an identifier can not be accessed, we say it is out of scope. Scope is a compile-time property, and trying to use an identifier when it is out of scope will result in a compile error.

Local variables have block scope

Local variables have block scope, which means they are in scope from their point of definition to the end of the block they are defined within.

Related content

Please review lesson 6.1 -- Compound statements (blocks) if you need a refresher on blocks.

Although function parameters are not defined inside the function body, for typical functions they can be considered to be part of the scope of the function body block.

The exception case is for function-level exception handling (which we cover in lesson 20.7 -- Function try blocks).

All variable names within a scope must be unique

Variable names must be unique within a given scope, otherwise any reference to the name will be ambiguous. Consider the following program:

The above program doesn’t compile because the variable x defined inside the function body and the function parameter x have the same name and both are in the same block scope.

Local variables have automatic storage duration

A variable’s storage duration (usually just called duration) determines what rules govern when and how a variable will be created and destroyed. In most cases, a variable’s storage duration directly determines its lifetime.

Related content

We discuss what a lifetime is in lesson 2.4 -- Introduction to local scope.

For example, local variables have automatic storage duration, which means they are created at the point of definition and destroyed at the end of the block they are defined in. For example:

For this reason, local variables are sometimes called automatic variables.

Local variables in nested blocks

Local variables can be defined inside nested blocks. This works identically to local variables in function body blocks:

In the above example, variable y is defined inside a nested block. Its scope is limited from its point of definition to the end of the nested block, and its lifetime is the same. Because the scope of variable y is limited to the inner block in which it is defined, it’s not accessible anywhere in the outer block.

Note that nested blocks are considered part of the scope of the outer block in which they are defined. Consequently, variables defined in the outer block can be seen inside a nested block:

Local variables have no linkage

Identifiers have another property named linkage. An identifier’s linkage determines whether other declarations of that name refer to the same object or not.

Local variables have no linkage, which means that each declaration refers to a unique object. For example:

Scope and linkage may seem somewhat similar. However, scope defines where a single declaration can be seen and used. Linkage defines whether multiple declarations refer to the same object or not.

Related content

We discuss what happens when variables with the same name appear in nested blocks in lesson 6.5 -- Variable shadowing (name hiding).

Linkage isn’t very interesting in the context of local variables, but we’ll talk about it more in the next few lessons.

Variables should be defined in the most limited scope

If a variable is only used within a nested block, it should be defined inside that nested block:

By limiting the scope of a variable, you reduce the complexity of the program because the number of active variables is reduced. Further, it makes it easier to see where variables are used (or aren’t used). A variable defined inside a block can only be used within that block (or nested blocks). This can make the program easier to understand.

If a variable is needed in an outer block, it needs to be declared in the outer block:

The above example shows one of the rare cases where you may need to declare a variable well before its first use.

New developers sometimes wonder whether it’s worth creating a nested block just to intentionally limit a variable’s scope (and force it to go out of scope / be destroyed early). Doing so makes that variable simpler, but the overall function becomes longer and more complex as a result. The tradeoff generally isn’t worth it. If creating a nested block seems useful to intentionally limit the scope of a chunk of code, that code might be better to put in a separate function instead.

Best practice

Define variables in the most limited existing scope. Avoid creating new blocks whose only purpose is to limit the scope of variables.

Quiz time

Question #1

Write a program that asks the user to enter two integers, one named smaller, the other named larger. If the user enters a smaller value for the second integer, use a block and a temporary variable to swap the smaller and larger values. Then print the values of the smaller and larger variables. Add comments to your code indicating where each variable dies. Note: When you print the values, smaller should hold the smaller input and larger the larger input, no matter which order they were entered in.

The program output should match the following:

Enter an integer: 4
Enter a larger integer: 2
Swapping the values
The smaller value is 2
The larger value is 4

Show Solution

Question #2

What’s the difference between a variable’s scope, duration, and lifetime? By default, what kind of scope and duration do local variables have (and what do those mean)?

Show Solution


6.4 -- Introduction to global variables
Index
6.2 -- User-defined namespaces and the scope resolution operator

267 comments to 6.3 — Local variables

  • Waldo Lemmer

    I think this lesson needs an explanation of lifetime. Lifetime is only mentioned twice:

    > In most cases, a variable’s storage duration directly determines its lifetime.

    > Its scope is limited from its point of definition to the end of the nested block, and its lifetime is the same.

    ...but never explained

    ---

    What does it mean for a variable to be "created" or "destroyed"? I don't quite understand storage duration or lifetime.

    • Alex

      I added a link to the lesson where we discussed what a lifetime is.

      A variable is "created" when memory is allocated for it. A variable is "destroyed" when the memory allocated for that variable is no longer valid.

      Lifetime is simply the time between creation and destruction, much like our lifetime is the time between our birth and death.

      Storage duration is the property that the compiler uses to determine how to create and destroy variables. For local variables, these have automatic duration, which means the compiler will create these when they enter scope and destroy them when they leave scope. Other types of variables have different storage durations, which changes when they are created or destroyed. You'll see examples of this when we cover global variables shortly.

      • Waldo Lemmer

        Ah, now I get it! I thought all (non-dynamic) variables are stored in the executable and therefore don't need memory allocated. Thanks!

  • SHORIFUR RAHMAN

    #include<iostream>
    using namespace std;

    int getValue()
    {
        cout <<"Enter a  integer:" <<'\n';
        int x {};
        cin >> x;
        cout << " Enter a larger integer:"<<'\n';
        int y {};
        cin >> y;
        
        return x,y;
    }
    int chakeIn(int x, int y)
    {
        if( x<y)
        cout << getValue <<'\n';
        return getValue();
    }

    int main ()
    {
        int x{};
        int y{};
        
    cout << getValue ()<< '\n';
    cout <<  chakeIn  <<'\n';
    cout <<"swap value " << '\n';
    int temp{y};
    y = x;
    x = temp;

    cout<<"The smallest number is =" << x <<'\n';
    cout << "The largest number is =" << y <<'\n';
    return 0;
    }

    whats wrong with it?

    • H2Oaddict

      01. In function getValue(), the comma operator evaluates the left operand(x in your case) and right operand(y in your case) and return the evaluation of the right operand which results in y. Thus only Y is returned.
      reference: (https://www.learncpp.com/cpp-tutorial/comma-and-conditional-operators/)

      02. Function getValue()'s return value is used as an insertion to the console, where it should have been used to assign x and y respectfully. reference: (https://www.learncpp.com/cpp-tutorial/function-return-values/)

      03. Calling a Function without a parenthesis leads to undefined behaviour. The output might not appear as intended.
      reference: (https://www.learncpp.com/cpp-tutorial/introduction-to-function-parameters-and-arguments/)

      CORRECTION

      ```C
      #include<iostream>
      using namespace std;

      int getSmallerInteger()
      {
          cout << "Enter a  integer:" << '\n';
          int x{};
          cin >> x;
          

          return x;
      }

      int getLargerInteger()
      {
          cout << " Enter a larger integer:" << '\n';
          int y{};
          cin >> y;
          
          return y;
      }

      bool chakeIn(int x, int y)
      {
          return (x > y);
      }

      int main()
      {
          int x{ getSmallerInteger() };
          int y{ getLargerInteger() };

          
          if (chakeIn(x, y))
          {
              cout << "Swapping the value\n";
              int temp{ y };
              y = x;
              x = temp;
          }//temp is out of scope
          
          cout << "The smallest number is =" << x << '\n';
          cout << "The largest number is =" << y << '\n';
          return 0;
      }//x and y goes out of scope

      ```

  • Jay

    Hello everyone. Complete newbie here. After looking at the solution of question 1, I am just curious to check if my solution works as well or not:

    Will this work or do I need to follow the steps in the solution?
    Thanks in advance.

    • Armando IG

      Hello, I'm a fellow student but if you allow me to reply, your code does tell you which one is the smaller value, until there you got it right, but the question is asking you to also tell which one is the larger value, e.g:

      Enter an integer: 4
      Enter a larger integer: 2
      Swapping the values
      The smaller value is 2
      The larger value is 4

      So you are only missing the part where you assure that the code prints correctly which one is the larger value and which one is the smaller.

      As a hint, you can use a block within the "if" to do so:

      I hope this helps

      • Jay

        Thanks for your explanation. I have a pretty good grasp of the if statements so making those changes were quite easy. The thing that trips me up in the solution is this part:

        especially why we created a new integer temp. While I tried doing this in my IDE:
        larger = smaller; smaller = larger;
        I didnt get the desired result but I just cant understand the reason behind it. I mean we initialized temp with larger so why cant we directly assign larger to smaller instead?
        Sorry if this seems very basic but I just can't wrap my head around this.

        • Armando IG

          I see... Imagine your variables "larger" and "smaller" as glasses of the same size, imagine that "larger" is full of juice and "smaller" is full of water, in order to fill one with the content of the other glass you first need to empty it.

          The moment you do "larger = smaller" you empty the juice to fill it with water, so if you didn't save the juice into another glass (aka temp) you lose it forever.

          Therefore when you do "smaller = larger" larger now has water, so smaller will not have the juice it was supposed to have. But if you have "temp" filled with the juice then you can fill "smaller", that's why we do "smaller = temp".

          It is about the order you do things since C++ does stuff from top to bottom as a list of instructions. First fill "temp" with the juice in "larger", then empty the water in "smaller" into "larger", finally empty the juice in "temp" into "smaller".

          This is a rough example, since the program actually copies data instead of "filling" but I hope I explained myself correctly

          • Jay

            That is a great example Armando. It made perfect sense to me and cleared a lot of things that I was struggling with. Thanks a lot.

            • Armando IG

              My pleasure, Jay. I like how you need to understand something before doing it yourself, I'm also that way and that's how we learn.
              Wish you luck!

  • Hi

    If outside variables are accessible within nested blocks, then why does this:

    work?  Isn't it defining x twice?

  • mark brain

    My code using functions:

    #include <iostream>

    void printResult(int smaller, int larger)
    {
        std::cout << "The smaller value is " << smaller << '\n';
        std::cout << "The larger value is " << larger;
    }

    void checkIntegers(int smaller, int larger)
    {
        int temp{};
        if (smaller > larger)
        {
            std::cout << "Swapping the values" << '\n';
            temp = smaller;
            smaller = larger;
            larger = temp;
        }
        printResult(smaller, larger);
    }
    void getInput()
    {
        int smaller{};
        int larger{};
        std::cout << "Enter an integer: ";
        std::cin >> smaller;
        std::cout << "enter a larger integer: ";
        std::cin >> larger;
        checkIntegers(smaller, larger);
    }
    int main()
    {
        getInput();
        return 0;
    }

  • joshie

    How's this?

    • Bob

      Did you try it?

      If a user enters 3 for larger and 5 for smaller, then when you get to your swap at best the "new" definition of larger will make that 5, but then in the next line smaller gets defined as larger, so it would be 5 as well.
      And then the whole swap becomes pointless, since it's all in the if (larger<smaller) block you cout larger as the smaller value and smaller as the larger value.

      • joshie

        Thanks for the prompt reply!

        That completely blew over my head, I didn't consider that haha.

        Is this better?

  • nav

    • Bob

      You realize that doesn't actually swap any values, you're just determining which of two numbers is larger or smaller.

      Let alone the convoluted use of the count variable.

  • Cristopher Klingensmith

    For question one I did what it asked successfully but my code for swapping the numbers looks a lot different.
    Here's all my code

    #include <iostream>

    int main()
    {
        std::cout << "Please enter an integer: ";
        int smaller{};
        std::cin >> smaller;

        std::cout << "Please enter another integer: ";
        int larger{};
        std::cin >> larger;

        if (smaller > larger)
        {
            smaller -= larger;// assuming smaller = 15 larger = 5, now smaller = 10
            larger += smaller;// smaller = 10 larger = 5, now larger = 15
            smaller = (larger - smaller);//smaller = 10 larger = 15, now smaller = 5
            std::cout << '\n' << "The smaller value is " << smaller << '\n';
            std::cout << "The Larger value is " << larger << '\n';
        }
        else
        {
            std::cout << '\n' << "The smaller value is " << smaller << '\n';
            std::cout << "The Larger value is " << larger << '\n';
        }

        return 0;
    }// larger and smaller die here

    • TomCruise

      You are actually making the code harder yourself operator - and + are unnecessary in this case. But you made it yourself which is great. Just do simplify your code next time so you can understand it better and others too.

  • J34NP3T3R

    QUESTIONS : "Local variables have no linkage"

    int main()
    {
        int x { 2 }; // local variable, no linkage

        {
            int x { 3 }; // this identifier x refers to a different object than the previous x

            // 1.) Does this mean the previous x is still "2" outside of this block and whatever changes we make to x here only applies to the x here ?
            // 2.) we can no longer reference the previous x here ? because we defined a new x to be used here.
        }

        return 0;
    }

  • I naturally took Question 1 to the next level here thanks to this book! I love how chronological everything is! Topics build on themselves very well. I could move this function to a separate .cpp file to clean up the main.cpp and add a .h file to declare the "printResult" function I suppose.  

  • Chayim

    How is smaller swapped to be smaller by =temp ? How does 'smaller = temp'  work? temp is not smaller (the original smaller because it’s swapped to larger the original smaller) it’s larger.

    • bob

      Let's say that the use inputs 7 for smaller, and 2 for larger.  The swap is only called if the number entered for smaller is larger

      Temp is a new variable which holds the original value of larger, therefore Temp = 2
      We now set larger to the value that was entered as smaller, so larger =7
      We can now set smaller to the original value of larger, which was held by Temp - smaller =2

      The values of the variables have now been swapped.

  • Waldo Lemmer

    Thanks for the great lesson! Just one correction:

    Second code snippet, line 7:

    The last "here" should be removed.

    P.S. I think I want to stop reading comments. I've spent an hour and a half on this lesson, most of which is spent going through page 4's comments :(

  • Q.1

  • yeokaiwei

    Just some weird feedback.
    Since {} highlights a block, initializing a variable int {7}; seems to suggest that 7 dies within its {}.

  • Hali

    This is my take on question one, i feel like mine is unnecessary long compare to your solution but my focus point is that i want to keep the main function clean which i feel like i have achieved that goal.

    • nascardriver

      - "\n" is a string (expensive), '\n' is a character (cheap)
      - Line 23-26 and 33-36 are identical, you can move them into a separate function to avoid code duplication.

      Otherwise good job :)

  • Chris Alexander

    How is this?

  • 3li Mot 3alim

    How can multiple declarations refer to the same object?

  • Yousuf

    I expected to read some lines about one definition rule or ODR in the light of local variables.

  • Uy Ph

    Hi. I'm really confused on the linkage versus scope part. According to this code:

    the two x's refer to different objects, but are they not in the same scope, considering x{2} can also be seen in the nested block? This should make them two collided identifier names though?

  • Tony

    Question 2, almost there!

    btw, these questions are so nice! Make me want to learn more every time :P

    • nascardriver

      The point of swapping the integers is that you don't need to repeat line 19/23. This line is unconditional and should be moved out of the if-statement.

  • Tony

    Hi again!
    Why does this work:

    And this one not?

    This one ^ is considered double definition, but in the first example it's not. Is that because of blocks? What's the reason? Thank you :)

  • Max

    Hello, I'm really stuck on the fact that local variables don't have linkage, especially with your example:

    Is it because when we declare x here it also defines it and so both local vars can never refer to the same object?

    and are there any other reasons?

  • Math

    Hello!
    This is my code for the first question but I noticed that it prints twice like this:
    Enter an integer: 1
    Enter a larger integer: 0
    Swapping the values
    The smaller value is 0
    The larger value is 1
    The smaller value is 0
    The larger value is 1

    This is my code:

    However, I noticed that when I run the solution you provided to the question it runs fine.
    I added an 'else' statement so the rest of my code looks like this

    It then worked as expected, only printing once.

    I couldn't figure out why my code needed an 'else' statement and yours didn't, can you guys please explain?

    • nascardriver

      You're printing once in line 19,20 and then again in line 23,24.
      Line 23,24 aren't affected by your if-statement. See lesson 4.10.
      When you add an `else`, line 23,24 will only execute if the condition in line 13 was `false`.

      Line 19,20 and 23,24 are identical. If you repeat code, you're doing something wrong. You can remove line 19,20, because you're already printing in line 23,24. The printing of the values is unconditional.

      • Math

        Ooooh my bad sorry, I haven't realized that this was the difference I thought both codes were identical.

      • Math

        I am sorry one more question, I know it is best practice to separate your code into functions. So here would it have been better if I declared a function to get the integer value from the user or is it unnecessary in this case since we're only asking the user to enter an integer twice?

  • Nahin

    Do those 'multiple declarations' in the sentence below refer to the forward declarations of a non-static function in as many files as we want?

    > Linkage defines whether multiple declarations refer to the same object or not.

  • AE35_Unit

    Thanks Nascar, I went back and looked at all your recommendations, they really helped. I can't thank you enough. My code looks better and proper now.  AS far as where I am with studying, I've just been going straight through this lesson plan from the beginning.  I've seen tutorial videos on the youtube and would based on the one C++ class I had at university I would say I'm about one and 1/3 semesters.  I just enough about classes to creates small and easy ones so I'm looking forward to your sections on the subject. Also I used pointers and references pointers in the above, I try to use them if I'm able so as to get a more solid understanding of them as well. I see that the pointers section are in the near future.  Right now I'm at 6.9 Why global variables are evil, and after I finish all of 6 I'll probably go back for a bit of review.  Again, thanks for all your help. Cheers.

  • AE35_Unit

    Again trying to utilize concepts learned from previous chapters.  This could be written a lot faster and easier to read but I find that by using previous lessons I get a better grasp on the course's teaching's.

    ae35unit.cpp

    main.cpp

    • nascardriver

      If you let me know how much of C++ already know by stating a chapter number (eg. "I know C++ up to, and including, chapter P") I can give you better suggestions.

      Still going with your lander huh? That's nice, keeps the fun up :)

      - Missing #include <cstdint>
      - `int_fast16_t` should be `std::int_fast16_t`
      - By using pointers, you're allowing the caller to pass in a `nullptr`. This can be avoided by using `gsl::not_null` from the guidelines support library or by using references.
      - If you can, initialize variables (`temp`).
      - main.cpp:16: `ae` is not an object, it has no lifetime, only a scope. We talk about the scope later in this chapter.
      - main.cpp:23: There's no need for a global namespace selector here.

      If you weren't programming a lander, here are some extra notes that I don't want to let go unnoticed:
      - Don't restrict integers unless you have a reason to (Your reason is speed). Plain `int` is fine most of the time.
      - Don't use `std::endl` unless you have a reason to (Your reason is that logs are critical).

Leave a Comment

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