Search

2.4 — Introduction to local scope

Local variables

Function parameters, as well as variables defined inside the function body, are called local variables (as opposed to global variables, which we’ll discuss in a future chapter).

In this lesson, we’ll take a look at some properties of local variables in more detail.

Local variable lifetime

In lesson 1.3 -- Introduction to variables, we discussed how a variable definition such as int x; causes the variable to be instantiated (created) when this statement is executed. Function parameters are created and initialized when the function is entered, and variables within the function body are created and initialized at the point of definition.

For example:

The natural follow-up question is, “so when is an instantiated variable destroyed?”. Local variables are destroyed in the opposite order of creation at the end of the set of curly braces in which it is defined (or for a function parameter, at the end of the function).

Much like a person’s lifetime is defined to be the time between their birth and death, an object’s lifetime is defined to be the time between its creation and destruction. Note that variable creation and destruction happen when the program is running (called runtime), not at compile time. Therefore, lifetime is a runtime property.

For advanced readers

The above rules around creation, initialization, and destruction are guarantees. That is, objects must be created and initialized no later than the point of definition, and destroyed no earlier than the end of the set of the curly braces in which they are defined (or, for function parameters, at the end of the function).

In actuality, the C++ specification gives compilers a lot of flexibility to determine when local variables are created and destroyed. Objects may be created earlier, or destroyed later for optimization purposes. Most often, local variables are created when the function is entered, and destroyed in the opposite order of creation when the function is exited. We’ll discuss this in more detail in a future lesson, when we talk about the call stack.

Here’s a slightly more complex program demonstrating the lifetime of a variable named x:

In the above program, x’s lifetime runs from the point of definition to the end of function main. This includes the time spent during the execution of function doSomething.

Local scope

An identifier’s scope determines where the 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 not in scope will result in a compile error.

A local variable’s scope begins at the point of variable definition, and stops at the end of the set of curly braces in which they are defined (or for function parameters, at the end of the function). This ensures variables can not be used before the point of definition (even if the compiler opts to create them before then).

Here’s a program demonstrating the scope of a variable named x:

In the above program, variable x enters scope at the point of definition and goes out of scope at the end of the main function. Note that variable x is not in scope anywhere inside of function doSomething. The fact that function main calls function doSomething is irrelevant in this context.

Note that local variables have the same definitions for scope and lifetime. For local variables, scope and lifetime are linked -- that is, a variable’s lifetime starts when it enters scope, and ends when it goes out of scope.

Another example

Here’s a slightly more complex example. Remember, lifetime is a runtime property, and scope is a compile-time property, so although we are talking about both in the same program, they are enforced at different points.

Parameters x and y are created when the add function is called, can only be seen/used within function add, and are destroyed at the end of add. Variables a and b are created within function main, can only be seen/used within function main, and are destroyed at the end of main.

To enhance your understanding of how all this fits together, let’s trace through this program in a little more detail. The following happens, in order:

  • execution starts at the top of main
  • main‘s variable a is created and given value 5
  • main‘s variable b is created and given value 6
  • function add is called with values 5 and 6 for arguments
  • add‘s variable x is created and initialized with value 5
  • add‘s variable y is created and initialized with value 6
  • operator+ evaluates expression x + y to produce the value 11
  • add copies the value 11 back to caller main
  • add‘s y and x are destroyed
  • main prints 11 to the console
  • main returns 0 to the operating system
  • main‘s b and a are destroyed

And we’re done.

Note that if function add were to be called twice, parameters x and y would be created and destroyed twice -- once for each call. In a program with lots of functions and function calls, variables are created and destroyed often.

Functional separation

In the above example, it’s easy to see that variables a and b are different variables from x and y.

Now consider the following similar program:

In this example, all we’ve done is change the names of variables a and b inside of function main to x and y. This program compiles and runs identically, even though functions main and add both have variables named x and y. Why does this work?

First, we need to recognize that even though functions main and add both have variables named x and y, these variables are distinct. The x and y in function main have nothing to do with the x and y in function add -- they just happen to share the same names.

Second, when inside of function main, the names x and y refer to main’s locally scoped variables x and y. Those variables can only be seen (and used) inside of main. Similarly, when inside function add, the names x and y refer to function parameters x and y, which can only be seen (and used) inside of add.

