7-3 局部变量

第2.5课——局部作用域介绍中,我们介绍了局部变量,即在函数内部定义的变量(包括函数参数)。

实际上,C++并没有单一属性来定义变量属于局部变量。相反,局部变量具有若干特性,这些特性使其行为区别于其他类型的(非局部)变量。我们将通过本课及后续课程探讨这些特性。

第2.5课——局部作用域介绍中,我们还介绍了作用域的概念。标识符的作用域决定了它在源代码中可被访问的位置。当标识符可被访问时,我们称其在作用域内;当标识符不可被访问时,则称其在作用域外。作用域是编译时属性,尝试使用作用域外的标识符将导致编译错误。


局部变量具有块作用域

局部变量具有块作用域block scope,这意味着它们从定义点到所在块的结尾都处于作用域内。

相关内容
若需复习代码块相关知识,请参阅第7.1课——复合语句(代码块)

int main()
{
    int i { 5 }; // i enters scope here
    double d { 4.0 }; // d enters scope here

    return 0;
} // d and i go out of scope here

尽管函数参数未在函数体内定义,但对于典型函数而言,它们可被视为函数体代码块作用域的一部分。

int max(int x, int y) // x and y enter scope here
{
    // assign the greater of x or y to max
    int max{ (x > y) ? x : y }; // max enters scope here

    return max;
} // max, y, and x leave scope here

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

变量名在给定作用域内必须唯一,否则对该名称的任何引用都将产生歧义。请考虑以下程序:

void someFunction(int x)
{
    int x{}; // compilation failure due to name collision with function parameter
}

int main()
{
    return 0;
}

上述程序无法编译,因为函数体内部定义的变量 x 与函数参数 x 具有相同名称,且两者均处于同一块作用域内。


局部变量具有自动存储期

变量的存储期storage duration(通常简称为存储期duration)决定了哪些规则将管理变量何时以及如何被创建(实例化)和销毁。在大多数情况下,变量的存储期直接决定了其生命周期lifetime

相关内容
我们在第2.5课——局部作用域介绍中讨论了什么是生命周期。

例如,局部变量具有自动存储期automatic storage duration,这意味着它们在定义处创建,并在定义所在代码块结束时销毁。例如:

int main()
{
    int i { 5 }; // i created and initialized here
    double d { 4.0 }; // d created and initialized here

    return 0;
} // d and i are destroyed here

因此,局部变量有时也被称为自动变量automatic variables


嵌套代码块中的局部变量

局部变量可在嵌套代码块内部定义。其作用机制与函数主体代码块中的局部变量完全相同:

int main() // outer block
{
    int x { 5 }; // x enters scope and is created here

    { // nested block
        int y { 7 }; // y enters scope and is created here
    } // y goes out of scope and is destroyed here

    // y can not be used here because it is out of scope in this block

    return 0;
} // x goes out of scope and is destroyed here

在上例中,变量 y 在嵌套代码块内部定义。其作用域从定义点延伸至嵌套代码块的结尾,生命周期亦与此一致。由于变量 y 的作用域仅限于其定义的内部代码块,因此在外层代码块中无法访问该变量。

需注意:嵌套代码块被视为其所在外部代码块作用域的一部分。因此,外部代码块中定义的变量可在嵌套代码块内部被访问:

#include <iostream>

int main()
{ // outer block

    int x { 5 }; // x enters scope and is created here

    { // nested block
        int y { 7 }; // y enters scope and is created here

        // x and y are both in scope here
        std::cout << x << " + " << y << " = " << x + y << '\n';
    } // y goes out of scope and is destroyed here

    // y can not be used here because it is out of scope in this block

    return 0;
} // x goes out of scope and is destroyed here

局部变量没有链接关系

标识符还具有另一种称为链接关系的属性。标识符的链接关系linkage决定了在不同作用域中对同一标识符的声明是否指向同一对象(或函数)。

局部变量没有链接关系。每个没有链接关系的标识符声明都指向一个唯一的对象或函数。

