Monday, January 17, 2011

Blocks (compound statements) and local variables

Blocks (compound statements)
A block of statements, also called a compound statement, is a group of statements that is treated by the compiler as if it were a single statement. Blocks begin with a { symbol, end with a } symbol, and the statements to be executed are placed in between. Blocks can be used any place where a single statement is allowed.
You have already seen an example of a block when writing the function main():
1int main()
2{ // start a block
3
4    // multiple statements
5    int nValue = 0;
6    return 0;
7
8} // end a block
Blocks can be nested inside of other blocks. As you have seen, the if statement executes a single statement if the condition is true. However, because blocks can be used anywhere a single statement can, we can instead use a block of statements to make the if statement execute multiple statements if the condition is true!
01#include <iostream>
02
03int main()
04{
05    using namespace std;
06    cout << "Enter a number: ";
07    int nValue;
08    cin >> nValue;
09
10    if (nValue > 0)
11    { // start of nested block
12        cout << nValue << " is a positive number" << endl;
13        cout << "Double this number is " << nValue * 2 << endl;
14    } // end of nested block
15}
If the users enters the number 3, this program prints:
3 is a positive number
Double this number is 6
Note that both statements inside the nested block executed when the if statement is true!
It is even possible to put blocks inside of blocks inside of blocks:
01int main()
02{
03    using namespace std;
04    cout << "Enter a number: ";
05    int nValue;
06    cin >> nValue;
07
08    if (nValue > 0)
09    {
10        if (nValue < 10)
11        {
12            cout << nValue << " is between 0 and 10" << endl;
13        }
14    }
15}
There is no practical limit to how many nested blocks you can have. However, it is generally a good idea to try to keep the number of nested blocks to at most 3 (maybe 4) blocks deep. If your function has a need for more, it’s probably time to break your function into multiple smaller functions!
Local variables
A variable’s scope determines who can see the variable, and how long it lives for. Variables declared inside a block are called local variables, and local variables have block scope (also called local scope). Variables with block scope can be accessed only within the block that they are declared in, and are destroyed as soon as the block ends. Consider this simple function:
01int main()
02{ // start main block
03
04    int nValue = 5; // nValue created here
05
06    double dValue = 4.0; // dValue created here
07
08    return 0;
09
10} // nValue and dValue destroyed here
Because nValue and dValue were declared inside the block that defines the main function, they are both destroyed when main() is finished executing.
Variables declared inside a block can only be seen within that block. Because each function has it’s own block, variables in one function can not be seen from another function:
01void someFunction()
02{
03    int nValue;
04}
05
06int main()
07{
08    // nValue can not be seen inside this function.
09
10    someFunction();
11
12    // nValue still can not be seen inside this function.
13
14    return 0;
15}
Variables declared inside nested blocks are destroyed as soon as the inner block ends:
01int main()
02{
03    int nValue = 5;
04
05    { // begin nested block
06        double dValue = 4.0;
07    } // dValue destroyed here
08
09    // dValue can not be used here because it was already destroyed!
10
11    return 0;
12} // nValue destroyed here
Nested blocks are considered part of the outer block in which they are defined. Consequently, variables declared in the outer block can be seen inside a nested block:
01int main()
02{ // start outer block
03    using namespace std;
04
05    int x = 5;
06
07    { // start nested block
08        int y = 7;
09        // we can see both x and y from here
10        cout << x << " + " << y << " = " << x + y;
11    } // y destroyed here
12
13    // y can not be used here because it was already destroyed!
14
15    return 0;
16} // x is destroyed here
Note that variables inside nested blocks can have the same name as variable inside outer blocks. When this happens, the nested variable “hides” the outer variable:
01int main()
02{ // outer block
03    int nValue = 5;
04
05    if (nValue >= 5)
06    { // nested block
07        int nValue = 10;
08        // nValue now refers to the nested block nValue.
09        // the outer block nValue is hidden
10    } // nested nValue destroyed
11
12    // nValue now refers to the outer block nValue
13
14    return 0;
15} // outer nValue destroyed
This is generally something that should be avoided, as it is quite confusing!
Variables should be declared in the most limited scope in which they are used. For example, if a variable is only used within a nested block, it should be declared inside that nested block:
01int main()
02{
03    // do not declare y here
04    {
05        // y is only used inside this block, so declare it here
06        int y = 5;
07        cout << y;
08    }
09    // otherwise y could still be used here
10}
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. A variable declared inside a block can only be used within that block (or nested sub-blocks). This can make the program easier to understand.
Summary
Blocks allow multiple statements to be used wherever a single statement can normally be used.
Variables declared inside blocks are called local variables. These variables can only be accessed inside the block in which they are defined (or in a nested sub-block), and they are destroyed as soon as the block ends.
If a variable is only used in a single block, declare it within that block.

No comments: