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

  • michael oska

    hello
    as far as i know c++ program start executing from the main function not the top part
    as you said:
    "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"
    maybe i m wrong i just want to make sure.
    thank you

  • Thank you!
    This learn is good!

  • Mathari

    Hello, I was wondering here if this is the best way of writing this add function?? Or is it better to just return x+y; which one is preferred when writing code in real life ??

    • nascardriver

      is better. For `int`s, there is no difference, and for other types the optimizer will most likely make the 2 versions identical. But if we don't trust the optimizer, then the version with `z` has to create another `int`, whereas the version without `z` is guaranteed to not create any temporaries. You'll understand this better once you learn about classes and constructors.

      This doesn't mean you should write everything in a single line. If the calculation is too complex, use variables to make it easier.

    • James Clarks

      You dont have to worry about how better you write the code, by the time you will complete this tutorial you will automatically start writing short and effective code, I have seen many beginners doing same thing, they just care about short code, and not about breaking code in chunks and understanding each and every single thing, thats happening on the perticular snippit.

  • rancho

    how value of x has been updated to 3 in fun doIt() ,while it's a call by value in function

  • Typo

    doIt‘s parameter x is created (AND) initialized with value 1
    doIt’s variable y is created and initialized with value 4

  • sami

    I think objects are created and initialized ,after their definitions, during run time. Shouldn't it be "no sooner than" instead of "no later than" in your sentence below?

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

  • variables within the function body are created

    "... and variables within the function body are created and initialized at the point of definition."

    Hi,
    As I recall from the previous lessons, it is said that variables are created and initialized at the run time (being allocated memory address). However, here you said something different:
    they are created and initialized at the point of definition.

    Here you also mentioned that :Note that variable creation and destruction happen when the program is running (called runtime), not at compile time.

    But what you said (with quotes above) implied that creation and initialization are done in the compile time(during definition) .

    • nascardriver

      "created and initialized at the point of definition" doesn't imply compile-time.

      We talk about "definition" as a concept in a programming language, but also as something that happens to a variable. `i` and `j` are defined in line 1 and 3 when we talk about code. When we talk about the program, `i` is define before we print "hello", `j` is defined after we print "hello".

      So, we have a definition in code. Code is just text, nothing actually happens. Then we have the definition at run-time.

  • Upto this lesson i'm still not sure about the job of "void". It doesn't return a value so how could it have worked under main()?

    • nascardriver

      It's up to the caller what they do with a returned value. Even if a function returns a value, the caller can discard it.

      If you don't intend on returning a meaningful value from a function, you declare it `void`. That way the caller knows that the function doesn't return anything, your code is simpler, and your program is faster.

    • Ethan

      The job of void is to do things that do not require a return value. Here is an example:

      The following code defines a void function Something() which simply prints out a statement. It does not return a value, meaning when the function is called, there is nothing represented by the function. It is just doing... something.

      In this example, the function would not work without its return type:

      In the above code, the function Check() is of type bool. This means that it returns a value that is either true or false. In this case, when check is called, it will return the value true. When main calls it later, the return value is being compared to the value true in the if statement. Since check returns true, it is the same thing as saying

      (Note that the == true is unnecessary, considering you can just type if(Check()) which translates to if(true).)

      However, if the above function Check() returned void (nothing), then it wouldn't compile. Let's pretend it compiles though, then it gets to the if statement in main, but it's a void function, so there is nothing to compare in the if statement because there is nothing returned by Check() if it is void.

      Hope that helps! I remember return values made no sense to me when I was learning. I later learned it was because I did not really know what a return value was. Void functions are useful when you don't have to return anything. Typically for print statements or for settings values equal to other values. (Although you can't technically do that through functions at this point in the lessons)

      • Yes! Actually with the example of bool that you wrote it makes much more sense! Thank you so much for the detailed answer, it really helps me! Have a good day.

  • José

    Lets see if I understood it.
    In the "main()" function, "doIt(x)" takes the value "1" because it is callead as "doIt(x)" instead of "doIt()"? Or is there another reason I didn't get?

    • nascardriver

      `doIt` cannot be called with `doIt()`, we have to give it something. We give it `x`, and `x` has the value 1.
      If we called it with `doIt(y)`, it'd get the value 2, and if we called it with `doIt(9000)`, it'd get the value 9000.

  • Hi Sires!

  • prince

    In programing where does the execution starts? does it starts on the first statement inside function "main" or in the first statement in the first function?

  • sexy

    i need help with this code

  • Chayim

    In the quiz, in function doit x is initialized to 3, so why when function doit is called in main it’s x is main’s initialization 1 until it executes the second function in doit?

    • `x` in `doIt` is initialized by the caller, in this case in `main` line 19. The `x` in `main` is not the same `x` as the `x` in `doIt`, they could have different names.
      In line 19, the value of `main`'s `x` is used to initialize the `x` of `doIt`.
      Line 8 changes the value of `doIt`'s `x` to 1, but it doesn't affect `main`'s `x`, because that's a different variable that just happens to have the same name.

      • Chayim

        I know that x in Main and x in doIt are not the same. My question was that doIt's x was already declared in it's own function as 3, so why when it's called in main it uses main's x and not it's own declared x as 3?
        I thought myself that when main calls doIt function doIt is being executed in order by it's two phases at the first part of it's function it is not declared yet so it uses main's x declared as 1, and only at it's second phase of it's function it is declared as 3, but my question was that why is it this way, because it's declared in this function as 3 so even at the beginning of the function it should not use main's x but it's own.

        • > it's declared in this function as 3
          It's not. It's declared in line 3 and will be initialized whenever `doIt` is called.

          Line 8 is an assignment. It changes the value of `x`.

          • Chayim

            “In this function” I meant in beginning when function doIt is declared, so why when doIt is called in main it uses main’s x and not it’s own?

            • nascardriver

              That's because `main` calls it with

              When you call a function that has a parameter, you have to pass in a value. It can't be called like

              • Chayim

                The clear answer of my question is:
                because the compiler compiles the contents of code files sequentially like it’s stated in chapter 2.7, so when the compiler compiles the function ‘doIt’ the it’s not assigned yet until next section of the function that assigns it, so it uses main’s x.

                • Chayim

                  The clear answer of my question is:
                  because the compiler compiles the contents of code files sequentially like it’s stated in chapter 2.7, so when the compiler compiles the function ‘doIt’ it’s not assigned yet until next section of the function that assigns it, so it uses main’s x that was assigned before.

  • Dan

    Quick scroll through the comments and didn't see this question. I apologize if it's a repeat.  Is there a best practice when naming a parameter variable and the argument variable passed to it? Even though they can have the same name like in the example demonstrating scope, it seems to me that it could be a source of confusion to anyone reading your code if they did.

    • If they describe the same thing, they should have the same name. `x` and `y` are bad names, unless they're used for coordinates where they're well established names.
      Local variables are a feature of many programming languages, anyone who knows a language will understand them.
      If this is your first language, it's ok to be confused, it'll pass.

  • Jose

    Hi, when explaining the quiz code at the end I think
    "doIt‘s x and y are destroyed" and
    "main‘s x and y are destroyed"
    should have the identifiers in reverse order to keep consistency with what´s been explained previously.

  • Singh

    I am getting below the result of running a shared program. Just curious to know what made the malfunction while running below code whereas its working fine with other two options.

    OUTPUT:
    "Please add both numbers.
    A: 5
    B: 6
    Addition of both 5 and 6 is 116296576

    Program Finished."

    [code]
    #include <iostream>

    int addfunction(int x, int y)
    {  
       std::cout<<"Addition of both " <<x <<" and " <<y <<" is " <<x+y;
       /* why can't we use above where as both the belw sections works fine.
        instead of
        int c{x+y};
        return c;
        OR
        retrun x+y; */
    }

    int main()
    {  
        int a, b;
        std::cout<<"Please add both numbers.\n";
        std::cout<<"A: ";
        std::cin>>a;
        std::cout<<"B: ";
        std::cin>>b;
        
        std::cout<<addfunction(a,b);

        std::cout<<"\n\nProgram Finished.";
        
    return 0;
    }
    [\code]

    • Arman

      Mistake 1:type of addfunction function must be void.because it doesnt return any value and just print sth on screen.
      Mistake 2:you shouldn't use cout<<addfunction(a,b); because addfunction doesnt return any value.you should simply call the function itself without cout.

  • Prateek

    In second snippet(above ) :

    int add( int x , int y)            // x and y created and initialized here

    I am a bit confused here. I understand that 'x' and 'y' has been created but how is it initialized?
    Here 'x' and 'y'has not been assigned any value.

    Please do reply

  • Cade

    Under the local scope section it says: “Here’s program” instead of “Here’s a program” or “Here is a program”. Not try a be annoying just wanted to help.

    Have a Good Day:)

  • Michael Johnston

    Great explanation for people new to programming; however, as someone coming from another language where, by default, parameters are passed by reference, I got the quiz wrong. It might be helpful to include another ‘for advanced users’ snippet on this which states this explicitly.

    • Hi!

      Which language is it you're talking about? I only know languages where objects or arrays are passed by reference by default, but none where all arguments are passed by reference.

    • Dirk de Klerk

      You seemed to have skipped 2.3. He explicitly states that when arguments are passed to parameters when calling functions it is "passed by value". He even wrote it in bold.

  • Saurabh

    https://www.learncpp.com/cpp-tutorial/introduction-to-local-scope/

    This ensures variables can not be used before the point of definition (even if the compiler opts to create them before them).

    I think that this should be ... compiler opts to create them before then

    (then instead of them)

  • Juan

    The page is very good, I would just say: they can relax with the use of "astute readers". It sounds to differentiate groups of people, according to their qualities (which leads to discrimination). If this is a page to learn something without prior knowledge, it is not necessary to assume anything that has not been explained. Perhaps, instead of astute readers, less ambiguous explanations would be needed. That is why the work of rewriting parts of the tutorials, to update their content, is a good practice. Thanks for that hard work!

  • Hana

    The output of the solution's "doit" is "doIt" with a capital I instead.

  • Wilson

    First three code snippets has a small typo here:

    int z{ x + y; }

    (should be int z{ x + y };)....

    otherwise, great guide, Alex. Thanks!

  • GG

    in another example:  main‘s b and z are destroyed  should be.... main‘s b and a are destroyed

  • Hans

    Helo there, first of all thanks for making this site. I learn so much here. I wanna ask something about the answer for this quiz. Why does the variable x in void doIt has value of 1? void doIt(int x) didn't give it value so shouldn't it become unpredictable?

  • HEY
    if nothing is initialized in int x= {nothing is assigned here to x}
    what will be the value given by compiler to X???

  • Kio

    Hi Alex,

    Maybe some kind of suggestion. What do you think,
    using same example below this

    additional print address of the variable x and y (so beginners can see, that their addresses are different). Or maybe this is confusing?

Leave a Comment

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