绝对不要坐等成功的到来
本期内容充分用到实例讲解,会更深层次的讲解本期关键字,可能会让你有意想不到的收获,希望每位小伙伴在读任何技术文章的时候都能抱着一个空杯心态,这样收获比可能比你想得要多!
1、你必须懂得 --------- 变量的认识(重点)
2、跑的飞快 --------- register(理解)
⚕️ 3、告知对方 --------- extern(重点)
️♂️ 3、名副其实 --------- static(重点)
首先我们来了解C语言一共有多少个关键字呢?
在我们一般书籍的介绍中,一共有32个关键字,这些是基于C90(C89)的标准,但是在后面的C99标准中新增了5个关键字,不过目前主流的编译器其实对C99的支持并不友好,所以暂时可以先不了解。
定义变量的本质:在内存中开辟一块空间,用来保存数据。
如何定义变量相信大家都知道了,这个我就不细说了,但是我发现有的学校老师写main函数的时候返回值为void或者直接不写返回值,这样有错吗?其实严格意义上这是不规范的写法的!(C/C++标准规定main 函数的返回值应该定义为 int 类型)如果现在有的书上还是void main我建议你考虑扔掉那本书,那么标准的main函数的写法是什么呢?见代码:
其实这里你 return 1; 或者任何数都没问题包括负数编译器都不会报错,但是,我们C语言从开始到现在以来,已经约定了规矩,如果程序是异常结束则返回非0值,程序正常结束则返回0!就像我们去任何一个地方要尊重当地的习俗一样!
接着我们来理解变量的声明:
这里我给大家简单举一个例子,张三喜欢上了隔壁学院的小美,在某一天晚上张三鼓起勇气跟小美告白,于是小美成为了张三的女朋友,这就叫做定义!那么张三晚上回寝室特别开心,他把这个消息告诉了室友李四,也告诉了隔壁寝室的王五,甚至还打电话告诉了她的妈妈,张三给任何人声明小美就是他女朋友,那么这就叫做声明!
声明可以有多次,代码块内定义只能有一次!(详细内容放在extern讲解)
变量我们可以分为局部变量和全局变量:
局部变量:包含在代码块中的变量叫局部变量,同时局部变量具有临时性,当它进入代码块的时候会自动开辟空间,退出代码块会自动释放。
全局变量:在所有函数外定义的变量叫做全局变量,全局变量具有全局性,声明周期从整个进程开始到进程结束。
变量的作用域:指的是该变量的可以被正常访问的代码区域!
变量的生命周期:指的是该变量从定义到被释放的时间范围!
*局部变量:进入代码块,形成局部变量到退出代码块
*全局变量: 定义完成之后,程序运行的整个生命周期内,该变量一直都有
这里我们先简单了解概念,讲static关键字会带入进去以上概念。
见名知意,为什么叫跑的飞快,让然是非常快!首先我们先来看一幅图:
在我们先认识register这个关键字之前先了解一个概念,计算机中CPU是主要负责进行计算的硬件单元,但是为了运算方面,我们一般要先把数据硬盘读取到内存中,再从内存中读取到CPU内,那么CPU也就需要一定的数据临时存储能力,所以现代的CPU内,都集成了一组叫做寄存器的硬件,用来做临时数据的保存,那么我们如果把变量放在寄存器中,不用通过从内存中读取这个步骤的话是不是会更快呢?
寄存器存在的本质:在硬件层面上,提高计算机的运算效率。因为不需要从内存读取数据!
用register修饰变量:尽量将所修饰的变量放到CPU寄存器中,从而达到提高效率的目的!
那么所有变量适合采用register修饰吗?并不是!
- 局部变量(全局变量会导致CPU寄存器长时间被占用)
- 不会被写入的(写入就需要写回内存,后续还要读取检测的话,register的意义在哪呢?)
- 需要高频被读取的(提高效率所在)
建议:如果要使用,请不要大量使用,因为寄存器数量有限!
来到这里我们要注意一个问题,既然被register修饰的变量会被放到CPU寄存器中,那么它还可以进行取地址操作吗???
这里我们win vs编译器的环境下显然是不可以的!
为什么呢?因为地址是内存上的概念。(其他环境未测试)。
最后:这个关键字在目前来看,已经不重要了,主流的编译器已经很智能了,能够进行比人更好的代码优化,我们只需要了解即可,不然面试官问你,你两眼干瞪天花板咋办?
我们先引入一个概念,如果说小伙伴们以后写项目了,只会存在一个.c文件吗?那么全局变量,函数可以跨文件使用吗?我们看代码:
看上面的代码,我们有两个源文件,并且把函数写在basketball.c,同时test.c文件文件调用此函数,请问程序是否可以正常运行?
我们直接让我们代码走起来,会出现警告但是代码仍然可以强制运行,但是这只是函数,那我们的全局变量呢?也可以强制运行吗?
这里我们通过实验发现,全局变量并不能像函数一样可以跨文件直接使用,那么我们如何解决这个问题呢?这里不知道小伙伴们之前学C语言有没有听过一句话:先声明,后使用!我们有基础的都知道声明是extern关键字,那么我们就在对应的test.c里声明一下g_val;
这里我们发现,声明的时候如果赋值那么就会报错,为什么呢?因为声明并没有开辟空间!当 g_val = 100; 这样写的话叫做赋值或初始化,也就是上面我们说的定义只能定义一次!而声明可以声明多次!
那么我们正确声明的写法是什么呢?
由上可见,我们确实可以在多个文件中声明g_val,同时每个文件都引用了
头文件,但是万一以后做项目很多个.c文件我们还是要一一声明吗?每次都引用吗?这样就会大大减少我们程序的可维护性了!那么这里我们就要介绍.h头文件了,那么我们为什么要用头文件呢? 如果我们单纯使用源文件,组织项目结构的时候,项目越大越复杂的时候,维护成本越高,在使用.h头文件,组织项目结构的时候,可以减少大型项目的维护成本问题!
见代码:
头文件:一般包含函数声明,变量声明,宏定义,头文件等内容
#pragma once预处理指令是为了防止头文件重复包含,保证头文件只被编译一次!(目前了解即可)
函数声明可以不带extern(编译器在发现函数没有函数体会把它解释称声明),但是我建议小伙伴们带上,如果函数带参,声明也要带参数!
变量声明必须带上extren(头文件只能包含变量的声明,不能包含变量的定义)。
首先我们先来看这样一个问题,全局变量可以跨文件访问吗?可以!函数可以跨文件访问吗?也可以!那么在具体的应用场景中,有没有可能我们不想让全局变量或者函数跨文件放访问,只在本文件内部访问。我们先来看一个例子:
当我们发现用static修饰了全局变量后,接着运行我们的代码会报出链接错误,得出:static修饰全局变量该变量只能在本文件内被访问,不能被外部其他文件直接访问!但是我们可以间接访问,如下代码:
我们可以看到,我们在baketball.c文件里的Print函数里打印了被static修饰的全局变量,然后通过test.c文件调用了Print,这里我们可以看到仍然可以间接访问被static修饰的全局变量,程序不会报错!
那么如果static修饰函数呢?会出现什么情况?
这里我们看到仍然会报跟上边一样的链接错误,那么我们可以得出:static修饰函数,该函数只能在本文件内被访问,不能在外部其他文件直接访问!但是仍然可以间接访问!
那么还有一个static修饰局部变量呢?会发生什么情况呢?我们看代码:
此时我们fun函数是被static修饰了,那么它的代码块内局部变量也会受影响吗?答案是不会的,因为i是局部变量,具有临时性,函数调用开辟空间并初始化,函数结束,释放空间!
当我们用static修饰函数内部局部变量会发生什么情况呢?
这里我们发现i的值并没有被重置,也就是说在函数fun结束时i并没有被释放空间,那么为什么i不会被释放呢?这里小伙伴们可以不懂可以看下我上一期博客的一个内存分布图快速排序,归并排序导致栈溢出如何解决?(非递归算法)_程序猿教你打篮球的博客-CSDN博客我们知道临时变量都是放在栈区上的,但是一旦我们临时变量被static修饰了就会被放到静态区,所以并不会被释放,直到进程结束!
那么通过上面的static修饰函数和全局变量以及局部变量,我们得出以下结论:
static修饰函数的时候,函数的外部链接属性就被成了内部链接属性,那么它的作用域变小了,但是生命周期还是从进入到销毁(代码块)
static修饰全局变量的时候,全局变量的外部链接属性就被成了内部链接属性,那么它的作用域变小了,但是生命周期还是整个进程
static修饰局部变量的时候,局部变量内部链接属性并没有被改变,也就是作用域没有变,生命周期变成了整个进程
本期几个关键字的讲解到这里就结束了,相信抱着空杯心态看完的你们肯定有所收获吧,我们下期见,接着带你们深度解剖C语言!
能走多远就走多远! Go as far as you can!