前四节,我们从编写一个计算器的角度,讨论出编程语言需要数据类型、变量和常量,并详细介绍了这三个概念。那么编写一个计算器还需要哪些东西呢?
我们想想看,当我们想设计一个比较复杂的程序,例如开发一款游戏时,我们不可能写在一个文件下吧,那这个文件得多庞大。
比如说王者荣耀,是一个团队共同开发的,例如我设计韩信的动作和技能介绍,他设计李白的凤求凰皮肤,另一个人设计镜的出装。用膝盖想一想,这么多人不可能围在一起对一个文件指指点点,否则策划与程序员之间,岂不是天天“你在教我做事?”
虽然不在一块,但各个文件之间没有交流么?
有时候李白和王昭君在同一局游戏碰到,会产生一些奇怪的台词,所以不同文件是有交流的,那怎么产生联系呢?
这就是今天的重点——存储类型。
存储类型是干嘛的呢?
简单来说,存储类型是用来定义C程序变量/函数的范围(可见性)和生命周期的。具体分为四类:auto、register、static、extern。
我们挨个来看:
auto存储类
auto是用来修饰局部变量的,如果局部变量前没有存储类型,默认为auto类型,例如我们通常看到的int a;,实际是auto int a;。
register存储类
register是寄存器类型,顾名思义,是把修饰的变量放到寄存器中,目的是为了提高程序的运行效率。但要记住,不管是单片机也好,计算机也罢,任何CPU的寄存器资源都是有限的,如果寄存器满了,被修饰的变量就会默认回到auto类型。
一般格式:register 数据类型 变量;
extern存储类
前面提过,如何在不同文件之间交流?
嘿嘿,extern就是一个很好的方法。
extern是外部引用类型,主要是引用统一工程中的全局变量或函数,什么意思呢?就是说,我想用其他文件的变量或函数,那么就需要使用extern这个关键字。
我们上一节提到过,定义在内存上是分配空间的,而使用extern是在声明,意思是告诉编译器该变量存在空间,但并不是在声明处开辟的空间。
怎么去用呢?
假设在第一个c文件,我们定义:int a;在另一个文件我们想要使用第一个文件中的a,我们就需要在第二个c文件中加上extern int a;这样这个a就在这两个文件共享。
static存储类
static这个关键字非常重要,为啥呢?因为面试题经常考。
当static修饰全局变量或函数时,限制变量或函数的作用域,表示只能在本文中使用;当static修饰局部变量时,改变局部变量的存放位置,延长局部变量的生命周期,运算的结果保留上一次的结果。
“改变局部变量的存放位置”,怎么理解这句话呢?
这就要提到C语言的内存管理了,这个我们后面详细说,这里我们简单提一下:
首先,我们要知道计算机中的内存是分区来管理的,程序和程序之间的内存是独立的,不能互相访问,比如QQ和王者荣耀分别所占的内存区域是不能相互访问的。而每个程序的内存也是分区管理的,一个应用程序所占的内存可以分为很多个区域。
我们需要了解的区域有四个,分别是栈区、堆区、静态区和代码区。通常我们定义的局部变量,是存放在栈区的,并且局部变量的生命周期在该语句块执行结束时便结束了。
但我们用static修饰,该变量便存放在静态数据区,其生命周期一直持续到整个程序执行结束。但是其作用域并没有改变,它仍然是一个局部变量,作用域仅限于该语句块。
而当static修饰全局变量或函数时,该变量或函数仅在本文使用,就没法和其他文件产生交流,用extern关键字都不行。
这个我们一般什么时候用呢?
因为每一个大工程都不是一个人完成的,当某人在某一文件使用一个全局变量时,另一个人在其他文件可能也定义了一个相同的全局变量,这样会发生冲突,所以干脆用static修饰,以防发生奇怪的BUG。
因为有了static这个关键字,变量从全局变量和局部变量这两种,就变成了全局变量、局部变量、静态全局变量、静态局部变量四种。
他们之间怎么区别呢?
全局变量、局部变量、静态全局变量、静态局部变量的区别
从作用域看:
1、全局变量具有全局作用域。全局变量只需在一个源文件中定义,就可以作用于所有的源文件。当然,其他不包含全局变量定义的源文件需要用extern 关键字再次声明这个全局变量。
2、静态局部变量具有局部作用域,它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见。
3、局部变量也只有局部作用域,它是自动对象(auto),函数的一次调用执行结束后,变量被撤销,其所占用的内存也被收回。
4、静态全局变量也具有全局作用域,它作用于定义它的文件里,不能作用到其它文件里,即被static关键字修饰过的变量具有文件作用域。这样即使两个不同的源文件都定义了相同名字的静态全局变量,它们也是不同的变量。
从分配内存空间看:
1、全局变量,静态局部变量,静态全局变量都在静态存储区分配空间,而局部变量在栈里分配空间。
2、全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。这两者的区别虽在于非静态全局变量的作用域是整个源程序。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。
· 1)静态变量会被放在程序的静态数据存储区(全局可见)中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆栈变量和堆变量的区别。
· 2)变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。
总结
本节介绍了auto、register、static、extern这四种存储类型,并介绍了全局变量、局部变量、静态全局变量、静态局部变量这四种变量的区别,对于内存管理这一块的知识点大家能看懂最好,不能看懂也没关系,后面我们会详述。存储类型到这里就是说完了,下一节将进入到运算符的学习。
上节作业分析
1.下列程序的输出结果是16.00,请填空
#include
int main()
{
int a = 9, b = 2;
float x = _____,y = 1.1, z;
z = a/2 + b*x/y + 1/2;
printf("%5.2f\n", z);
return 0;
}
已知a = 9,b = 2;z = 9/2 + 2*x/1.1 + 1/2;在C语言中,如果运算时,浮点型向整型进行强制转换时注意精度丢失的问题
怎么理解呢?定义的a和b是整数,9/2 = 4.5,但由于a是int型,得到的结果为整数,即4,而1/2则为0,所以z = 4+2*x/1.1 = 16,解方程得x = 6.6
2.以下程序运行后的输出结果是:( )
#include
#define S(x) 4*x*(x)+1
int main()
{
int i=6,j=8;
printf("%d\n",S(i+j));
return 0;
}
根据上一节我们说的,遇到这种宏定义问题要整体代入,S(i+j) = 4*i+j*(i+j)+1 = 4*6+8*(6+8)+1 = 137
本节作业
下面程序输出结果是什么?
温馨提示:第一节有一个很奇怪的链接,大家记得保存哦~