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

文章目录

  • 一、函数的声明和定义
    • 1.1 单个文件
    • 1.2 多个文件
    • 1.3 static和extern
      • 1.3.1 static修饰局部变量
      • 1.3.2 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)中,例如这样:
在这里插入图片描述
这里是点击名字,进行拖拉,把三个文件放在一个窗口上看的。

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

#include"add.h"

使用双引号就可以了。
稍作对比就可以发现,库函数和这里的原理是对应的。
当然add.c不用必须与add.h的名字一样,这里之所以一样只是为了对应起来更加直观,其他地方调用也只要包含对应的头文件就可以了。

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

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

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

  • 加法
  • 减法
  • 乘法
  • 除法
 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修饰。


总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/82125.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PySide6 GUI 学习笔记——常用类及控件使用方法(地址类QUrl)

文章目录 地址类QUrl主要功能URL 格式介绍常见 scheme(协议)类型QUrl 类常用方法常用方法示例典型应用场景 地址类QUrl QUrl 是 PySide6.QtCore 模块中的一个类,用于处理和操作 URL(统一资源定位符)。它可以解析、构建…

GEE:获取研究区的DEM数据

最近有粉丝追更 GEE 系列,说上次看完 DEM 代码解析后,自己试着改了一版。今天咱们就来拆解他的优化版代码 ——基于 SRTM 数据获取研究区 DEM 并导出,顺便聊聊怎么把 GEE 代码写得更专业! 先下结论:代码逻辑完整,3 处细节值得新手抄作业! 这版代码在数据加载→裁剪→可…

汽车安全 2030 预测 (功能安全FuSa、预期功能安全SOTIF、网络安全CyberSecurity):成本、效益与行业影响

