冬季学期 2024/2025
课程名称:编程导论(Einführung in die Programmierung)
章节:第6章 作用域(Gültigkeitsbereiche)
主讲人:Dr.-Ing. Alexander Krause
单位:多特蒙德工业大学 系统软件应用信息学12组
联系方式:eidp-problems@ls12.cs.tu-dortmund.de
课程官网: https://sys.cs.tu-dortmund.de/de/lehre/ws24/eidp/
第6章:作用域(Gültigkeitsbereiche)
学习目标
- 理解局部变量与全局变量的作用域
- 掌握变量遮蔽(Verdeckung)机制
- 了解静态变量的生命周期
- 熟悉命名空间(namespace)的使用与目的
1. 变量作用域基础
局部变量(Lokale Variablen)
- 在函数或代码块
{}内定义 - 存储在栈内存(Stapelspeicher)
- 作用域仅限于定义它的块
- 块结束时变量失效
全局变量(Globale Variablen)
- 在所有函数之外定义(通常在
main()前) - 存储在静态内存(statischer Speicher)
- 生命周期贯穿整个程序运行
- 原则上处处可见,但可被同名局部变量遮蔽
2. 变量遮蔽与作用域层级
示例1:全局与局部变量同名
int x = 10; // 全局变量
int main() {
cout << x << endl; // 输出: 10
int x = 5; // 局部变量,遮蔽全局变量
cout << x << endl; // 输出: 5
cout << ::x << endl; // 使用 :: 访问全局变量,输出: 10
return 0;
}
:: 是作用域解析运算符(scope resolution operator),用于访问被遮蔽的全局变量。
示例2:嵌套代码块中的作用域
Cpp
深色版本
int k = -1; // 全局变量
int main() {
cout << "k global: " << k << endl; // 输出: -1
int k = 0;
cout << "k main: " << k << endl; // 输出: 0
{
int k = 1;
cout << "k block1: " << k << endl; // 输出: 1
{
int k = 2;
cout << "k block2: " << k << endl; // 输出: 2
}
cout << "k block1: " << k << endl; // 输出: 1
}
cout << "k main: " << k << endl; // 输出: 0
cout << "k global: " << ::k << endl; // 输出: -1
return 0;
}
变量遮蔽规则:内层块中的同名变量会遮蔽外层变量,但外层变量仍存在。
- 循环变量的作用域 示例3:for 循环变量 Cpp
int main() {
int i;
for (i = 0; i < 100; ++i) {
cout << i << endl;
}
cout << i << endl; // ✅ 合法:i 在循环后仍存在
for (int j = 0; j < 100; ++j) {
cout << j << endl;
}
// cout << j << endl; // ❌ 错误:j 作用域已结束
return 0;
}
建议:
若循环变量后续还需使用,应在循环外声明 若仅用于循环,应在循环头中声明 4. 静态变量(Statische Variablen) 静态局部变量(Statische lokale Variablen) 使用 static 关键字声明 作用域:仅在定义它的函数/块内可见 生命周期:从首次初始化到程序结束,存储在静态内存 仅初始化一次 示例:计数器函数 Cpp
int countCalls() {
static int ctr = 0; // 仅在第一次调用时初始化
return ++ctr;
}
int main() {
for (int i = 0; i < 10; ++i)
cout << countCalls() << endl;
// 输出: 1 2 3 4 5 6 7 8 9 10
return 0;
}
静态局部变量在函数多次调用间保持其值。
静态全局变量(Statische globale Variablen) 在文件级别使用 static 声明 作用域:仅限于定义它的源文件 其他文件无法通过 extern 访问 示例: Cpp
// 文件 Global.cpp
int global = 1; // 全局,其他文件可见
static int globalS = 2; // 静态全局,仅本文件可见
// 文件 Haupt.cpp
extern int global; // ✅ 合法:可访问
// extern int globalS; // ❌ 错误:static 变量不可 extern
注意:C++ 中静态全局变量已过时(deprecated),不建议使用。
- 命名空间(Namespaces) 目的:避免命名冲突 当多个开发者定义同名函数时,会发生冲突:
Cpp
// 开发者 A
void print(int n, double a[]) {
// 正序输出
}
// 开发者 B
void print(int n, double a[]) {
// 逆序输出
}
// ❌ 编译错误:重复定义
解决方案:命名空间
Cpp
深色版本
namespace A {
void print(int n, double a[]) {
// 正序输出
}
}
namespace B {
void print(int n, double a[]) {
// 逆序输出
}
}
int main() {
double a[] = {1.1, 2.2, 3.3};
A::print(3, a); // 调用 A 的 print
B::print(3, a); // 调用 B 的 print
return 0;
}
命名空间的使用 using namespace std;:使 std 中所有名称可见(如 cout, endl) 更安全的方式:using std::cout;,仅导入特定名称 支持嵌套命名空间:using namespace std::chrono; 建议:避免在头文件中使用 using namespace,以防污染全局命名空间。
- 总结 概念 关键点 局部变量 作用域在块内,栈存储,块结束即销毁 全局变量 全局可见,静态存储,程序结束销毁,易被遮蔽 作用域解析 :: 可访问被遮蔽的全局变量 静态局部变量 作用域局部,生命周期全局,仅初始化一次 静态全局变量 已过时,不建议使用 命名空间 避免命名冲突,推荐使用 using std::xxx 而非 using namespace std 最佳实践:尽量避免使用全局变量,优先使用局部变量和命名空间管理代码。
举例说明:
ttt.cpp
namespace A {
void print(int n, double a[]) {
// 正序输出
}
int global = 20;
}
#include<bits/stdc++.h>
#include "ttt.cpp"
using namespace std;
extern int global;
extern void print(int n, double a[]);
int main(){
cout << A::global ; //必须加A::
return 0;
}
—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com