🦖作者:学写代码的恐龙
🦖博客主页:学写代码的恐龙博客主页
🦖专栏:【初级c语言】
🦖语录:❀未来的你,一定会感谢现在努力奋斗的自己❀
十一:关键字
11.1:register–寄存器
在计算机中我们可以把数据存放在网盘、硬盘、内存、高速缓存、寄存器里。
他们之间结构成了上图的金字塔结构。越往金字塔的顶端,存储空间越小,访问速度也越来越快,这也导致他们的价格越来越贵。
在计算机中有一个叫CPU的东西,他也被叫做中央处理器,是用来处理数据的,所有的运算都是通过CPU来进行的。最早的时候CPU的运算速度不算快,内存的访问速度也还可以,CPU就会从内存里面拿取数据来进行运算。但随着硬件的发展,CPU的运行速度越来越快,而内存中数据的读取速度没怎么提升,这时尽管CPU的处理速度特别快,但读取数据的速度却显得很慢,这时就诞生了寄存器和高速缓存区,此时CPU直接从寄存器中读取数据,在CPU处理数据的同时,寄存器也会从高速缓存区读取即将要用到的数据,高速缓存区则从内存中读取即将要要用到的数据,这样他们同时工作,大大提升了计算机的工作效率。其中寄存器的单位是字节,我们一般都说有几个寄存器,它集成在CPU上。高速缓存又分为一级缓存,二级缓存,三级缓存等等。
register的作用就是建议编译器把创建的变量值放到寄存器里面,未来对变量的访问就是访问寄存器,从寄存器里面取值,当大量使用到此变量时,从寄存器里面拿效率就会高一些。但register的仅仅起到建议作用,最终是否把变量的值放到寄存器里完全取决于编译器。但现在的编译器都比较聪明,即使不加register他也会对程序进行分析,把一些值放到寄存器里面。
int main() { register a = 10;//仅仅是建议 return 0; }
11.2:typedef
type翻译为:类型,def是英文define的缩写翻译为:定义。顾名思义:typedef叫做类型定义。这里应该理解为类型重命名。
当我们想定义一个无符号的整型变量是需要写unsigned int ,这显得非常的长,不方便,此时我们就可以通过typedef给unsigned int重新取一个名字来代替它。
typedef unsigned int uint; int main() { unsigned int a; uint b; return 0; }
此时我们把unsigned int重新命名为uint,这里的a和b具有相同的数据类型。
11.3:static–静态的
主要有三种用法:
修饰局部变量
修饰全局变量
修饰函数
11.3.1:static修饰局部变量
void text() { int a = 0; a++; printf("%d\n", a); } int main() { int i = 0; while (i < 10) { text(); i++; } return 0; }
这里我么们写了一个循环,当i<10的时候调用text函数,可见一共调用了10次。每调用一次text函数就会打印一个值。
从结果看出这里一共打印了10个1,那为什么呢?
void text() { int a = 0; a++; printf("%d\n", a); }
text是一个函数,这里的大括号是一个局部范围,每次进入这个函数都会创建一个局部变量a,a的生命周期是进入这个局部范围时创建,出这个局部范围时销毁当 i=0 循环第一次调用text函数时,进入函数创建a,然后让a++,打印出a的值为1,出这个函数时a就销毁,然后让i++,i变成1,继续执行循环,调用text函数,由于上一次调用结束时,a已经被销毁,所以此时会重新创建一个a=0,然后a++,a变成1,打印出a的值,出text函数,销毁掉a。就这样重复循环十次,所以就会打印出10个1。
接下来我们在局部变量a的前面加上static进行修饰。
```c void text() { static int a = 0; a++; printf("%d\n", a); } int main() { int i = 0; while (i < 10) { text(); i++; } return 0; }
此时打印出来的结果是1-10.
大家可以这样来想原因,当我们第一次调用text函数的时候,起初定义a=0,a++后a变成1,打印出第一个数字1,当第二次调用text函数的时候,如果上一次的a销毁了,那此时重新创建一个a=0,然后a++,打印出来a的值还是1,但实际却打印的是2,那说明分析的有问题,再看第二次调用text函数的时候执行a++了,结果是2,那说明a原来的值一定是1,那这个1是从哪里来的呢?如果第二次循环重新创建a了,那a就是0,所以这里a=1,说明第一次循环调用text函数结束的时候并没有把a销毁,而是继续保存a=1。因此,这里的a从第一次循环调用text函数时被创建,在每次函数调用结束时没有被销毁,这样a就会产生一个累加效果。
这里本质的原因是,被static修饰后的局部变量会被存储在内存中的静态区。这里就需要扩展一点内存方面的知识。内存中被分了3块区域,分别是栈区、堆区和静态区。栈区中一般是用来存储局部变量和函数的参数这类临时变量。堆区里面是用来动态内存分配的,malloc、calloc、realloc、这些函数都是在堆区中申请的空间。静态区存放的是静态变量和全局变量。栈区的特点是,进入作用域创建,出了作用域就释放。里面存放的变量都是临时的。静态区里面的数据创建好后直到程序结束才销毁。
注意:被static修饰是不会影响作用域的。但是生命周期发生了变化,变长了。
总结一下,当我们希望保留变量上一次的值,这时我们就可以用static来修饰这个变量。
11.3.2:static修饰的全局变量
在之前介绍全局变量作用域的时候给大家举了个例子,就是我们可以用extern来声明一个外部变量,这样我们就可以在A这个源文件中用同一个工程下B这个源文件中定义的全局变量了。
如上图,我们在add.c中定义了一个全局变量a=1,此时我们要是想在text.c中用a的值,这需要在text.c中用extern声明一下这个外部变量a就可以。
如果我们在add.c中的“int a = 1;”前面加上一个static会发生什么结果呢?
可以看出,此时程序报错了“无法解析的外部符号_a”,也就是程序不认识这个符号。为什么会出现这个原因呢?
原因是:全局变量本身是具有外部链接属性的,也就是在同一个工程下的a源文件中定义的全局变量在b源文件中可以通过链接使用。但是如果全局变量被ststic修饰,这个外部连接属性就变成了内部连接属性,这个全局变量只能在自己所在的源文件内使用。static的修饰会把全局变量的外部链接属性变成内部连接属性。最终使得全局变量的作用域变小。(一个工程最终只能编出一个可执行程序,一个text.c可以通过 编译 + 链接 生成一个可执行程序----- .exe。链接这个阶段回去其他的文件找我们想要的符号。(注意:局部变量没有链接属性)
11.3.3:static修饰函数
我们在B源文件中定义了一个函数,在A中直接调用这个函数程序会发出警告。如下图,在add.c源文件中定义了一个函数add在text.c中调用这个函数,程序报警说:add未定义。此时我们可以像声明一个外部变量那样,用extern来声明一个外部函数。(每一个编译器都是对.c文件进行单独编译的)
修改后的代码为:
此时编译器没有报警告。
当我们在add函数的前面加上一个ststic后会发生什么呢?
是不是很熟悉,这结果和上面static修饰全局变量的结果是一样的。
函数本身是具有外部链接属性的,被static修饰后,外部链接属性变成了内部链接属性,使得这个函数只能在自己的这个源文件里使用,其他源文件无法使用。