In short, neither add nor main know that the other function has variables with the same names. Because the scopes don’t overlap, it’s always clear to the compiler which x and y are being referred to at any time.

Key insight

Names used for function parameters or variables declared in a function body are only visible within the function that declares them. This means local variables within a function can be named without regard for the names of variables in other functions. This helps keep functions independent.

We’ll talk more about local scope, and other kinds of scope, in a future chapter.

Where to define local variables

Local variables inside the function body should be defined as close to their first use as reasonable:

In the above example, each variable is defined just before it is first used. There’s no need to be strict about this -- if you prefer to swap lines 5 and 6, that’s fine.

Best practice

Define your local variables as close to their first use as reasonable.

Quiz time

Question #1

What does the following program print?

Show Solution


2.5 -- Why functions are useful, and how to use them effectively
Index
2.3 -- Introduction to function parameters and arguments

191 comments to 2.4 — Introduction to local scope

  • Kevin

    In the quiz, I am a little confused about how the function doIt assigns both a 1 and a 3. Does it occur in the function main where it says doIt(x)?

  • Silviu

    i wanna know why doesn't show the exact number after "." like 7.73 and just rounds the number at 7 ? thx

    • nascardriver

      Hi Silviu!

      Your @addfloat function returns an int. An int cannot store floating point numbers.

  • #include <iostream>
    void doIt(int x)
    {
        x = 3;
        int y = 4;
        std::cout << "doIt: x = "
        << x << " y = " << y << std::endl;
    }
    int main()
    {
        int x = 1;
        int y = 2;
        int z = 5;
        std::cout << "main: x = "
        << x << " y = " << y << std::endl;
        doIt(z);
        std::cout << "main: x = "
        << x << " y = " << y << std::endl;
        return 0;
    }

    z=5 if this value is being passed to doit()..
    i am thinking x"s value is 5 at first
    then x is given the value as 3;
    whether it prints 5 0r 3...
    and please explain the flow to understand

    • Alex

      You got it. When main calls doIt(z), z is evaluated to produce the value 5, which is passed to the function, and assigned to function parameter x.

      However, doIt() immediately assigns the value of 3 to x, so from that point forward, x is 3 (within doIt). This does not impact the value of main's x (which is still 1).

      In other words, the x in main and the x in doIt are not connected in any way.

  • Alex,

    I love learncpp.com!  I love it that you go over things is such detail!  Thank you!  

    There's something that I haven't been able to figure out.  DoIt is called withing main so x gets the main value of 1, not the doIt value of 3.  Then why does y get the doIt value of 4 instead of the main value of 2?  the only thing I can think of is that when doIt is called, it only gets (x), not (x,y).  But then why does the code reach back to doIt to get the y value of 4? Then it does reach back to doIt and gets the values of 3,4. After that, it goes back to the 1,2 values of main.  I know that the point of this exercise is to show the scope of functions, and I don't understand.

    • nascardriver

      Hi Robyn!
      main doesn't know about the variables in doIt and doIt doesn't know about the variables in main. To make things easier you could change the names of the variables in main and doIt independently

      This is the same exact program.
      Since doIt doesn't know about the variables in main, we can re-use the name "y" for a variable created in doIt. The same goes for "x".
      The avoid confusion I'm going to use a,b,x,y from here on.
      The doIt function knows two variables, "a" and "b". When doIt reaches it's end both "a" and "b" will disappear. "b" will always be initialized to 4. "a" will be initialized to a value specified by the caller.
      In our case this happens in line 16

      This will copy the value (Not the variable itself!) of "x" into "a".
      Since "a", "b" and "x", "y" have nothing to do with each other, because they were declared in different scopes(curly braces), we can change the values of "a", "b" without affecting "x", "y" and the other way around.

  • aditya

    hi there!
    you said that "functions, variables are created and destroyed often."
    what do you mean by destroyed? can you please elaborate ?

    thank you

    • Alex

      When the end of a function is reached, all variables defined inside the function are destroyed. "Destroy" means the variable is no longer usable, the memory that has been set aside for those variables is freed up for reuse.

  • seb

    hey i think your breakdown section at the end is missing some steps and has steps out of order...

    • Alex

      Can you provide some more detail about why you believe so?

      • Jake

        doIt() is called with argument 1
        doIt’s variable x is created and given value 1        in between this line and the next doIt  should print "x=1 y=4"
        doIt’s variable x is assigned the value 3

        this is in the solution part of the quiz

        • Alex

          Ah, yes. I updated the previous part of the quiz and forgot to update the latter part. Thanks for pointing this out!

          • Robert

            Hi, I think the solution steps are still in the wrong order for these lines:
            doIt’s variable x is created and given value 1
            doIt prints “doit: x = 1 y = 4”
            doIt’s variable x is assigned the value 3
            doit’s variable y is created and given value 4

            The variable y is actually created before doIt prints anything. The lines should be:
            doIt’s variable x is created and given value 1
            doit’s variable y is created and given value 4
            doIt prints “doit: x = 1 y = 4”
            doIt’s variable x is assigned the value 3

  • tracid1987

    I just cant understand why did you use argument at doIt(x)if its even not used in the function. isnt doIt() enough? without argument? or you put it there just to understand how it works? Thanks

    • Alex

      Yup, it's just there to test the user's knowledge about parameters vs local variables. It could just as easily been a local variable.

      I've updated the example and answer to actually use the parameter, as that's a bit less weird. Thanks for pointing this out.

  • 282475249

    Thanks for these tutorials! I actually have a quick question about scope.

    So let's say we have

    I suspect that this is more a question about how std::vector is implemented. What I believe is happening is that it's making a copy of the entire vector 'vect' each time. Does this still happen if vect is a vector of, say, some custom struct I made? Or a vector of vectors? Does everything get copied?

    • Alex

      Yes, it's making a copy of your temporary vector vect. And yes, this will happen even if vect is a vector of a custom class. Everything in the vector gets copied.

  • Rahul

    How can u use int 2times in a same function

    • Alex

      I don't understand what you are asking. Can you restate the question more clearly?

    • davis

      You can use 'int' as many times as you want in the same function for initialization. Read this properly: http://www.learncpp.com/cpp-tutorial/13-a-first-look-at-variables-initialization-and-assignment/

      • Bill

        You have to define what x and y are. I don't know what you're specifically talking about in 1.3 but you can use them as many times as you want in a function so the compiler knows you're dealing with integers.

  • My dear c++ Teacher,
    Please let me ask you following question to be sure:
    Is it true that variables are not created during compilation but during execution?
    With regards and friendship.

  • Tanya

    What is the meaning of these two lines in the quiz question?
    doIt() is called with parameter 1
    doIt’s variable x is created and given value 1

    Isn't x in doIt given the value 3?

    • Alex

      The main program calls doIt(x), and x has value 1, so doIt() is called with argument 1. When doIt is called, the x parameter is initialized with the value of 1 that was passed in as an argument.

      x in doIt _is_ given the value 3, but this happens later.

      • Sajid Ahamed

        The main program calls doIt(x), x has value "1"... Okay but from where does x get the value of 1? From the main()'s int, or from doIt()?

        And why/how does x get assigned the value 3?

        • Alex

          x was assigned the value of 1 previously in function main(). x is assigned the value of 3 inside function doIt() -- but because doIt() version of x is a local variable, main's version of x isn't affected.

  • My dear c++ Teacher,
    Please let me say, I do not see any benefit from that lines in programs are alternative shadowed.
    With regards and friendship.

  • My dear c++ Teacher,
    Please let me say that in program

    first comment it should be added following

    With regards and friendship.

  • jack clare

    hey,

    just wanted to ask why you include the math header?

    i ran the program without it and it works the same. i thought the math header was for more complex stuff like cos and sin?

    thanks

    • Alex

      Not sure, seems like something got pasted into the wrong place somehow. I've removed the math header and replace it with iostream, which is actually needed for std::cout and std::endl.

      Thanks for pointing this out.

  • Sebastian

    Hey Alex, why is x defined on line 4 in the function name and not inside the the function?

    • Alex

      When x is defined as a function parameter (before the function body), it means that we expect the caller of the function to provide a value for that variable.
      When x is defined inside the function body, it means that the function will be responsible for providing a value for that variable.

      That's really the only difference.

  • Igna

    Hey Alex, I have a question: In this example

    In the line 12

    Where is stored the value returned by add(x, y) and when is destroyed??

    thanks and great tutorial by the way!!

  • Ashraf

    i coded my calle function after the  int main fucntion , but after compiling it shows error . is that a wrong way of coding sir ?

    • Alex

      For now, put your other user-defined functions before main(). We'll show you how to deal with putting them below main (or in other files) in future lessons.

  • Devil Hand

    Does the variables x and y have different memory locations for different functions?
    If two different memory locations have the same name at one time, then how does the compiler knows which value to use?
    Does this has to do something with pointers?

    • Alex

      The memory locations for x and y will be distinct not only for different functions, but also for each function call! Consider the case where a function calls itself (this is called recursion). In such a case, the x and y for each function call had better be distinct, or one call to the function would step on the values from the previous call. We'll talk about this more in chapter 7.

      If it's ever the case that two variables (or functions) with the same name are in scope (can be used) at the same time, then you have a possible naming conflict. In some cases, there are precedence rules that govern which will get used. But if the compiler can't decide, it will give you a compile error and ask you to disambiguate. Fortunately, C++ contains a lot of ways to avoid this, so it doesn't happen very often as long as you follow best practices.

      This has nothing to do with pointers.

  • My dear c++ Teacher,

    Please let me explain the reason I consider you my c++ Teacher. It is that you explain exactly how a program works. For example, regarding program in quiz you state:
    "   main() is executed
        main’s variable x is created and given value 1
        main’s variable y is created and given value 2
        cout prints “main: x = 1 y = 2”
        doIt() is called with parameter 1
        doIt’s variable x is created and given value 1
        doIt’s variable x is assigned the value 3
        doit’s variable y is created and given value 4
        cout prints “doIt: x = 3 y = 4”
        doIt’s x and y are destroyed
        cout prints “main: x = 1 y = 2”
        main returns 0 to the operating system
        main’s x and y are destroyed

    Note that even though doIt’s variables x and y had their values assigned to something different than main’s, main’s were unaffected."

    With regards and friendship.

  • My dear c++ Teacher,
    Please let me add to quiz code, variable z = 5, and doIt(z), as follows:

    It works fine.
    With regards and friendship.

  • My dear c++ Teacher,
    Please let me make a remark.In second program's comments you are referred to variable x only.
    With regards and friendship.

  • Ed

    Hi Alex

    Thanks for these tutorials, they're great.

    For the question example:

    Is it necessary to put (int x) in void doIt(int x) at the start?

    Cant you set the function to automatically take the values for x and y specified within, without having to take 1 for x first and change it to 3?

    • Alex

      No, it's not necessary -- in fact, it would be better to declare x as a local variable inside the function (like y is) since the argument passed in is never used. But the point here is to make sure you understand how function parameters work at a basic level.

  • Hariharan

    Hey,the tutorial is really efficient and friendly.But please try to work with the ease of navigation.Currently its section 1.4 right,so it'll be much better if you could provide a next and previous button so that the user can go through the stuffs sequentially.Table of contents on the left hand side will also suffice.

  • Jim

    Alex,

    I'm rereading this lesson and I noted that you didn't mention that curly braces limit the scope of all local variables in C++. That's surprising since you didn't mention them at all. Why? This is the ideal lesson to teach this in?

    • Alex

      This section is just a first look. I haven't yet talked about what a block is, or how nested blocks work, so it's a bit difficult to explain without that. I cover this in more detail in chapter 4.

  • MechanicNeurons

    /*
    Alex. I hope you are earning well, I hope you are happy. I don't know you but I believe that you deserve a beautiful life.
    */

  • pandaMax

    Hey there! Just saw a little repetition in the explanations. At "Local scope prevents naming collisions", juste below the exemple, the sentence "Because x and y in main() have local scope, add() can’t see them." is written twice in a row.

    Thanks for your hard work!

  • Mohamed

    thnx for this website

  • Lampros Papalampros

    alex which is the font your are using for code? I cannot find a satisfactory font. I am leaning towards Consolas but I am not ok. 1Il are very similar and yours font looks ideal

  • harshahemanth

    Alex tell the last answer for this above questions in simple way

    • Alex

      I'm not sure what you mean. Are you asking for more detail about the answer to the quiz question? The answer steps through what happens. Is there something in particular you don't understand?

  • Trev

    Line 15: Why is doIt printed to console with no std::cout?Thnks

    • Alex

      doIt() is a function call. When the code gets to to line 15, it jumps to line 3, executes lines 3-8, and then returns back to line 16.

      The "doIt" output is printed on line 7 using std::cout.

Leave a Comment

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