汽车安全 2030 预测 (功能安全FuSa、预期功能安全SOTIF、网络安全CyberSecurity):成本、效益与行业影响 到 2030 年,汽车行业将迎来一场安全技术的深度变革,其中 “三重安全防护”(功能安全 FuSa、预期功能安全 SOTIF、网络安全&…

深入理解设计模式之状态模式

深入理解设计模式之:状态模式(State Pattern) 一、什么是状态模式? 状态模式(State Pattern)是一种行为型设计模式。它允许一个对象在其内部状态发生改变时,改变其行为(即表现出不…

Redis的大Key问题如何解决?

大家好,我是锋哥。今天分享关于【Redis的大Key问题如何解决?】面试题。希望对大家有帮助; Redis的大Key问题如何解决? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Redis中的“大Key”问题是指某个键的值占用了过多…

【STM32开发板】接口部分

一、USB接口 可以看到USBP和USBN与PA12,PA11引脚相接,根据协议,需要添加上拉电阻 二、ADC和DAC 根据原理图找到可以作为ADC和DAC的引脚 ADC和DAC属于模拟部分的,所以要接模拟地 三、指示灯电路 找几个通用的引脚,因为单片机的灌电流比拉电流…

[Hackers and Painters] 读书笔记 | 设计模式思想 | LISP

目录 黑客与艺术的关系 “如果你有两个选择,就选择较难的那个“ 金钱不等于财富。创造有价值的东西就是创造财富 项目公式 探讨关于优秀程序员的话题 摘抄 保罗格雷厄姆其人其事 人物经历 图书介绍 个人作品编辑 译者序 为什么书呆子不受欢迎 黑客与画…

Linux基本指令/下

目录 1.echo、cat与printf 2. > 操作符 与 >> 操作符 3. < 操作符 4.消息传送 linux文件深入 5.文件类型 6.mv命令 7.时间相关指令 8.查找命令 9.grep命令 10.zip/unzip/tar命令 11.scp命令 12.bc命令 13.uname 指令 14.快捷键大全 15.关机/重启/睡…

Apptrace:APP安全加速解决方案

2021 年&#xff0c;某知名电商平台在 “618” 大促期间遭遇 DDoS 攻击&#xff0c;支付系统瘫痪近 2 小时&#xff1b;2022 年&#xff0c;一款热门手游在新版本上线时因 CC 攻击导致服务器崩溃。观察发现&#xff0c;电商大促、暑期流量高峰和年末结算期等关键商业周期&#…

NodeJS全栈开发面试题讲解——P1Node.js 基础与核心机制

✅ 1.1 Node.js 的事件循环原理&#xff1f;如何处理异步操作&#xff1f; 面试官您好&#xff0c;我理解事件循环是 Node.js 的异步非阻塞编程核心。 Node.js 构建在 V8 引擎与 libuv 库之上。虽然 Node.js 是单线程模型&#xff0c;但它通过事件循环&#xff08;event loop&a…

【深度学习】sglang 的部署参数详解

SGLang 的部署参数详解 SGLang(Structured Generation Language)是一个高性能的大语言模型推理框架,专为结构化生成和多模态应用设计。本文将全面介绍SGLang的部署参数,帮助你充分发挥其性能潜力。 🚀 SGLang 项目概览 SGLang是由UC Berkeley开发的新一代LLM推理引擎,…

MATLAB实战:机器学习分类回归示例

以下是一个使用MATLAB的Statistics and Machine Learning Toolbox实现分类和回归任务的完整示例代码。代码包含鸢尾花分类、手写数字分类和汽车数据回归任务&#xff0c;并评估模型性能。 %% 加载内置数据集 % 鸢尾花数据集&#xff08;分类&#xff09; load fisheriris; X_i…

数组。。。。。

//创建Book 对象&#xff0c;放入相关的属性private String name;//书名private String[] writer;//作者private List<String> reader;//读者private Map<String, Object> topics;//评讲Book book new Book();book.setName("昆虫总动员");book.setWrite…

day16 leetcode-hot100-31(链表10)

25. K 个一组翻转链表 - 力扣&#xff08;LeetCode&#xff09; 1.模拟法 思路 将这个过程拆解为两个步骤&#xff0c;第一步将单分组的节点反转&#xff0c;第二步将反转后的链表加入原链表。 针对节点反转很容易&#xff0c;参考之前的206. 反转链表 - 力扣&#xff08;Le…

测试总结(二)

持续集成 软件开发实践 开发提交代码到gitlab上 自动化构建&#xff08;编译、打包、部署、自动化测试&#xff09; 尽早发现集成问题 过程&#xff1a; 提交代码-人工/定时触发-自动构建-自动部署-构建成功-获取构建环境信息-邮件通知-自动测试 快速集成、快速反馈、快速解决…

(二)微服务(grpc/grpc消费者)

文章目录 项目地址一、grpc介绍1.1 项目初始化1. 创建grpc项目2. 项目结构二、Discount grpc创建2.1 实体层1. Coupon实体2.2 Protos1. 创建discount.proto2. 配置proto3. 创建DiscountService4. Program里注册服务2.3 Seed 数据1. 创建表和Seed数据2. 自动migration2.4 更新Do…

InfluxQL 数据分析实战:聚合、过滤与关联查询全解析

InfluxQL 作为时序数据库的专用查询语言&#xff0c;在处理时间序列数据时展现出独特优势。本文深入探讨 聚合计算、数据过滤和跨测量关联 三大核心操作&#xff0c;通过真实代码示例展示如何从海量时序数据中提取关键洞察。文中涵盖从基础平均值计算到复杂多维度分析的完整流程…

TDengine 运维——巡检工具(安装前检查)

简介 本文档旨在介绍 TDengine 安装部署前后配套的巡检工具。 相关工具的功能简介&#xff1a; 工具名称功能简介安装前检查部署前对 TDengine 安装部署的依赖要素进行安装前检查安装前预配置部署前对 TDengine 安装部署的依赖要素进行安装前预配置安装部署指定环境安装部署…

大语言模型(LLM)入门 - (1) 相关概念

文章来自&#xff1a;大语言模型(LLM)小白入门自学项目-TiaoYu-1 GitHub - tiaoyu1122/TiaoYu-1: For People! For Freedom!For People! For Freedom! Contribute to tiaoyu1122/TiaoYu-1 development by creating an account on GitHub.https://github.com/tiaoyu1122/TiaoYu…

LearnOpenGL-笔记-其十一

Normal Mapping 又到了介绍法线贴图的地方&#xff0c;我感觉我已经写了很多遍了... 法线贴图用最简单的话来介绍的话&#xff0c;就是通过修改贴图对应物体表面的法线来修改光照效果&#xff0c;从而在不修改物体实际几何形状的前提下实现不同于物体几何形状的视觉效果。 因…