当前位置: 首页 > news >正文

16_作用域存储类别与typedef

作用域、存储类别与 typedef

一、本篇文章要解决什么问题

你写的程序越来越复杂——多个函数、多个文件、变量到处都是。这时候有三个问题你必须搞清楚:

  1. 作用域:一个变量在代码的哪些地方"可见"、哪些地方"不可见"?为什么在函数 A 里定义的变量,函数 B 里不能用?
  2. 生命周期:变量什么时候出生、什么时候销毁?为什么函数返回后里面的局部变量就失效了?
  3. typedefstruct Student每次都要写 struct 太麻烦了,怎么给它起个短别名?

二、先用一个简单例子理解

2.1 公司里的话筒和广播

  • 局部变量(auto):你桌上的内部电话——只能你自己用(函数内部),别人打不进来。你下班后电话就拔了(函数返回,变量销毁)。
  • 全局变量:公司广播喇叭——整个公司的人都能听到(所有函数都能访问),24 小时不断电(程序运行期间一直存在)。
  • static 局部变量:你抽屉里的笔记本——只有你能看,但不会每天销毁,而是持续保留上次写的内容。
  • extern:告诉房东"隔壁房间有个冰箱,我能用"——变量定义在别处,但我声明我要用。

2.2 typedef 就是"起外号"

你的名字是 “Christopher”,但朋友都叫你 “Chris”。typedef就是给一个类型起个短的外号——typedef unsigned long long ull;之后你就可以写ull x;代替unsigned long long x;


三、核心知识点讲解

3.1 局部变量(自动变量 auto)

在函数内部或复合语句({}块)内部定义的变量默认是局部变量:

