在讲全局变量和局部变量之前,先讲一下作用域和生命周期
作用域和生命周期
作⽤域(scope):是程序设计概念,通常来说,⼀段程序代码中所用到的名字并不总是有效(可⽤)的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
⽣命周期:指的是变量的创建(申请内存)到变量的销毁(收回内存)之间的⼀个时间段。
简单的说,作用域是该变量的使用范围。生命周期是变量从创建到销毁的时间段。
全局变量与局部变量
局部变量
概念:定义在函数(包括main函数)内部的变量。
作用域:变量所在的局部范围。即从定义的那一行开始, 直到遇到}结束或者遇到return为止。
生命周期:进⼊作⽤域变量创建,⽣命周期开始,出作用域⽣命周期结束。
存储位置: 局部变量会存储在内存的栈区中。
特点: 相同作用域内不可以定义同名变量,不同作用范围可以定义同名变量,内部作用域的变量会覆盖外部作用域的变量。例如当全局变量与局部变量名字相同时,优先使用局部变量。
全局变量
概念:定义在函数外面的变量称为全局变量。
作用域:整个⼯程(项⽬)。
生命周期:程序一启动就会分配存储空间,直到程序结束。
存储位置:静态存储区
特点: 多个同名的全局变量指向同一块存储空间。
局部变量/全局变量作用域举例说明
局部变量的作用域:
全局变量的作用域:
局部变量/全局变量的区别
1、全局变量在全部程序(整个工程)执行结束后才释放内存,而局部变量是在定义该变量时创建,在作用域之后就销毁该变量释放内存。
2、两者的存储空间不同,全局变量存放在静态区(全局区),局部变量存放在栈区。
拓展:静态区用于存放全局变量、静态变量(用static修饰的局部变量和全局变量)。
3、全局变量在未初始化化时系统会赋值为0,而局部变量未初始化时会赋随机值,故局部变量在定义时就往往赋值为0,以免该变量被系统赋随机值。
4、全局变量与局部变量重名时优先使用局部变量。
static关键字用法
1.static修饰局部变量
①用static关键字修饰的局部变量定义变量的代码在整个程序运行期间仅仅会执行一次,第二次遇到时会自动跳过。
②用static关键字修饰的局部变量的生命周期变长,有static后,生命周期由原来的函数执行完即销毁变为从程序启动到程序退出,但作用域不变。
根本原因在于用static修饰之后,存放在了静态区(一般局部变量是放在栈区中的,用完即销),生命周期与全局变量相同,和程序的生命周期一样。
③用static关键字修饰的局部变量具有记忆功能,未来⼀个变量出了函数后,我们还想保留值,等下次进⼊函数继续使用,就可以使⽤static 修饰。
例如:
#include<stdio.h>
void test();
int main()
{
test();
test();
test();
return 0;
}
void test()
{
static int num = 0; // static修饰局部变量 ,该代码在程序中只运行一次
num++; // 如果不加static输出 1 1 1 // 如果添加static输出 1 2 3
printf("num = %i\n", num);
}
2.static修饰全局变量/函数
全局变量和函数是具有外部链接属性的(就是本文件中的全局变量和函数可以在其他文件中使用(使用之前需要声明))
当用static修饰之后,之前的外部链接属性就变为了内部链接属性,本文件的全局变量/函数只能在本文件中使用,其他文件不可使用,即使声明了也不可使用。
默认情况下多个同名的全局变量共享一块空间, 这样会导致全局变量污染问题,如果想让某个全局变量只在某个文件中使用,并且不和其他文件中同名全局变量共享同一块存储空间, 那么就可以使用static。
全局变量分类: 内部变量:只能在本文件中访问的变量 外部变量:可以在本文件中以及其他文件中访问的变量,默认所有全局变量都是外部变量
函数分类:内部函数:只能在本文件中访问的函数 外部函数:可以在本文件中以及其他的文件中访问的函数
其他注意点:
如果只有函数声明添加了static,而定义中没有添加static,那么编译会报错,定义要与声明一致。
extern关键字用法
首先讲讲变量/函数声明的意义:
声明的意义在于:(告诉编译器)我在本文件/其他文件中已经定义了该全局变量/函数,可以放心使用该变量/函数。(让编译器在文件中去找这个全局变量/函数),函数/变量声明可以多次声明,但定义只能定义一次。
extern声明:
1、extern的英文是外部的,extern多用于在本文件中声明一个外部(其他文件)的全局变量/函数。
例如:有两个文件led.c文件和main.c文件,如果main.c文件想用led.c中的一个全局变量,就要在main.c文件的开头用extern对变量进行声明。
2、extern也可对本文件的全局变量声明,但本文件的全局变量往往在文件开头就已经定义了,就不需要声明了。
3、而声明一个局部变量是没有效果的,因为只有运行到局部变量的定义时,才会开辟该局部变量的存储空间。
extern声明的简化(省略extern):
函数的外部声明extern往往省略。而全局变量的外部声明需要用extern。(理论上全局变量也具有外部链接属性,extern也可以省略,但例如:声明一个外部变量a,用extern int a;,如果省略了,就变成int a;,这是一个定义,不是声明,故有些编译器会报错(如keil5),故对外部全局变量声明时,extern往往不省略。
extern也可用于对外部函数/全局变量的定义,但默认的情况下所有的函数/全局变量都是外部函数/所有的全局变量都具有外部链接属性,故常常省略extern。
.c文件和.h文件的概念
源文件(.c):一般用于存放各种函数的定义。
头文件(.h):一般存放源文件中需要的各种宏定义以及变量和函数的声明。
我的理解是源文件将各种琐事都交给头文件,自己只去完成函数的功能设计。
一般源文件的第一条程序就是包含自己的头文件,例如led.c文件的第一条就是include "led.h"。而include "led.h"就相当于把头文件的代码复制到了源文件的开头。
extern用法总结
在本文件中需要使用其他文件的全局变量/函数时
1、如果是全局变量,只需在本文件的.h文件或.c文件中用extern声明一下即可(例如:extern int a;),不要重复定义。
2、如果是函数,只需在本文件的.h文件或.c文件中声明一下即可,extern往往省略掉,但加上也可以。
3、若main.c没有自己的main.h文件,往往包含许多其他头文件,可在main.c文件中声明变量或函数或任意一个头文件中声明即可。
4、extern多应用于外部文件中全局变量的声明。
auto和register关键字
auto关键字
只能修饰局部变量, 局部变量如果没有其它修饰符, 默认就是auto的。
特点: 随用随开, 用完即销。
register关键字
只能修饰局部变量, 原则上将内存中变量提升到CPU寄存器中存储, 这样访问速度会更快,但是由于CPU寄存器数量相当有限,通常不同平台和编译器在优化阶段会自动转换为auto。
const关键字
可以定义 const 常量。
const 可以修饰函数的参数、返回值,甚至函数的定义体。
被 const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
C语言中用const修饰的局部变量存放在栈区,用const修饰的全局变量存放在常量区。
故用const修饰的局部变量可以用指针来修改,而用const修饰的全局变量则不能被修改。
STM32用const修饰之后存在Flash里,不占用运行内存SRAM的空间。
欢迎讨论提问,共同学习进步,望诸君共勉。