例如:

int main()
{
    int x { 2 }; // local variable, no linkage

    {
        int x { 3 }; // this declaration of x refers to a different object than the previous x
    }

    return 0;
}

作用域和链接属性看似有些相似。然而,作用域决定了单个标识符的声明在代码中可被看到和使用的位置。关联性则决定了多个相同标识符的声明是否指向同一个对象。

相关内容
我们在第7.5节——变量遮蔽(名称隐藏)中讨论了当相同名称的变量出现在嵌套代码块中时会发生什么。

在局部变量的语境下,变量关联机制并不特别引人入胜,但我们将在接下来的几节课中深入探讨。


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

如果变量仅在嵌套代码块内使用,则应在该嵌套代码块内部定义:

#include <iostream>

int main()
{
    // do not define y here

    {
        // y is only used inside this block, so define it here
        int y { 5 };
        std::cout << y << '\n';
    }

    // otherwise y could still be used here, where it's not needed

    return 0;
}

通过限制变量的作用域,可以降低程序的复杂性,因为活动变量的数量减少了。此外,这使得更容易看出变量在何处被使用(或未使用)。在代码块内部定义的变量只能在该代码块(或嵌套代码块)内使用。这有助于提高程序的可读性。

如果变量在外部代码块中需要使用,则必须在外部代码块中进行声明:

#include <iostream>

int main()
{
    int y { 5 }; // we're declaring y here because we need it in this outer block later

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

        // if we declared y here, immediately before its actual first use...
        if (x == 4)
            y = 4;
    } // ... it would be destroyed here

    std::cout << y; // and we need y to exist here

    return 0;
}

(如果输出4, 则会改变y的值)
image

image

上述示例展示了罕见的情形之一:你可能需要在变量首次使用前就提前声明它。

新手开发者有时会质疑:是否值得专门创建嵌套代码块来刻意限制变量作用域(并强制其提前失效/销毁)?虽然这样能简化变量本身,但会导致函数整体变得冗长复杂。这种权衡通常得不偿失。若创建嵌套代码块对限制代码片段作用域确实有效,那么该代码更适合移至独立函数中处理。

最佳实践
在现有作用域中尽可能限定变量作用域。避免创建仅用于限制变量作用域的新代码块。


测验时间

问题 #1

编写一个程序,要求用户输入两个整数,分别命名为 smaller 和 larger。如果用户为第二个整数输入较小的值,请使用代码块和临时变量交换较小值和较大值。然后打印 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

image

显示解答

#include <iostream>

int main()
{
    std::cout << "Enter an integer: ";
    int smaller{};
    std::cin >> smaller;

    std::cout << "Enter a larger integer: ";
    int larger{};
    std::cin >> larger;

    // if user did it wrong
    if (smaller > larger)
    {
        // swap values of smaller and larger
        std::cout << "Swapping the values\n";

        int temp{ larger };
        larger = smaller;
        smaller = temp;
    } // temp dies here

    std::cout << "The smaller value is: " << smaller << '\n';
    std::cout << "The larger value is: " << larger << '\n';

    return 0;
} // smaller and larger die here

在未来,你可以使用来自头文件的std::swap()函数来交换两个变量的值。例如:

int temp{ larger };
larger = smaller;
smaller = temp;

// is the same as
std::swap(larger, smaller);

问题 #2

变量的作用域scope存储期duration生命周期lifetime有何区别?默认情况下,局部变量具有何种作用域和存续期(以及这些术语的含义)?

显示解答

变量的作用域决定了该变量在源代码中的可访问范围。存储期则规定了变量何时创建与销毁的规则。变量的生命周期即其从创建到销毁的实际时间跨度。

局部变量具有块作用域,这意味着它们可在定义点至所属块结尾范围内被访问。

局部变量具有自动存储期,即在定义点创建,并在所属块结尾处销毁。
posted @ 2026-02-21 18:52  游翔  阅读(2)  评论(0)    收藏  举报