voidfunc(void){intx=10;// 局部变量,只在 func 内部有效}// 出了这个函数,x 就不存在了

局部变量的生命周期:进入函数时创建,函数返回时销毁。不同函数可以有同名的局部变量,互不影响。

图16-1 作用域层级图:帮读者建立作用域的层级概念。

3.2 全局变量——在所有函数之外定义

#include<stdio.h>intglobalCount=0;// 全局变量,所有函数都能访问voidincrement(void){globalCount++;}intmain(void){increment();increment();printf("count = %d\n",globalCount);// 输出 2return0;}

全局变量在程序启动时创建,程序结束时销毁。它的初始值默认为 0(如果不手动初始化)。全局变量虽然方便,但过度使用会让程序难以理解和维护——任何函数都能改它,出问题时很难定位是谁改的。

3.3 static——持久的局部变量

static 让局部变量"记住上一次的值":

#include<stdio.h>voidcountCalls(void){staticintcallCount=0;// 只初始化一次!callCount++;printf("这个函数被调用了 %d 次\n",callCount);}intmain(void){for(inti=0;i<5;i++){countCalls();}return0;}

运行结果:

这个函数被调用了 1 次 这个函数被调用了 2 次 这个函数被调用了 3 次 这个函数被调用了 4 次 这个函数被调用了 5 次

如果callCount是普通局部变量,每次输出都是 1。但 static 让它在函数调用之间保持值不变。

static 也用于全局变量和函数:static int x;static void helper(void)——表示"只在当前文件中可见",其他文件不能访问。

图16-2 static 局部变量生命周期图:一图讲清 static 让变量"持久化"的机制。

3.4 extern——跨文件共享变量

// file1.cintshared=100;// 定义全局变量// file2.cexternintshared;// 声明:我在用 file1.c 中定义的 sharedvoidfunc(void){shared++;}

extern 告诉编译器"这个变量在别处定义了,你只管用,链接时会找到它"。

图16-3 多文件项目中 extern 和 static 的使用场景图:帮助理解多文件协作时的变量可见性控制。

3.5 typedef——给类型起别名

typedefunsignedlonglongull;ull big=12345678901234ULL;// 等价于 unsigned long long bigtypedefstruct{intid;charname[20];doublescore;}Student;// 现在 Student 就是一个类型名Student stu={1001,"Tom",90.0};// 不需要再写 struct!

typedef 和 #define 的区别:

#definePTRint*// 文本替换typedefint*IPTR;// 类型别名PTR a,b;// → int *a, b; → a 是指针,b 是 int!(陷阱!)IPTR a,b;// → a 和 b 都是 int *

这就是为什么定义指针类型别名时应该用 typedef 而不是 #define。


图16-4 typedef vs #define 指针类型陷阱图:让读者记住"定义指针别名用 typedef"。

四、完整代码示例

一个演示作用域、static 和 typedef 的综合程序:

#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<string.h>// 用 typedef 简化结构体使用typedefstruct{intid;charname[30];doublescore;}Student;// 全局变量:统计总人数staticinttotalStudents=0;voidaddStudent(Student arr[],int*count){printf("请输入学号、姓名、成绩:");scanf("%d %29s %lf",&arr[*count].id,arr[*count].name,&arr[*count].score);(*count)++;totalStudents++;}voidprintStudents(constStudent arr[],intcount){printf("\n===== 学生列表(全局统计:%d 人)=====\n",totalStudents);for(inti=0;i<count;i++){printf("[%d] %d %-10s %.1f\n",i+1,arr[i].id,arr[i].name,arr[i].score);}}intmain(void){Student students[50];// 直接用 Student,不用 struct 关键字intcount=0;intchoice;do{printf("\n1. 添加学生 2. 显示列表 0. 退出\n请选择:");scanf("%d",&choice);switch(choice){case1:addStudent(students,&count);break;case2:printStudents(students,count);break;case0:break;default:printf("无效选择\n");}}while(choice!=0);printf("程序结束,共服务了 %d 名学生\n",totalStudents);return0;}

五、运行结果

1. 添加学生 2. 显示列表 0. 退出 请选择:1 请输入学号、姓名、成绩:1001 Tom 90.5 1. 添加学生 2. 显示列表 0. 退出 请选择:1 请输入学号、姓名、成绩:1002 Jerry 85.0 1. 添加学生 2. 显示列表 0. 退出 请选择:2 ===== 学生列表(全局统计:2 人)===== [1] 1001 Tom 90.5 [2] 1002 Jerry 85.0 1. 添加学生 2. 显示列表 0. 退出 请选择:0 程序结束,共服务了 2 名学生

六、代码逐行解析

typedef 简化结构体定义:

typedefstruct{intid;charname[30];doublescore;}Student;

这行定义了一个匿名结构体,并给它起了别名Student。之后用Student s;就可以定义结构体变量,不需要写struct。结构体内部也可以有自己的名字(Student放在}之后),但用匿名结构体+typedef 更简洁。

static 全局变量的隐藏作用:

staticinttotalStudents=0;
  • 在全局范围用static修饰变量,表示这个变量"只在当前 .c 文件内部可见"
  • 如果以后拆分成多文件(第 17 篇),别的文件不能直接访问totalStudents——这是一种封装手段

static 还可以让函数只在当前文件中可见(如果addStudent前加static),这在多文件项目中很常用。


七、初学者常见错误

错误1:在函数外部使用局部变量

