目录
1、作用域与生命周期
1.1 作用域
1.2 生命周期
1.3 变量的作用域和生命周期之间的关系
2、static 和 extern
2.1 static 修饰局部变量:
2.2 static 修饰全局变量(包含extern的作用):
2.3 static修饰函数:
结语:
本篇文章将介绍两个关键字 static 、extern的作用
1、作用域与生命周期
在介绍 static 和 extern之前,我们需要先了解:作用域与生命周期
1.1 作用域
作用域(scope)是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效(可用)的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
- 局部变量的作用域是变量所在的局部范围。
- 全局变量的作用域是整个工程(项目)。
该怎么理解呢?举个例子
int main()
{{int a = 10;printf("a1 = %d\n", a);}// printf("a2 = %d\n", a); return 0;
}
a1可以打印,a2不可以打印
从上图我们可以看到a2显示是未声明的标识符,是因为a2并不在int a = 10 所在的大括号内。
a只能在局部范围内使用。所以a的作用域就是大括号范围。
因此我们可以知道局部变量的作用域就是变量所在的局部范围。
那全局变量的作用域我们也就可以推算出来,全部变量的作用域是整个工程。
int a = 10;//全局变量
void test()//子函数
{printf("a3 = %d\n", a);
}
int main()//主函数
{{printf("a1 = %d\n", a);}printf("a2 = %d\n", a); return 0;
}
不管a在哪里,只要在该工程内,都是可以使用的,所以全局变量的作用域是整个工程。
1.2 生命周期
生命周期指的是变量的创建(申请内存)到变量的销毁(收回内存)之间的⼀个时间段。
- 局部变量的生命周期是:进入作用域变量创建,生命周期开始,出作用域生命周期结束。
- 全局变量的生命周期是:整个程序的生命周期。
这个该怎么理解呢?
首先是关于局部变量生命周期的理解:
int main()
{{int a = 10;printf("a1 = %d\n", a);}// printf("a2 = %d\n", a); return 0;
}
图解:
从上图我们可以看到,当a出了大括号范围后,a就不能使用了
所以,当a创建的时候,生命周期开始,当a出作用域后就没法用了,所以被销毁,也就是生命周期结束。
所以局部变量的生命周期是:进入作用域变量创建,生命周期开始,出作用域生命周期结束
所以我们也可以认为全局变量的生命周期就是整个程序的生命周期。
1.3 变量的作用域和生命周期之间的关系
变量的作用域和生命周期密切相关,但又有所不同。
作用域决定了变量在程序中的哪些部分可以被访问和操作,而生命周期则决定了变量存在和有效的时间段。
一般来说,变量的作用域会影响其生命周期。
当一个变量具有局部作用域(例如在函数内部定义的非静态变量)时,它的生命周期通常与函数的执行相关。函数被调用时变量被创建,函数执行结束后变量被销毁,其生命周期相对较短。
而具有全局作用域的变量,或者在函数内部使用 static 关键字修饰的变量,其作用域通常更广,生命周期也更长,从程序开始运行一直到程序结束。
例如:
void fun()
{int a = 10; // 局部变量,作用域在 fun 函数内,生命周期随函数执行开始和结束
}int b = 20; // 全局变量,作用域整个程序,生命周期整个程序
在上述示例中,a 的作用域仅限于 fun 函数,其生命周期也在函数执行期间。而 b 的作用域是整个程序,生命周期也是从程序启动到程序结束。
作用域限定了变量能被使用的范围,而生命周期决定了变量存在的时间跨度,作用域在一定程度上会反映和影响变量的生命周期。
2、static 和 extern
static 和 extern 都是C语⾔中的关键字。
static 是静态的的意思,可以用来:
- 修饰局部变量
- 修饰全局变量
- 修饰函数
extern 是用来声明外部符号的。
2.1 static 修饰局部变量:
代码1:不含static
void test()
{int a = 5;a++;printf("%d ", a);
}
int main()
{int i = 0;for (i = 0; i < 6; i++){test();}return 0;
}
该代码的结果会是多少呢?来一起看一下
欸,6个6,怎么得到的呢?
在上述代码中,test 函数内部定义的变量 a 是一个局部变量。
每次调用 test 函数时,都会为 a 分配新的内存空间,并初始化为 5 。然后 a 自增并打印其值。
在 main 函数的 for循环中,连续调用了 6 次 test 函数。
由于每次调用都是独立的,a 的值都会重新从 5 开始计算,然后自增到 6 并打印。
所以会连续打印出 6个6 。
代码2:含static
void test()
{static int a = 5;//加上statica++;printf("%d ", a);
}
int main()
{int i = 0;for (i = 0; i < 6; i++){test();}return 0;
}
该代码的结果又会是什么呢?
现在的结果变为了6 7 8 9 10 11。为什么呢?
好像这里每一次调用函数后a产生的值没有被刷新,当下一次调用的时候,会继续使用上一次留下来的值。也就是说进来的a不会再重新创建。
为什么呢?我们可以通过对比来理解static的作用
代码1的test函数中的局部变量i是每次进入test函数先创建变量(生命周期开始)并赋值为0,然后 ++,再打印,出函数的时候变量⽣命周期将要结束(释放内存)。
代码2中,我们从输出结果来看,i的值有累加的效果,其实test函数中的i创建好后,出函数的时候是 不会销毁的,重新进⼊函数也就不会重新创建变量,直接上次累积的数值继续计算。
我们知道内存被划分为栈区,堆区,和静态区
分别存放的东西:
栈区:局部变量、形式参数
堆区:动态内存分配,malloc,calloc,realloc等函数
静态区:全局变量,静态变量
结论:static修饰局部变量改变了变量的生命周期,生命周期改变的本质是改变了变量的存储类型,本来⼀个局部变量是存储在内存的栈区的,但是被 static 修饰后存储到了静态区。存储在静态区的变量和全局变量是一样的,生命周期就和程序的生命周期⼀样了,只有程序结束,变量才销毁,内存才回收。但是作用域不变的。
也就是说被static修饰的局部变量会增长生命周期,即使出了作用域,也不会被销毁。
当a被声明为static时,它具有以下特点:
- 只会被初始化一次。
- 在函数调用之间,其值会被保留。
注意:static修饰的变量是可以被改变的。
2.2 static 修饰全局变量(包含extern的作用):
首先,我们现在add.c文件定义一个全局变量int g_val = 2024;
然后再xu.c中使用全局变量intg_val
结果展示:
哎,为什么打印不出来呢?
这时候我们需要给代码做一点小小的改变,我们再int main()上面加上一行代码
extern int g_val; 这时候我们再来看一下
没有报错,成功打印了。
从上面对比你知道extern的作用了吗?
extern 是用来声明外部符号的,如果⼀个全局的符号在A文件中定义的,在B文件中想使用,就可以使用extern进行声明,然后使用。
如果我们用static来修饰全局变量会产生什么影响呢?来看一下
我们可以发现,原来可以运行的程序由于用static修饰int g_val无法运行了,这是为什么呢?
这就是static修饰全局变量的作用:
使用static修饰全局变量,会使得被static修饰的全局变量只能在本源文件内使用,不能在其他源文件内使用。
结论:
⼀个全局变量被static修饰,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使用。本质原因是全局变量默认是具有外部链接属性的,在外部的文件中想使用,只要适当的声明就可以使用;但是全局变量被 static 修饰之后,外部链接属性就变成了内部链接属性,只能在自己所在的源文件内部使用了,其他源文件,即使声明了,也是无法正常使用的。
所谓的外部链接属性,就是可以跨文件使用。
使用建议:如果⼀个全局变量,只想在所在的源文件内部使用,不想被其他文件发现,就可以使用static修饰。
2.3 static修饰函数:
static修饰函数与修饰全局变量非常相似。
当使用extern声明add(int x, int y)后,图中函数add(int x, int y)能否使用呢?
肯定是可以的,但是,当我们使用static修饰函数add(int x, int y)后,程序是否还能运行成功吗?
不能正常运行。所以static修饰函数后,函数也就不能跨文件使用了。
static 修饰函数和 static 修饰全局变量是一模一样的,一个函数在整个工程都可以使用, 被static修饰后,只能在本⽂件内部使用,其他文件无法正常的链接使用了。
结语:
本篇文章将告一段落,这篇文章主要讲了两个关键字static 和 extern的作用以及用法
同时还介绍了两个名词作用域与生命周期,在介绍static的用法中,使用了将代码放在多个文件的用法,明天会更新将代码拆分放在多个文件的操作,期待大家的到来!!!