局部变量
本节阅读量:
局部变量是在函数内定义的变量(包括函数参数)。
在之前,我们还介绍了作用域的概念。标识符的作用域确定了可以在源代码中访问标识符的作用域。当一个标识符可以被访问时,我们说它在作用域内。当标识符不能被访问时,我们说它超出作用域。作用域是编译时属性,当标识符超出作用域时,尝试使用它将导致编译错误。
局部变量具有代码块作用域
局部变量具有代码块作用域,这意味着从定义点到定义它们的代码块的末尾都在作用域内。
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;
}
|
新手开发人员有时会想,仅仅为了有意限制变量的作用域(并强制它超出作用域/提前销毁)而创建嵌套块是否值得?这样做使该变量更简单,但结果是整个函数变得更长、更复杂。这种折衷通常是不值得的。如果创建嵌套块有助于有意限制变量的作用域,则该代码最好放在单独的函数中。