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的值)


上述示例展示了罕见的情形之一:你可能需要在变量首次使用前就提前声明它。
新手开发者有时会质疑:是否值得专门创建嵌套代码块来刻意限制变量作用域(并强制其提前失效/销毁)?虽然这样能简化变量本身,但会导致函数整体变得冗长复杂。这种权衡通常得不偿失。若创建嵌套代码块对限制代码片段作用域确实有效,那么该代码更适合移至独立函数中处理。
最佳实践
在现有作用域中尽可能限定变量作用域。避免创建仅用于限制变量作用域的新代码块。
测验时间
问题 #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

显示解答
#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
在未来,你可以使用来自
int temp{ larger };
larger = smaller;
smaller = temp;
// is the same as
std::swap(larger, smaller);
问题 #2
变量的作用域scope、存储期duration和生命周期lifetime有何区别?默认情况下,局部变量具有何种作用域和存续期(以及这些术语的含义)?
显示解答
变量的作用域决定了该变量在源代码中的可访问范围。存储期则规定了变量何时创建与销毁的规则。变量的生命周期即其从创建到销毁的实际时间跨度。
局部变量具有块作用域,这意味着它们可在定义点至所属块结尾范围内被访问。
局部变量具有自动存储期,即在定义点创建,并在所属块结尾处销毁。

浙公网安备 33010602011771号