voidfunc(void){intx=10;}intmain(void){printf("%d\n",x);// 错误!x 只在 func 内部存在return0;}

错误2:函数返回局部变量的地址

int*getValue(void){intx=42;return&x;// 悬空指针!函数返回后 x 已销毁}

错误3:重复初始化 static 局部变量

voidfunc(void){staticintx=0;// 只在第一次调用时初始化x=0;// 如果想每次调用都重置,用这行}

错误4:用 #define 定义指针类型别名

#definePTR_INTint*PTR_INT a,b;// b 是 int!陷阱!// 正确:typedef int * PTR_INT;

错误5:extern 声明和定义搞混

// 在 .h 文件中intx=10;// 错误!头文件中不应定义变量(多文件包含会重复定义)externintx;// 正确:只声明// 在 .c 文件中intx=10;// 正确:在源文件中定义

八、练习题

练习题1:调用计数器

写一个函数int getID(void),每次调用返回一个递增的唯一编号(1, 2, 3…)。在 main 中调用 5 次,输出每次返回的值。使用 static 局部变量实现。

练习题2:typedef 简化函数指针

用 typedef 定义一个函数指针类型Operation,指向double func(double, double)。然后用Operation声明一个数组并存放加减乘除四个函数的指针,用循环测试。

练习题3:static 的作用域验证

在同一个项目中创建两个 .c 文件(或用注释模拟):在其中一个文件定义static int secret = 42;int shared = 100;,在另一个文件中尝试访问这两个变量。观察哪个能访问、哪个不能。


九、本篇总结

  1. 局部变量函数内部可见,函数返回后销毁;全局变量所有函数可见,程序运行期间一直存在
  2. static 局部变量只在函数内可见,但值在调用之间保持
  3. static 全局变量/函数限制为仅在当前文件内可见,是多文件编程中的封装手段
  4. extern声明变量在其他文件中定义,实现跨文件共享
  5. typedef 给类型起别名,推荐用于简化结构体和函数指针声明,不要用 #define 定义指针类型

http://www.jsqmd.com/news/893005/

相关文章:

  • 2026广州黄埔办证机构排行榜|5家许可证代办实测盘点,靠谱选手避坑清单全整理 - 资讯快报
  • 2026年防水涂料/抗渗防水/屋面防水工程厂家推荐榜:JS防水涂料、水性聚氨酯与彩钢防水胶专业品牌深度解析 - 企业推荐官【官方】
  • 基于码分复用的音频可逆数据隐藏:高容量与高保真的正交嵌入方案
  • 百度竞价托管“水深”在哪?一套标准帮你筛掉90%伪精细化服务商 - GEO优化
  • 2026国产管段式电磁流量计TOP10品牌深度评测:技术突破与市场格局的重塑逻辑 - 液体流量液位品牌推荐
  • 【2024最全Lovable工具栈图谱】:基于137家技术团队实测数据,仅剩最后23个高适配组合
  • 17_预处理条件编译与多文件编程
  • 基于AI代理的求职自动化系统:从简历优化到智能申请全流程实践
  • 2026年苏州专业回收名酒服务商,究竟凭啥在市场脱颖而出? - 资讯快报
  • Unabyss 新手入门与实战部署指南
  • 无锡GEO优化公司哪家口碑最好?(含维度说明+问题解答) - wxxwlm
  • Redis学习总结
  • 【路径规划】基于遗传算法求解低碳冷链物流车辆路径问题(目标函数固定成本 运输成本 制冷成本 惩罚成本 总碳排放成本)附Matlab代码
  • 南京少儿围棋培训哪家好:南京棋院学有所长 - 13425704091
  • AI 智能体实训室:从大模型到教学落地的全链路实践
  • windows下让cmd可以使用相关linux指令配置步骤
  • gitlab的一些使用异常记录
  • 为什么你的Three.js场景又平又假、塑料感拉满?90%前端都踩的灯光大坑!
  • 2026年5月厦门财产分割律师服务能力测评:3家律所处理水平对比 - 奔跑123
  • 基于图注意力网络的医疗欺诈检测:从关系网络挖掘共谋团伙
  • Taotoken助力嵌入式场景下的智能对话应用开发
  • 2026年,苏州那些口碑爆棚的维修保养厂家,你知道几家? - 资讯快报
  • 2027年199 管理类联考 在职考研学习机构哪家好?考研攻略指南:林晨陪你考研,为何能成为管理类联考备考优选 - 资讯速览
  • 壹[1],倍福TwinCat环境搭建
  • go: N-Barrier Pattern
  • cc/ds教学,计算机小白笔记(2.2)
  • alert - So
  • 南京少儿围棋考级培训推荐:南京棋院考级专长 - 19120507004
  • 一文读懂 Agent Skills:AI 智能体的 “超级技能包”
  • 想找靠谱的建站服务商?这6款高实用性工具别错过!