内容正文:
7.8节:作用域和存储类型
C语言程序设计
电子工业出版社
掌握变量的作用域与生存期的区别与联系
区分局部变量和全局变量,理解其作用范围与核心特点
深入掌握 auto, static, register, extern 四种存储类型的含义
编写包含不同作用域/存储类型的程序,并能准确预测运行结果
培养编程逻辑与抽象思维,建立良好的变量管理意识,编写高可读、可维护的代码
知识目标
能力目标
素养目标
01
01
01
学习目标
目录
Part 01
教学导入
Part 02
知识讲授
Part 03
师生互动
Part 04
课堂练习
Part 05
课堂小结
教学导入:我们身边的“作用域”与“存储”
作用域
📖 图书馆场景
你在特定的“社科阅览室”内,可以自由使用室内所有的书籍和电脑,这就是你的“活动范围”。
💻 C语言类比
变量就像读者,也有自己的“阅览室”,它只能在特定的代码区域内被访问、修改和使用。
存储类型
📦 物品存放场景
物品是随身携带、寄存前台还是放入储物柜?不同的存放方式决定了它的可用性和存在时间。
⚙️ C语言类比
变量的“存储方式”(如auto/register等),决定了它在内存中的位置,进而影响生命周期和访问效率。
教学导入:图书馆里的四种“存储类型”
auto (自动变量)
📖 比喻:随身携带的笔记本
特点:方便灵活,但离开图书馆(程序结束)就可能丢失,生命周期短。
static (静态变量)
🔒 比喻:储物柜里的箱子
特点:存放后即使离开,下次再来东西依然存在,具有“记忆”功能,全局有效。
register (寄存器变量)
⚡ 比喻:大脑里记住的信息
特点:直接存放在CPU寄存器中,计算速度最快,无需像硬盘一样查找。
extern (外部变量)
📢 比喻:大厅的公共告示板
特点:定义在外部文件中,信息对所有程序文件可见,用于实现数据的共享。
知识讲授
作用域和存储类型
局部变量和全局变量
变量的存储类型
第一部分
局部变量和全局变量
知识讲授
知识讲授:作用域——变量的“活动范围”
核心定义:变量的“有效范围”
作用域指的是一个变量在程序中能够被访问的有效范围,本质上规定了变量的可见性。
核心思想:变量的“势力范围”
变量并非在程序的任何地方都有效。它像现实中的领地一样,拥有只属于自己的“势力范围”,超出范围则无法被感知。
局部作用域
仅在函数或代码块 `{}` 内部有效,属于“私有空间”。
全局作用域
在整个源程序文件中都有效,属于“公共区域”。
知识讲授:生存期——变量的“存在时间”
💡 核心定义
生存期指变量在程序运行中,从被分配内存到内存被释放的时间段,即变量在内存中的存在性。
🧠 核心认知
变量并非一直占据内存资源,它拥有属于自己的“生命周期”,在不同阶段会被动态管理。
静态存储
程序启动时分配内存,结束时释放。生存期贯穿程序运行的始终。
动态存储
执行到定义处分配,离开作用域时释放。生存期是临时且局部的。
知识讲授:局部变量——函数的“私有财产”
核心定义 & 常用别名
定义:在函数内部或复合语句(代码块)内定义的变量。
别名:常被开发者称作“内部变量”,强调其封闭性。
作用域限制
仅限定义它的函数或代码块内部有效,外部无法访问。
动态生存期
进入时分配内存,退出时立即释放,随代码块生命周期动态变化。
栈区存储
数据通常存放在内存的动态存储区(栈区),空间复用率极高。
无默认初始值
必须显式初始化后使用,否则为随机垃圾值,极易引发程序运行错误。
同名互不干扰
不同函数中的同名局部变量是完全独立的实体,拥有各自独立的内存空间。
知识讲授:全局变量——程序的“公共资源”
核心定义
在所有函数之外定义的变量,独立于任何局部作用域。
常见别名
也常被称为“外部变量”。
作用域极广
从定义处开始,至整个源文件结束均有效。
静态生存期
随程序运行而存在,随程序结束而销毁。
静态存储区
存放在内存的数据段,而非栈区。
默认初始化
若未手动赋值,系统默认初始化为0。
多函数共享
可被多个函数访问/修改,实现数据交互。
💡 核心价值
作为程序的“公共数据池”存在。
例题分析:同名局部变量互不干扰
程序文本:同名局部变量互不干扰
#include <stdio.h>
void func1()
{
int x = 10;// 局部变量 x
printf("In func1, x = %d
", x);
}
void func2() {
int x = 20;// 独立的局部变量 x
printf("In func2, x = %d
", x);
}
main()
{ func1();
func2(); }
核心思考
01
程序的函数体内,一共定义了几个名为`x`的局部变量?
02
`func1()` 中的变量 `x` 与 `func2()` 中的变量 `x` 是同一个变量吗?
03
结合局部变量的“作用域”规则,推断这段程序运行后的输出结果是什么?
例题分析:同名局部变量互不干扰
📝 详细答案拆解
程序中存在两个名为 `x` 的局部变量,分别定义在不同函数内。
这两个变量是完全独立的,因为作用域严格限制在各自的函数内部。
▶ 程序运行实际输出:
In func1, x = 10
In func2, x = 20
核心知识点总结
不同函数中定义的同名局部变量,在内存中拥有完全不同的独立空间。
因此,它们的修改和访问彼此之间不会产生任何影响。
💡 重点记忆:局部变量的“作用域隔离”机制
例题分析:同名局部变量互不干扰
例题分析:作用域的嵌套
核心规则:代码块的“隔离”效应
在{ }内部定义的变量,其作用域仅限于该代码块内部。内部同名变量会屏蔽外部变量,离开代码块后,内部变量立即失效。
💻 代码实现示例
#include <stdio.h>
main()
{ int a = 100;
printf("Outer: %d
", a);
{ // 内部代码块开始
int a = 200;// 定义新变量,屏蔽外部
printf("Inner: %d
", a);
} // 内部代码块结束
printf("Outer: %d
", a); }
📊 程序运行结果
Outer a =100 // 访问局部变量a
Inner a =200
/*访问花括号内部的局部变量(屏蔽花括号外的局部变量)*/
Outer a =100
/*内部局部变量已销毁,恢复外侧局部变量*/
例题分析:作用域的嵌套
例题分析:全局变量与局部变量同名
程序文本【7.12】外部变量与局部变量同名
#include<stdio.h>
int a=3,b=5; //a,b为外部变量
int max(int a,int b) //a,b为局部变量
{
int c;
c=a>b?a:b;
return (c);
}
main()
{
int a=8;
printf("%d
",max(a,b));
}
核心规则:局部优先
当局部变量与全局变量同名时,在局部作用域内,局部变量优先生效,全局变量被“屏蔽”。对局部变量的修改不会影响全局变量。
例题分析:全局变量与局部变量同名
背诵记忆
作用域
决定变量的可见范围
局部变量 Local
作用域仅限于函数或代码块内部
全局变量 Global
作用域覆盖整个程序文件
生存期
决定变量的存在时间
局部变量 Local
随函数调用分配/释放,动态存储
全局变量 Global
程序运行期间一直存在,静态存储
核心规则
变量使用的黄金定律
局部优先原则
同名情况下,局部变量会屏蔽全局变量
作用域独立性
不同作用域的同名变量互不干扰
课堂问答
QUESTION 01
为什么要在代码块内部定义变量?
提高封装性:将变量作用域限制在最小范围,避免对外部代码产生意外的副作用。
增强可读性:代码阅读者可以直观地判断该变量仅服务于当前这个特定的代码逻辑块。
避免命名冲突:在不同的代码块中,可以安全地复用相同的变量名称。
QUESTION 02
在一个函数内,能否在多个不同的代码块中定义同名变量?
当然可以!
因为不同代码块中的同名变量,其作用域是完全隔离的,仅限于各自的代码块内部,彼此之间互不干扰。
课堂问答
01. 全局变量定义位置?
在所有函数之外进行定义,是程序的外部变量。
02. 全局变量作用范围?
默认从定义处开始,一直到整个源程序文件的结束。
03. 全局变量生存期?
属于静态存储方式,在整个程序的运行期间始终存在。
04. 若不初始化,全局变量默认值是多少?
系统会自动将其初始化为0。这与局部变量必须显式初始化的特点形成鲜明对比。
05. 全局变量的核心用途是什么?
作为不同函数之间进行数据通信的桥梁,实现函数间的数据共享,减少参数传递的复杂度。
课堂练习:判断输出结果
程序文本:判断输出结果
#include<stdio.h>
int a = 1;
void f1()
{ int b = 2;
printf("a=%d, b=%d
", a, b); }
void f2()
{ int a = 3;
{ int b = 4;
printf("a=%d, b=%d
", a, b);
}
printf("a=%d
", a);
// printf("b=%d
", b); // 编译会出错吗?
}
思考与实战任务
01. 推演输出结果
请在草稿纸上模拟程序的执行流程,完整写出控制台打印出的两行结果。
02. 分析错误原因
分析代码中被注释的 printf 语句。它是否会导致编译失败?请结合“作用域”规则说明理由。
课堂练习:判断输出结果
01 / MAIN 核心代码
main()
{
f1();
f2();
printf("a=%d
", a);
}
程序执行的入口逻辑,通过调用 f1() 和 f2() 来观察变量作用域的变化。
02 / 执行输出推演
a=1, b=2// f1():a全局 / b局部
a=3, b=4// f2()块内:a/b均局部
a=3// f2()块外:b已失效
a=1// main():a回归全局
03 / 编译错误警示
代码编译报错
printf("b=%d
", b);
↑ 该行无法通过编译
错误根源解析
变量 b 的作用域仅限于 f2() 内部的代码块 {}。离开该区域后,b 立即释放,程序无法识别。
课堂练习:判断输出结果
第二部分
知识讲授
变量的存储类型
知识讲授:变量的“存储方式”
核心思想:存储类型决定了变量在内存中的存储位置和生存期。
程序区
专门用于存放程序的二进制代码。这是程序运行的基础,由系统在程序加载时分配。
静态存储区
存放全局变量和静态局部变量。该区域在程序整个运行期间始终存在,直到程序结束才释放。
动态存储区 (栈区)
存放自动变量、函数参数等。随函数的调用而分配,随函数的返回而自动释放,具有动态性。
知识讲授:默认的动态变量(auto)
💡 核心定义
`auto` 是局部变量的默认存储类型。在函数或代码块内定义的变量,若未显式指定存储类型,编译器会默认将其识别为 `auto` 类型。
⚙️ 关键特性
局部作用域
仅限代码块内有效
动态生存期
随代码块创建销毁
栈区存储
占用动态内存空间
💻 代码示例 (C Language)
void func() {
auto int x = 10;// 等价于 int x = 10;
}
知识讲授:拥有“记忆”的变量(static)
核心概念:持久性
让局部变量拥有“记忆”的能力
📖 定义
`static` 关键字是 C/C++ 中用于修饰变量(局部/全局)的重要关键词,它赋予了变量特殊的存储属性和生命周期。
作用域
保持**局部作用域**,仅在定义它的函数或代码块内部可见,外部无法访问。
生存期
变为**静态存储**。在程序启动时分配内存,直到程序完全结束才释放资源。
核心特性
值会被保留**。函数调用结束后值不消失,下次调用时直接使用上次的结果。
初始化
具有**惰性初始化**特性。无论函数被调用多少次,仅在“第一次”进入时初始化。
例题分析:static 的记忆效应
程序文本:
#include <stdio.h>
void count() {
auto int a = 0;// 自动变量,每次初始化
static int b = 0;// 静态变量,仅初始化一次
a++; b++;
printf("a=%d, b=%d
", a, b);
}
main()
{
count();
count();
count();
}
//思考:程序的输出结果?
例题分析:static 的记忆效应
▍程序运行输出结果
a = 1, b = 1
a = 1, b = 2
a = 1, b = 3
注:共调用 count() 函数 3 次
a (auto) · 自动变量的特性
每次调用count()函数时,a 都会被重新创建并初始化为 0,随后自增为 1。函数执行完毕后,其内存空间被立即释放。因此无论调用多少次,输出结果始终为 1。
b (static) · 静态变量的“记忆性”
首次调用初始化后,函数返回时b 的存储空间不释放,值被保留。后续调用会跳过初始化,直接在上一次的结果上自增。因此输出呈现 1 → 2 → 3 的递增趋势。
例题分析:static 的记忆效应
课堂问答:static 局部变量的特点
Q1: 作用域发生变化了吗?
答案:没有,依然保持局部作用域范围。
Q2: 生存期发生变化了吗?
答案:变了,从动态存储区改为静态存储区。
Q3: 最重要的特性是什么?
答案:值会被保留,具有“记忆”上一次的功能。
Q4: 初始化操作执行了几次?
答案:只执行一次,仅在第一次进入函数时。
知识讲授:限制全局变量的作用域(static)
核心定义
关键字static不仅可以修饰局部变量,同样可以修饰全局变量,赋予其特殊的访问规则。
核心作用:作用域收缩
将全局变量的作用域严格限制在当前源文件 (.c)内部,使其无法被其他源文件通过 `extern` 关键字访问。
设计目的:封装与隔离
实现文件级别的数据封装,有效避免在大型项目开发中,不同源文件之间出现全局变量的命名冲突问题。
作用域范围对比
普通全局变量:作用域覆盖整个程序(所有源文件)
静态全局变量:作用域仅限当前源文件(定义所在文件)
知识讲授:追求极致速度的变量(register)
CPU 寄存器加速
核心定义:建议编译器将变量存储在CPU寄存器而非内存中,实现硬件级的快速读写。
设计目的:专为高频读写场景优化,如循环计数器 (Loop Counter)。
作用域限制:仅支持局部作用域(Local Scope),即只能在函数或代码块内部定义。
动态生存期:变量随所在代码块的执行而创建,执行结束后立即释放资源。
非强制性指令:这只是对编译器的“建议”,现代编译器优化能力极强,通常会自动忽略该关键字。
禁止取地址:寄存器没有物理内存地址,因此不能对register变量使用取地址符`&`。
例题分析:使用寄存器变量
程序文本【7.15】使用寄存器变量
#include<stdio.h>
int fac(int n)
{
register int i,f=1;
for(i=1;i<=n;i++)
f=f*i;
return (f);
}
main()
{
int i;
for(i=0;i<=5;i++)
printf("%d!=%d
",i,fac(i));
}
例题分析:使用寄存器变量
知识讲授:跨文件共享全局变量(extern)
跨文件数据共享示意
核心定义
用于声明一个已在其他源文件中定义的全局变量,建立跨文件的变量引用关系。
核心作用
•扩展作用域:在当前文件中调用其他文件定义的全局变量。
•提前引用:在当前文件变量定义代码之前的位置安全使用该变量。
关键注意
仅做声明不做定义。不会为变量分配新的内存空间,仅告知编译器变量的存在位置。
例题分析:使用extern声明外部变量
程序文本【7.16】使用extern声明外部变量
#include<stdio.h>
int max(int x,int y)
{
int z;
z=x>y?x:y;
return (z);
}
main()
{
extern A,B;
printf("%d
",max(A,B));
}
int A=13,B=-8;
例题分析:使用extern声明外部变量
课堂问答
QUESTION 01
`static` 局部变量和全局变量都存放在静态存储区,它们的本质区别是什么?
💡 核心解析
区别在于作用域。static局部变量作用域限于函数内部,而全局变量的作用域覆盖整个程序。
QUESTION 02
在什么场景下使用 `register` 变量?这个关键字在现代开发中还常用吗?
💡 核心解析
用于频繁访问的变量。现在不常用,因为现代编译器的优化能力极强,已能自动做出更优的寄存器分配判断。
QUESTION 03
`extern` 和 `static` 这两个关键字在修饰全局变量时,它们的作用是相反的吗?
💡 核心解析
是的。`extern` 关键字用于扩展全局变量的作用域,而 `static` 关键字恰好相反,是用于限制全局变量的作用域范围。
背诵记忆
auto · 自动变量
作用域:
生存期:
存储区:
默认值:
局部作用域
程序动态运行期
栈区 (Stack)
随机值 (未初始化)
static · 静态变量
作用域:
生存期:
存储区:
默认值:
局部 / 文件全局
程序整个运行期
静态存储区 (Data)
0 (自动初始化)
register · 寄存器变量
作用域:
生存期:
存储区:
核心特点:
局部作用域
程序动态运行期
CPU寄存器 / 栈区
读写速度快 (建议性)
extern · 外部变量
作用域:
生存期:
存储区:
核心特点:
全局作用域 (跨文件)
程序整个运行期
静态存储区 (Data)
扩展作用域,实现共享
课堂练习
C 语言代码编辑器
// 全局变量与局部变量综合示例
#include<stdio.h>
int x = 1;// 全局变量 x
void func(int x) // 形参(局部) x
{
x++;// 操作局部变量
static int y = 10;// 静态局部变量 y
y++;
printf("In func: x=%d, y=%d
", x, y);
}
课堂任务挑战
Step 1. 代码精读与识别
请圈出代码中的全局变量、局部形参以及静态局部变量,并思考它们各自的作用域与生命周期。
Step 2. 运行结果推演
结合即将展示的 main 函数调用逻辑,在草稿纸上模拟变量的变化过程,写出最终的控制台输出结果。
课堂练习
main 函数代码
main() {
int x = 100; // 局部变量
func(x); func(x);
printf("In main: x=%d", x);
}
执行逻辑:定义局部变量 x=100,并连续两次调用 func(x),最后打印 main 中的 x 值。
程序运行输出
In func: x=101, y=11
In func: x=101, y=12
In main: x=100
结果特征:func 中 x 恒为 101,y 递增;main 中 x 保持初始值不变。
核心逻辑分析
形参屏蔽效应
func的形参x屏蔽外部变量,每次传入100,自增后输出101。
静态变量特性
y是static变量,保留上次调用值,因此输出11后变为12。
局部变量独立性
main的x作用域仅限于main,未被func修改,保持为100。
课堂练习
课堂小结
核心:两大维度
•作用域:决定变量的可见范围(局部/全局)
•生存期 :决定变量的存在时间(动态/静态)
体系:两大分类
•局部变量:作用域局限,支持 auto(动态) / static(静态)
•全局变量:作用域广泛,默认采用静态存储方式
关键:四种存储类型
•auto(默认局部) |static(记忆/全局) |register(高速)
•extern(全局变量共享声明)
课堂小结
课后任务
作业 01 · 基础
编写一个C程序,定义全局变量total。
实现函数add(int a)和subtract(int a)对 total 进行操作。
在main函数中调用并打印结果,观察数据变化。
作业 02 · 进阶
实现一个fibonacci()函数,无参数,每次调用返回数列的下一个值。
核心要求:必须使用static静态变量来保存当前的计算状态。
思考:静态变量在此处起到了什么关键作用?
作业 03 · 思辨
系统性总结全局变量的优点与缺点。
对比分析:在实际工程项目中,你更倾向于使用全局变量,还是通过函数参数和返回值来传递数据?
请阐述具体的理由。
💡 学习贴士:建议先独立完成编码任务,遇到瓶颈时可查阅课堂笔记或与同学进行小组讨论。
谢谢观看
THANKS
$