章节目录

局部变量

本节阅读量:

局部变量是在函数内定义的变量(包括函数参数)。

在之前,我们还介绍了作用域的概念。标识符的作用域确定了可以在源代码中访问标识符的作用域。当一个标识符可以被访问时,我们说它在作用域内。当标识符不能被访问时,我们说它超出作用域。作用域是编译时属性,当标识符超出作用域时,尝试使用它将导致编译错误。


局部变量具有代码块作用域

局部变量具有代码块作用域,这意味着从定义点到定义它们的代码块的末尾都在作用域内。

1
2
3
4
5
6
7
int main()
{
    int i { 5 }; // i 进入作用域
    double d { 4.0 }; // d 进入作用域

    return 0;
} // d 和 i 离开作用域

尽管函数参数没有在函数体内定义,但对于一般函数,它们可以被视为函数体内作用域的一部分。

1
2
3
4
5
6
7
int max(int x, int y) // x 和 y 进入作用域
{
    // 将x与y的最大值赋值给max
    int max{ (x > y) ? x : y }; // max 进入作用域

    return max;
} // max, y, x 离开作用域

作用域内的所有变量名都必须唯一

变量名在给定作用域内必须唯一。考虑以下程序:

1
2
3
4
5
6
7
8
9
void someFunction(int x)
{
    int x{}; // 编译失败,x与参数名称冲突
}

int main()
{
    return 0;
}

上面的程序不能编译,因为函数体中定义的变量x和函数参数x具有相同的名称,并且两者都在相同的块作用域内。


局部变量具有自动存储期(automatic storage duration)

变量的存储期(storage duration)规则,决定了何时以及如何创建和销毁变量。在大多数情况下,变量的存储期规则直接决定其生命周期。

例如,局部变量具有自动存储期,这意味着它们在定义点创建,在定义它们的代码块末尾销毁。例如:

1
2
3
4
5
6
7
int main()
{
    int i { 5 }; // i 创建与初始化
    double d { 4.0 }; // d 创建与初始化

    return 0;
} // d i 在这里被销毁

因此,局部变量有时称为自动变量( automatic variables)。


嵌套代码块中的局部变量

局部变量可以在嵌套块内定义。这与函数体块中的局部变量相同:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int main() // 外围代码块
{
    int x { 5 }; // x 进入作用域,并被创建

    { // 内层代码块
        int y { 7 }; // y 进入作用域,并被创建
    } // y 离开作用域,并被销毁

    // y 无法再被使用,因为离开了作用域

    return 0;
} // x 离开作用域,并被销毁

在上面的示例中,变量y在嵌套代码块中定义。它的作用域从定义点到嵌套块的末尾,并且其生命周期也相同。因为变量y的作用域限制在定义它的内部块中,所以它在外部块不可访问。

请注意,嵌套块被认为是定义它们的外部块作用域的一部分。因此,可以在嵌套块内看到外部块中定义的变量:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <iostream>

int main()
{ // 外围代码块

    int x { 5 }; // x 进入作用域,并被创建

    { // 内层代码块
        int y { 7 }; // y 进入作用域,并被创建

        // x 与 y 都在作用域中
        std::cout << x << " + " << y << " = " << x + y << '\n';
    } // y 离开作用域,并被销毁

    // y 无法再被使用,因为离开了作用域

    return 0;
} // x 离开作用域,并被销毁

局部变量没有链接属性

标识符具有另一个名为链接(linkage)的属性。标识符的链接属性决定了该名称的其他声明是否引用同一对象。

局部变量没有链接属性,这意味着每个声明都是唯一的对象。例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int main()
{
    int x { 2 }; // 局部变量 无链接

    {
        int x { 3 }; // 这里的x,与外围的x,不是同一个对象
    }

    return 0;
}

作用域和链接性似乎有些相似。然而,作用域定义了单个声明的可见和使用位置。链接性定义多个声明是否引用同一对象。

在局部变量的上下文中,链接性看不出来什么价值,但在接下来的几节课中更多地讨论它。


变量应在最有限的作用域内定义

如果变量仅在嵌套块中使用,则应在该嵌套块中定义:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <iostream>

int main()
{
    // 不要在这里定义y

    {
        // y 只在这个代码块内使用,所以在这里定义
        int y { 5 };
        std::cout << y << '\n';
    }

    // 后续不再使用y,所以没有必要能够看到y的定义

    return 0;
}

通过限制变量的作用域,可以减少程序的复杂性,因为同时能访问到变量的数量减少了。此外,更容易查看在哪里使用(或不使用)变量。在块中定义的变量只能在该块(或嵌套块)中使用。可以使程序更容易理解。

如果外部块中需要变量,则需要在外部块中声明:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

int main()
{
    int y { 5 }; // 在这里定义y,因为在外围的代码块中需要使用y

    {
        int x{};
        std::cin >> x;

        // 如果在这个y的第一次使用之前定义y,在外围块中将无法访问到y
        if (x == 4)
            y = 4;
    }

    std::cout << y; // 需要在这里能访问到y

    return 0;
}

新手开发人员有时会想,仅仅为了有意限制变量的作用域(并强制它超出作用域/提前销毁)而创建嵌套块是否值得?这样做使该变量更简单,但结果是整个函数变得更长、更复杂。这种折衷通常是不值得的。如果创建嵌套块有助于有意限制变量的作用域,则该代码最好放在单独的函数中。


7.1 自定义命名空间和作用域解析操作符

上一节

7.3 全局变量

下一节