模块化设计,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的名字一样,这里之所以一样只是为了对应起来更加直观,其他地方调用也只要包含对应的头文件就可以了。
这样多文件的形式就达到了以下两种效果
- 方便模块化编程
- 方便协作
比如现在让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)**是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
一句话来概括,这个名字在哪里可以使用,哪里就是作用域
-
局部变量的作用域就是变量坐在的局部范围
可以看到这里的n就是局部变量,它的范围就是第二个{ }里面,这个{ }外的printf运行的时候就会发生报错。 -
全局变量的作用域就是整个工程(项目)
甚至在函数内,其他.c文件内也可以作用,附以代码截图
不过全局变量在其他.c文件里想要使用,需要声明外部符号extern,告诉编译器这个n来自外部,是其他源文件的,所以全局变量无论是在哪里定义的,整个工程想使用都是可以的。
生命周期指的是变量的创建(申请内存)到变量的销毁(收回内存)之间的一个时间段。
- 局部变量的生命周期是:进入作用域变量创建,生命周期开始,出作用域生命周期结束
- 全局变量的声明周期是:整个程序的生命周期(即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修饰。
总结
例如:以上就是今天要讲的内容,本文算是数组的彻底结束了,下一篇文章主播开始写扫雷,主播写这篇文章最后也是燃尽了,喜欢的兄弟们不要忘了一键三连给予支持。
相关文章:

模块化设计,static和extern(面试题常见)
文章目录 一、函数的声明和定义1.1 单个文件1.2 多个文件1.3 static和extern1.3.1 static修饰局部变量1.3.2 static修饰全局变量1.3.3 static修饰函数 总结 一、函数的声明和定义 1.1 单个文件 一般我们在使用函数的时候,直接将函数写出来就使用了 题目:写一个函数…...

【快速解决】数据库快速导出成sql文件
1、cmd直接打开 输入命令 mysqldump -u用户名 -p密码 数据库名 > 导出文件名.sql修改成自己mysql的用户名和密码,和要导出的数据库名称,给导出的文件起一个名字。 如图所示 这样就成功了。...
使用 Syncfusion 在 .NET 8 中生成 PDF/DOC/XLS/PPT
Syncfusion 是一个功能强大的控件库,提供了多种工具来生成和处理 PDF、Word、Excel 和 PowerPoint 文档。在 .NET 8 中,使用 Syncfusion 可以简化生成这些文档的流程,并确保生成的文件高效、准确。本文将介绍如何在 .NET 8 中使用 Syncfusion…...

LearnOpenGL-笔记-其十二
今天我们来将LearnOpenGL的高级光照部分彻底完结: Bloom 泛光是一个非常常见的用于改善图像质量的手段,其主要做法就是将某个高亮度区域的亮度向四周发善以实现该区域更亮的视觉效果(因为显示器的亮度范围有限,需要通过泛光来体…...
【C++】C++面向对象设计的核心思想之一: 接口抽象、解耦和可扩展性
1. 什么是虚函数? 虚函数(virtual)是C里实现“多态”的关键机制。 在基类中声明虚函数,在子类中可以**覆盖(override)**它们。通过基类指针/引用操作时,自动调用实际对象(子类&…...

Namespace 命名空间的使用
名字空间:划分更多的逻辑空间,有效避免名字冲突的问题 1.什么是命名空间 名字命名空间 namespace 名字空间名 {...} // 名字空间 n1 域 namespace n1 {// 全局变量int g_money 0;void save(int money){g_money money;}void pay(int money){g_money - m…...

mac 下安装Rust Toolchain(Nightly)
你可以用 Homebrew 安装 rustup,这是推荐的管理 Rust toolchain的 brew install rustup-init安装 Rust(包含 rustup) rustup-init安装过程中会让你选择安装那个,直接回车选择默认的即可 安装完成后,cargo, rustc, r…...
PHP中文网文章内容提取免费API接口教程
接口简介: 提取PHP中文网指定文章内容。本接口仅做内容提取,未经作者授权请勿转载。 请求地址: https://cn.apihz.cn/api/caiji/phpzww.php 请求方式: POST或GET。 请求参数: 【名称】【参数】【必填】【说明】 【用…...
【Java笔记】Spring IoC DI
目录 Spring IoC & DI1. IoC1.1 Bean的存储1.1.1 类注解1.1.2 方法注解 Bean1.1.3 重命名1.1.4 Spring扫描路径 2. DI Spring IoC & DI Spring两个核心思想:IoC & AOP Spring相当于一个容器,IoC就是把对象存放在Spring容器中,让…...

学习STC51单片机22(芯片为STC89C52RCRC)
记住这个AT指令千万不要去脑子记,要用手册查 每日一言 努力不是为了感动谁,而是为了不辜负自己的野心。 硬件:ESP8266 wife模块 蓝牙,ESP-01s,Zigbee,NB-lot等通信模块都是基于AT指令的设计 老样子 我们用…...
ubuntu20.04.5--arm64版上使用node集成java
ubuntu20.04.5arm上使用node集成java #ssh,可选 sudo apt update sudo apt install openssh-server sudo systemctl status ssh sudo systemctl enable ssh sudo systemctl enable --now ssh #防火墙相关,可选 sudo ufw allow ssh sudo ufw allow 22…...

Linux --UDP套接字实现简单的网络聊天室
一、Server端的实现 1.1、服务端的初始化 ①、创建套接字: 创建套接字接口: #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int socket(int domain, int type, int protocol); //1. 这是一个创建套接字的接…...
嵌入式学习笔记 - keil安装目录下的头文件自动包含问题
Keil MDK/MDK-ARM(ARM编译器)默认情况下会自动包含其安装目录下的标准头文件路径(如CMSIS库、设备头文件等)。具体机制如下: 默认自动包含: 新建工程或使用设备数据库选择芯片型号后,Keil会…...

word批量导出visio图
具体步骤 修改word格式打开VBA窗口插入代码运行代码 修改word格式 将word文档修改为docm格式 打开VBA窗口 打开开发工具VisualBasic项,如果没有右键在自定义功能区添加 插入代码 插入 -> 模块,代码如下: Sub ExportAllVisioDiagrams()D…...
把数据库做得能扩展:Aurora DSQL 的故事
把数据库做得能扩展:Aurora DSQL 的故事 我们在 AWS re:Invent 上发布了 Aurora DSQL,这是一个全新方式构建关系型数据库的尝试。它不是单纯的技术升级,而是一段从零开始、反复试错、不断学习的工程旅程。 我们为什么做 Aurora DSQL&#x…...
全面解析:npm 命令、package.json 结构与 Vite 详解
全面解析:npm 命令、package.json 结构与 Vite 详解 一、npm run dev 和 npm run build 命令解析 1. npm run dev 作用:启动开发服务器,用于本地开发原理: 启动 Vite 开发服务器提供实时热更新(HMR)功能…...

【本地部署】 Deepseek+Dify创建工作流
文章目录 DeepseekDify 简介流程1、下载Docker2、Dify下载3、使用浏览器打开 Deepseek Deepseek 是一款功能强大的 AI 语言模型工具,具备出色的理解与生成能力。它可以处理各种自然语言任务,无论是文本创作、问答,还是数据分析与解释&#x…...
Rust 配置解析`serde` + `toml`
🦀 Rust 配置解析:彻底搞懂 TOML、Option、Vec、derive 背后的原理 📌 目录 什么是 TOML 文件?为什么要用 serde toml crate?结构体上 #[derive(...)] 是什么?配置中数组 [] 和表数组 [[...]] 怎么用&…...
linux进程用户态内存泄露问题从进程角度跟踪举例
我们习惯性的会看下那个进程在泄漏内存,我这里使用一个test_malloc的测试进程,该进程每2秒钟会分配一个10000字节的空间,并作简单赋值(注意:如果仅malloc而不使用,编译器会优化,实际测试时将看不…...

数据结构-图的应用,实现环形校验和拓扑排序
文章目录 一、如何理解“图”?1.什么是图?2.无向图和有向图3.无权图和有权图 二、JGraphT-图论数据结构和算法的 Java 库1.引入Maven依赖2.环形校验2.1 什么是循环依赖 ?2.2 单元测试代码2.3 情况1:自己依赖自己2.4 情况2…...
交换机 路由器
在计算机网络中,S 和 R 常常分别代表以下设备: S:Switch(交换机)R:Router(路由器) 简要说明: Switch(交换机,S) 交换机工作在数据链…...

某乎x-zse-96 破解(补环境版本)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、总体概述二、请求分析分析请求流程三、逆向分析总结一、总体概述 本文主要实现某乎x-zse-96 破解(补环境版本),相关的链接: https://www.zhihu.com/search?type=content&q=%25E7%258…...

VSCode+Cline 安装配置及使用说明
安装配置 打开VSCode,点击左侧Extension图标,在弹出页面中,检索Cline,选择Cline进行安装。 安装完毕,在左侧会出现一个图标,点击图标 选择【Use your own API key】,在出来的界面中选择大模型&…...
Java中Redis面试题集锦(含过期策略详解)
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Java中Redis面试题集锦(含过期策…...

Maven 安装与配置指南(适用于 Windows、Linux 和 macOS)
Apache Maven 是一款广泛应用于 Java 项目的项目管理和构建工具。 本文提供在 Windows、Linux 和 macOS 系统上安装与配置 Maven 的详细步骤,旨在帮助开发者快速搭建高效的构建环境。 一、前置条件:安装 Java Development Kit (JDK) Maven 依赖于 Java …...

android 媒体框架之MediaCodec
一、MediaCodec 整体架构与设计思想 MediaCodec 是 Android 底层多媒体框架的核心组件,负责高效处理音视频编解码任务。其架构采用 生产者-消费者模型,通过双缓冲区队列(输入/输出)实现异步数据处理: 输入缓冲区队列…...

堆与堆排序及 Top-K 问题解析:从原理到实践
一、堆的本质与核心特性 堆是一种基于完全二叉树的数据结构,其核心特性为父节点与子节点的数值关系,分为大堆和小堆两类: 大堆:每个父节点的值均大于或等于其子节点的值,堆顶元素为最大值。如: 小堆:每个…...
Linux中检查当前用户是不是root
Linux中检查当前用户是不是root 检查当前用户是否为root用户。如果是root用户,输出“当前用户是root”;否则,输出“当前用户不是root”。 创建一个 aaa.sh脚本文件 写入如下内容 #!/bin/bash# 检查当前用户的UID是否为0(root用…...

软件锁:守护隐私,安心无忧
数字化时代,手机已成为我们生活中不可或缺的一部分,它不仅存储着我们的个人信息、照片、聊天记录等重要数据,还承载着我们的社交、娱乐和工作等多种功能。然而,这也意味着手机上的隐私信息面临着诸多泄露风险。无论是家人、朋友还…...

无人机桥梁3D建模、巡检、检测的航线规划
无人机桥梁3D建模、巡检、检测的航线规划 无人机在3D建模、巡检和检测任务中的航线规划存在显著差异,主要体现在飞行高度、航线模式、精度要求和传感器配置等方面。以下是三者的详细对比分析: 1. 核心目标差异 任务类型主要目标典型应用场景3D建模 生成…...