模块化设计,static和extern(面试题常见)

文章目录

  • 一、函数的声明和定义
    • [1.1 单个文件](#1.1 单个文件)
    • [1.2 多个文件](#1.2 多个文件)
    • [1.3 static和extern](#1.3 static和extern)
      • [1.3.1 static修饰局部变量](#1.3.1 static修饰局部变量)
      • [1.3.2 static修饰全局变量](#1.3.2 static修饰全局变量)
      • [1.3.3 static修饰函数](#1.3.3 static修饰函数)
  • 总结

一、函数的声明和定义

1.1 单个文件

一般我们在使用函数的时候,直接将函数写出来就使用了

题目:写一个函数判断一年是否是闰年

图中4-10行是函数的定义,16行这里是函数的调用,在这种场景下,函数的定义在函数的调用之前,没啥问题。

而在另一个场景下,当我们需要把函数的定义放在函数的调用后边,因为编译器是从前mian的函数往后处理代码的,当调用函数的时候,编译器不知道这是个什么东西,就会报警告了。

怎么解决呢?那就是在前面告诉编译器一声,有这么个函数,这就叫函数的声明

声明函数只要交代清楚:函数名,函数的返回类型和函数的参数。

如:int is_leap_year(int y);

函数声明中参数只保留类型,省略名字也是可以的(即y),因为声明中的y不会用到,定义里才用得到。

代码变成这样就可以正常编译了

有的兄弟就要问了,你前面虽然把函数的定义放到前面了,但也没有声明呀。

那是因为函数的定义是一种特殊的声明。

1.2 多个文件

一般在企业中写代码的时候,代码可能比较多,有个几w行,不会将所有的代码都放到一个文件里,往往会根据程序的功能,把代码拆分到多个文件里,这里很好理解,比如说有30个程序员,难道要他们在一个文件里写代码吗,难不成还要分前后顺序?就算给共享文档也会出现问题,而且每个人都要看前一个人写的代码,读起来也费劲,这不符合互相协调模块化程序

一般情况下,函数的声明、类型的声明放在头文件(add.h)中,函数的定义放在(add.c),函数的实现放在源文件(test_05_30_01.c)中,例如这样:

这里是点击名字,进行拖拉,把三个文件放在一个窗口上看的。

补充:因为是在不同的文件中,在源文件中想要实现函数需要包含头文件,因为文件时自己创建的,不是标准库里提供的,所以包含的样式有所区别:

c 复制代码
#include"add.h"

使用双引号就可以了。

稍作对比就可以发现,库函数和这里的原理是对应的。

当然add.c不用必须与add.h的名字一样,这里之所以一样只是为了对应起来更加直观,其他地方调用也只要包含对应的头文件就可以了。

这样多文件的形式就达到了以下两种效果

  1. 方便模块化编程
  2. 方便协作

比如现在让5个程序员来写一个计算器程序,这个计算机的程序可以分为4个大块

  • 加法
  • 减法
  • 乘法
  • 除法
c 复制代码
 A        B        C        D         E
add.c   sub.c    mul.c     div.c    test.c
add.h   sub.h    mul.h     div.h    #include"add.h"
                                    #include"sub.h"
                                    ......

这样每个程序员写好自己的函数声明和定义,最后一个程序员头文件整合在一起不就方便多了吗?

其实这里把头文件移除,add.h的头文件也不包含了,强行运行程序也是可以的

但是会报警告,因为没有声明。这里还有另一种解决办法。

因为函数来自外部文件,在(test_05_30_01.c)中声明需要在前面加一个extern,extern是用来声明外部符号的。

这样就不会报警告了

这里还是推荐第一种,包含头文件的方式,因为包含头文件其实就相当于把add.h里的声明拷贝过来,把#include"add.h"替换掉,本质上就相当于声明。

还有另一种好处在下一篇文章去讲。
价值10w的游戏引擎该怎么卖?

1.3 static和extern

static和extern都是C语言中的关键字

static是静态的的意思,可以用来:

  • 修饰局部变量
  • 修饰全局变量
  • 修饰函数

extern是用来声明外部符号的,外部符号可以是全局变量,函数等

谈起static和extern之前再讲一下:作用域和生命周期

**作用域(scope)**是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效的,而限定这个名字的可用性的代码范围就是这个名字的作用域。

一句话来概括,这个名字在哪里可以使用,哪里就是作用域

  1. 局部变量的作用域就是变量坐在的局部范围

    可以看到这里的n就是局部变量,它的范围就是第二个{ }里面,这个{ }外的printf运行的时候就会发生报错。

  2. 全局变量的作用域就是整个工程(项目)

    甚至在函数内,其他.c文件内也可以作用,附以代码截图


    不过全局变量在其他.c文件里想要使用,需要声明外部符号extern,告诉编译器这个n来自外部,是其他源文件的,所以全局变量无论是在哪里定义的,整个工程想使用都是可以的。

生命周期指的是变量的创建(申请内存)到变量的销毁(收回内存)之间的一个时间段。

  1. 局部变量的生命周期是:进入作用域变量创建,生命周期开始,出作用域生命周期结束
  2. 全局变量的声明周期是:整个程序的生命周期(即main函数的生命周期,从main函数开始到return 0)

1.3.1 static修饰局部变量

正常情况下,这个程序输出的结果就是5个2

test函数中的局部变量n是每次进入test函数先创建变量(生命周期开始)并赋值为0,然后++,再打印,出函数的时候变量生命周期将要结束(释放内存)

但加入static修饰局部变量n之后,就输出2,3,4,5,6

稍作反推,我们就能推导出这里n具有了累加的效果,其实是test函数中的n创建好之后,出函数的时候是不会销毁的,重新进入函数也就不会重新创建变量。

每次用的是上一次留下来的值,相当于static int n = 1;是个摆设,追究底层原理也可以进入调试转到反汇编看一下

C语言代码全转换成汇编语句时,没一串代码转换出的汇编指令数量不定,但是按f11进入函数之后,static int n = 1;无汇编指令,而且继续按f11调试的时候,会跳过这条语句。

由此我们可以得出结论:static修饰局部变量改变了变量的生命周期,生命周期改变的本质是改变了变量的存储类型

这是代码在内存中的存储,本来一个局部变量时存储在内存的栈区的,但是被static修饰后存储到了静态区。存储在静态区的变量和全局变量是一样的,生命周期就和程序的生命周期一样了,只有程序结束,变量才销毁,内存才回收,但是作用域不变,即还是只能在局部变量使用。

1.3.2 static修饰全局变量

这串代码再熟悉不过了,extern是用来声明外部符号的,如果一个全局变量的符号在A文件中定义的,在B文件中想使用,就可以使用extern进行声明,然后使用

但是如果我们在test2.c里的全局变量n前加static后

现象原因:

一个全局变量被static修饰,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使用

现象解释:

本质原因是全局变量默认是具有外部链接属性的,在外部的文件中想要使用,只要是党的声明就可以使用。但是全局变量被static修饰之后,外部连接属性就变成了内部连接属性,只能在自己所在的源文件内部使用了,其他源文件,即使声明了,也是无法正常使用的。

1.3.3 static修饰函数

这里和siatic修饰全局变量是类似的

正常情况下是可以运行的

但加入static之后修饰函数之后

一个函数在整个工程都可以使用,但被static修饰后,只能在本文件内部使用,其他文件无法正常的链接使用了

本质是因为函数默认是具有外部链接属性,使得函数在整个工程中只要适当声明就可以被使用,但是被static修饰后变成了内部链接属性,使得函数只能在自己所在的源文件内部使用。

总结:当一个全局变量或函数只想在源文件内部使用,不想被其他源文件使用,就可以使用static修饰。


总结

例如:以上就是今天要讲的内容,本文算是数组的彻底结束了,下一篇文章主播开始写扫雷,主播写这篇文章最后也是燃尽了,喜欢的兄弟们不要忘了一键三连给予支持。

相关推荐
零千叶4 小时前
【面试】Kafka / RabbitMQ / ActiveMQ
面试·kafka·rabbitmq
GilgameshJSS5 小时前
STM32H743-ARM例程9-IWDG看门狗
c语言·arm开发·stm32·单片机·嵌入式硬件·学习
Hello_Embed5 小时前
STM32 智能垃圾桶项目笔记(一):超声波模块(HC-SR04)原理与驱动实现
c语言·笔记·stm32·单片机·嵌入式软件·嵌入式项目
菠萝地亚狂想曲5 小时前
极简文件列表
c语言
麻雀20256 小时前
一键面试prompt
面试·职场和发展·prompt
PAK向日葵6 小时前
【算法导论】NMWQ 0913笔试题
算法·面试
PAK向日葵6 小时前
【算法导论】DJ 0830笔试题题解
算法·面试
PAK向日葵6 小时前
【算法导论】LXHY 0830 笔试题题解
算法·面试
big\hero6 小时前
蓝桥杯13届省题
职场和发展·蓝桥杯
聪明的笨猪猪6 小时前
面试清单:JVM类加载与虚拟机执行核心问题
java·经验分享·笔记·面试