C++局部变量详解与作用域、生命周期

什么是局部变量?

在 2.5 课《局部作用域简介》中,我们引入了“局部变量”这一概念,即定义在函数内部(包含函数形参)的变量。

实际上,C++ 并没有单一属性能够完全界定“局部变量”。相反,局部变量通过一组相互关联的性质区别于其他(非局部)变量。我们将在本节及后续课程中逐一探讨这些性质。

回顾 2.5 课可知,标识符的“作用域”决定了它在源代码中的可见范围。能够访问时,我们称其“在作用域内”;不能访问时,称其“不在作用域内”。作用域是编译期属性;在作用域外使用标识符将导致编译错误。

局部变量具有块作用域

局部变量拥有“块作用域”,即从其定义点开始,直至所在代码块的末尾均处于作用域内。

相关阅读
如需复习代码块,请回顾 7.1 课《复合语句(代码块)》。

int main()
{
    int i { 5 };    // i 在此处进入作用域
    double d { 4.0 }; // d 在此处进入作用域

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

尽管函数形参并非定义于函数体内部,对普通函数而言,可将其视为函数体代码块作用域的一部分。

int max(int x, int y)   // x 和 y 在此处进入作用域
{
    int max { (x > y) ? x : y }; // max 在此处进入作用域
    return max;
}   // max、y 和 x 在此处离开作用域

同一作用域内变量名必须唯一

给定作用域内的变量名必须互不相同,否则任何对该名字的引用都会出现歧义。示例:

void someFunction(int x)
{
    int x {}; // 与形参 x 同名,编译失败
}

局部变量具有自动存储期

变量的“存储期”(通常简称“期”)决定其创建(实例化)与销毁所遵循的规则。在大多数情况下,存储期直接决定了变量的“生命周期”。

相关阅读
关于生命周期的讨论见 2.5 课。

局部变量具有“自动存储期”:在定义点创建,在所属代码块结束时销毁。因此,局部变量也常被称作“自动变量”。

int main()
{
    int i { 5 };   // i 在此处创建并初始化
    double d { 4.0 }; // d 在此处创建并初始化
    return 0;
}   // d 和 i 在此处销毁

嵌套代码块中的局部变量

局部变量可定义于嵌套代码块,其行为与函数体代码块中的局部变量完全一致:

int main()      // 外层代码块
{
    int x { 5 };   // x 在此处进入作用域并创建

    {              // 嵌套代码块
        int y { 7 }; // y 在此处进入作用域并创建
    }              // y 在此处离开作用域并销毁

    // y 在此处已不在作用域,无法使用

    return 0;
}   // x 在此处离开作用域并销毁

变量 y 的作用域仅限于其所在的内层代码块,生命周期亦然。由于 y 不在外层作用域,外层无法访问。
注意:嵌套代码块仍属于外层代码块作用域的一部分,因此外层定义的变量在内层可见:

#include <iostream>

int main()
{   // 外层代码块
    int x { 5 };   // x 在此处进入作用域并创建

    {              // 嵌套代码块
        int y { 7 }; // y 在此处进入作用域并创建
        std::cout << x << " + " << y << " = " << x + y << '\n';
    }   // y 在此处离开作用域并销毁

    return 0;
}   // x 在此处离开作用域并销毁

局部变量无链接属性

标识符还具有“链接”属性。链接决定同一标识符在不同作用域中的声明是否指向同一对象(或函数)。

局部变量无链接。每个无链接的同名声明均指向独立对象:

int main()
{
    int x { 2 }; // 局部变量,无链接
    {
        int x { 3 }; // 此 x 与前一个 x 不同
    }
    return 0;
}

作用域与链接的区别:作用域决定单个声明在何处可见;链接决定多个同名声明是否指向同一实体。

相关阅读
同名变量在嵌套块中的行为见 7.5 课《变量遮蔽(名称隐藏)》。

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

若变量仅在内层代码块中使用,应将其定义于该内层块:

#include <iostream>

int main()
{
    {
        int y { 5 }; // y 仅在此块内使用
        std::cout << y << '\n';
    }   // y 离开作用域并销毁
    return 0;
}

限制变量作用域可减少活跃变量数量,降低复杂性,并明确使用位置。
若变量需在外层使用,则必须在外层声明:

#include <iostream>

int main()
{
    int y { 5 }; // 在外层声明,因为后续需要

    {
        int x{};
        std::cin >> x;
        if (x == 4) y = 4;
    }

    std::cout << y; // 此处仍需 y
    return 0;
}

新手有时会刻意增加嵌套代码块以提前销毁变量。虽然简化了该变量,但整体函数更长、更复杂,得不偿失。若需如此,建议将相关代码提取为独立函数。

最佳实践
在现有最小作用域内定义变量。切勿仅为限制变量作用域而新建代码块。

C++局部变量测验

问题 1

编写程序,要求用户输入两个整数,分别命名为 smaller 和 larger。若用户第二次输入的值较小,则利用代码块与临时变量交换二者。随后输出 smaller 与 larger 的值,并在代码中注释每个变量的销毁点。输出示例:

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

问题 2

简述变量作用域、存储期与生命周期的区别;默认情况下,局部变量具有何种作用域与存储期?

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"