文章目录
0. 前言
首先来看一个问题
如果我有两个.c文件,main.c
和test.c
相信第一反应是:不可以,需要先extern声明一下
我们来看一下结果
虽然会有一些警告,显然是可以的,那么是为什么呢?
为什么不同的.c文件中的函数可以直接调用呢?
这是因为
程序去找show函数的定义是什么时候呢?
是在程序最后一步链接生成可执行文件(.obj)的时候
也就是说
在编译的时候,编译器在本源文件中可能没有找到show函数(这时候,不同源文件中的内容是不互通的)
这时候就生成了警告,但是在最后一步链接的时候,编译器会到其他.c文件中寻找show函数,如果找到了,就可以正常运行,如果连接的时候还找不到,就会报错了!
解决方法:
利用extern声明全局变量
而如果extern的时候进行赋值可以吗?
!不可以
因为声明并不会开辟空间,=100叫做赋值或者初始化
而赋值或者初始化的前提就是需要有空间
而这里没有开辟空间,自然就会报错!
所有的变量声明的时候,不能设置初始值!
1. 为什么要用.h文件
2. .h文件中的声明
写在.h文件中的内容主要有
- C头文件
- 所有的变量的声明
- 所有的函数的声明
- #define,类型typedef ,struct
变量的声明
extern g_val;
函数的声明
不需要带函数体
{}
因为函数体的作用是开辟空间,声明不需要开辟空间
extern void show();
注意:
函数声明可以不带extern
但是全局变量必须要带extern
原因:
- 全局变量需要带extern:
int g_val
容易被编译器误认为是没初始化的定义 - 函数可以不带:函数声明后面的分号
;
就可以告诉编译器后面没有函数体,只是声明
3. static关键字
两个问题:
- 全局变量可以跨文件访问吗
- 函数可以跨文件访问吗
答案都是可以!
那么有没有具体的应用场景,我们不想让全局变量或者函数跨文件访问,只在本文件内部被访问?
这时候就用到static
关键字了
static修饰全局变量
static修饰局部变量,则该变量只在本文件内被访问,不能被其他文件直接访问,但可以间接访问,修改的是全局变量的作用域
虽然不能直接访问,但是可以借助函数来间接访问
如图:
这样就可以利用调用show函数来实现访问static修饰的g_val变量了!
static修饰函数
static修饰某一个函数,该函数只能在本文件内被访问,不能在外部其他文件直接访问,只能间接访问
也可以理解成缩小了函数的作用域
如图 如果把show函数用static修饰起来,那么就会出现以下错误
也就是不可以直接访问show函数了
但是可以利用一个其他函数来间接访问show函数
static修饰局部变量
static修饰局部变量,更改该局部变量的生命周期,该局部变量的空间不会被释放!直到工程结束才会释放
一旦一个局部变量被static修饰,初始化动作只有一次(第一次),因为定义只有一次!
但并不是局部变量就变成了全局变量
作用域还是本代码块
只是它的生命周期变成了全局!
例子:
我们知道,局部变量具有临时性,函数调用开辟空间并初始化,
当函数结束的时候,释放空间还给操作系统,这时候变量就销毁了
看一个例子
运行结果:
每一次调用fun函数都会新创建一个i,结束函数的时候销毁,所以每一次的i是相同的
如果用static修饰局部变量 i
结果是:
我们打印地址来看一下
可以看到,每一次的地址都是相同的,也就是说明该局部变量没有被释放!
实际上static是C语言中一种安全机制
进行项目维护,提供安全保证
你不想让别人用的函数或者全局变量就可以用static修饰,这样别人就没法直接使用这些内容了
4. 为什么默认允许函数和全局变量跨文件访问
因为有一定规模的项目,一定是多文件的,多个文件之间,后序需要进行数据"交互"
可以通过包含头文件#include"test.h"
来使得main.c
里面可以使用test.c
中的函数或者变量
而如果不能进行跨文件,那么”交互成本" 就比较高,即使利用头文件也不能跨文件使用,借助别的东西间接访问,成本太高了
但是总有一些需要隐藏的内容
这时候就体现出static的作用了
5. 两个拓展问题
- 为什么临时变量具有临时性
- 为什么全局变量的生命周期是全局的
如图是一个 进程地址空间
- 局部变量都是放在栈区的,栈区会有出栈和压栈的过程,定义一个变量就会压栈,销毁一个变量就会出栈,这个过程是动态的,栈区的变量具有临时性,所以局部变量(临时变量)具有临时性
- 全局变量或者用static修饰的变量并不存放在栈区,他们放在全局数据区,全局数据区的数据在整个“进程”运行的生命周期内都是有效的,“进程”就是运行中的程序