于灵动的变量变幻间:函数与计算逻辑的浪漫交织(下)
大家好啊,我是小象٩(๑òωó๑)۶
我的博客:Xiao Xiangζั͡ޓއއ
很高兴见到大家,希望能够和大家一起交流学习,共同进步。
这一节我们主要来学习单个函数的声明与定义,static和extern…
这里写目录标题
- 一、单个函数的声明与定义
- 1.1 单个文件
- 1.2 多个文件
- 二、static和extern
- 2.1 作用域和生命周期
- 2.2 static
- 2.3 static修饰局部变量
- 2.4 static修饰全局变量(extern声明外部符号)
- 2.5 static修饰函数
- 三、结尾
一、单个函数的声明与定义
1.1 单个文件
一般我们在使用函数的时候,直接将函数写出来就使用了。
咱们举之前练习过的一个例子来看,比如写一个函数判断一年是否是闰年。
#include<stdio.h>
int pan_duan_run_nian(int x)
{if ((x % 4 == 0) && (x % 100 != 0) || (x % 400 == 0))return 1;elsereturn 0;
}int main()
{int year = 0;scanf("%d", &year);int a = pan_duan_run_nian(year);if (a == 1){printf("%d为闰年", year);}else{printf("%d不是闰年");}return 0;
}
在这个代码中,这一部分代表的是**函数的定义**:
关于函数的定义:
在C语言中,函数定义是编程中的一个核心概念,它允许你将代码组织成可重用和可维护的模块。函数定义的作用主要包括以下几个方面:
1、代码重用:通过定义函数,你可以避免在多个地方重复编写相同的代码。当你需要在程序中多次执行某个任务时,只需调用定义好的函数即可,从而提高了代码的重用性。
2、模块化编程: 函数将程序划分为多个逻辑模块,每个模块负责完成特定的任务。这种结构化的编程方式使得程序更易于理解和维护。
3、提高可读性:将复杂的程序逻辑分解为多个函数,每个函数都有明确的目的,这可以提高代码的可读性。阅读和理解一个由多个简单函数组成的程序,比阅读一个包含大量复杂逻辑的单体程序要容易得多。
4、便于调试: 模块化编程使得在程序出现问题时更容易定位错误。你可以单独测试每个函数,确定哪个函数存在错误,从而减少了调试的复杂性。
5、增强代码的可维护性: 当需要修改程序时,只需修改相关的函数,而不需要在整个程序中搜索和修改代码。这大大简化了维护过程,降低了维护成本。
6、促进代码协作:在大型项目中,多个开发人员可以独立地编写和测试各自的函数。通过定义清晰的函数接口,开发人员可以协同工作,确保代码的正确性和一致性。
7、实现递归(后面会学): 函数定义允许函数自身调用自身(递归),这是解决某些类型问题(如排序、搜索等)的有效方法。
#include<stdio.h>
int pan_duan_run_nian(int x)
{if ((x % 4 == 0) && (x % 100 != 0) || (x % 400 == 0))return 1;elsereturn 0;
}
而这一部分代表着**函数的调用`**:
int a = pan_duan_run_nian(year);
然后,如果我们把函数的定义放在函数调用的后面,像这样:
int main()
{int year = 0;scanf("%d", &year);int a = pan_duan_run_nian(year);if (a == 1){printf("%d为闰年", year);}else{printf("%d不是闰年");}return 0;
}#include<stdio.h>
int pan_duan_run_nian(int x)
{if ((x % 4 == 0) && (x % 100 != 0) || (x % 400 == 0))return 1;elsereturn 0;
}
这个代码在VS2022上编译,会出现下面的警告信息:
这是因为C语言编译器对源代码进行编译的时候,第一行往下扫描的,当遇到pan_duan_run_nian函数调用的时候,并没有发现前面有pan_duan_run_nian的定义,就报出了上述的警告。
把怎么解决这个问题呢?就是函数调用之前先声明一下pan_duan_run_nian这个函数,声明函数只要交代清楚:函数名,函数的返回类型和函数的参数。
如:pan_duan_run_nian(int x);这就是函数声明,在C语言中,函数声明(Function Declaration) 是 告诉编译器有关函数的存在、其返回类型、函数名以及参数类型和数量的声明 。函数声明通常位于函数定义之前或在其他文件中,以便编译器在函数调用之前知道函数的签名(即函数的接口)。函数声明中参数只保留类型,省略掉名字也是可以的。
函数声明的作用:
1、提前通知编译器:编译器在编译代码时,需要知道函数的签名,以便在函数调用时进行类型检查。函数声明提供了这一信息,使得编译器可以在函数实际定义之前进行编译。
2、避免链接错误:在多个文件的项目中,函数声明允许编译器在编译单独的源文件时知道其他文件中定义的函数的存在。这样,链接器(Linker)可以在链接阶段找到这些函数的实际定义。
3、提高代码可读性:函数声明提供了函数的接口信息,有助于其他程序员理解如何使用该函数。
所以我们给上面的函数加上声明,就会变成下面这样:
pan_duan_run_nian(int x);#include<stdio.h>
int main()
{int year = 0;scanf("%d", &year);int a = pan_duan_run_nian(year);if (a == 1){printf("%d为闰年", year);}else{printf("%d不是闰年");}return 0;
}int pan_duan_run_nian(int x)
{if ((x % 4 == 0) && (x % 100 != 0) || (x % 400 == 0))return 1;elsereturn 0;
}
结果便正常了
函数的调用一定要满足,先声明后使用;
函数的定义也是一种特殊的声明,所以如果函数定义放在调用之前也是可以的。
1.2 多个文件
一般在企业中我们写代码时候,代码可能比较多,不会将所有的代码都放在⼀个文件中;我们往往会根据程序的功能,将代码拆分放在多个文件中,也就是模块化处理。
注意:虽然一个工程会有多个文件,但main函数只能有一个
⼀般情况下,函数的声明、类型的声明放在头文件(.h)中,函数的实现是放在源文件(.c)文件中。
我们来举个例子,我们用test.c来调用其它模块代码进行测试,我们用leap.h和leap.c分别来对于函数的声明和函数的定义
像这样:分别创建test.c和leap.c源文件,leap.h头文件
我们来举一个加法的例子:
我们先来看函数测试的雏形:
#include<stdio.h>
#include"leap.h" //你的头文件是leap.h
int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);int c = add(a, b);printf("%d", c);return 0;
}
我们把它放在test.c中
这里我们要注意要把leap.h头文件也包含进去#include"leap.h"
接着,我们看函数的声明:
int add(int x, int y);//函数的声明
我们把它放在leap.h头文件中
最后是函数的定义:
int add(int x, int y)//函数的定义
{int d = x + y;return d;
}
我们把它放在leap.c的源文件中
这样我们就写完了,运行看看结果:
没有问题
二、static和extern
static 和 extern 都是C语言中的关键字。
2.1 作用域和生命周期
**作用域(scope)**是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效(可用)的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
- 局部变量的作用域是变量所在的局部范围。
- 全局变量的作用域是整个工程(项目)。
生命周期指的是变量的创建(申请内存)到变量的销毁(收回内存)之间的一个时间段。
- 局部变量的生命周期是:进入作用域变量创建,生命周期开始,出作用域生命周期结束。
- 全局变量的生命周期是:整个程序的生命周期(就是程序从开始运行到结束)。
2.2 static
在C语言中,static关键字有多种用途,其中之一是用于修饰函数。当static关键字用于函数声明时,它改变了函数的链接属性(linkage)。
static函数的特点:
1、内部链接(Internal Linkage):
当一个函数被声明为static时,它的链接属性变为内部链接。这意味着该函数只能在声明它的源文件(.c文件)内部被访问。其他源文件无法直接调用这个函数,即使这些文件包含了该函数的声明。
这有助于封装和隐藏实现细节,防止其他文件直接访问和修改。
2、避免命名冲突:
由于static函数只能在其所在的文件内部被访问,因此不同源文件中的同名static函数不会引起命名冲突。
这允许开发者在不同的源文件中使用相同名称的函数,而无需担心命名冲突。
3、生命周期:
static关键字对函数的生命周期没有影响。函数的生命周期总是从程序开始执行到程序结束。static关键字仅影响函数的链接属性,即函数的可见性。
static 是 静态的 的意思,可以用来:
• 修饰局部变量
• 修饰全局变量
• 修饰函数
2.3 static修饰局部变量
我们先来看个例子:
#include<stdio.h>
void test()
{int i = 0;i++;printf("%d", i);
}int main()
{int i = 0;for (i = 0; i < 5; i++){test();}return 0;
}
结果是这样的:
如果我们这样改:
#include<stdio.h>
void test()
{static int i = 0;i++;printf("%d", i);
}int main()
{int i = 0;for (i = 0; i < 5; i++){test();}return 0;
}
结果就会变成这样:
我们来对比代码1和代码2的效果
代码1的test函数中的局部变量i是每次进入test函数先创建变量(生命周期开始)并赋值为0,然后++,再打印,出函数的时候变量生命周期将要结束(释放内存)。
代码2中,我们从输出结果来看,i的值有累加的效果,其实test函数中的i创建好后,出函数的时候是不会销毁的,重新进入函数也就不会重新创建变量,直接上次累积的数值继续计算。
结论:static修饰局部变量改变了变量的生命周期(被static修饰后生命周期会变长),生命周期改变的本质是改变了变量的存储类型,本来一个局部变量是存储在内存的栈区的,但是被 static 修饰后存储到了静态区。 存储在静态区的变量和全局变量是⼀样的,生命周期就和程序的生命周期⼀样了,只有程序结束,变量才销毁,内存才回收。但是作用域不变的。
使用建议:未来一个变量出了函数后,我们还想保留值,等下次进入函数继续使用,就可以使用static修饰。
2.4 static修饰全局变量(extern声明外部符号)
extern 是用来声明外部符号的,如果一个全局的符号在A文件中定义的,在B文件中想使用,就可以使用 extern 进行声明,然后使用。
我们首先创建两个源文件,然后我们在其中一个源文件创建一个变量,然后另一个源文件用extern声明一下,像下面的做法一样:
int xiaofeixiang = 666;
#include<stdio.h>
int main()
{extern int xiaofeixiang;//声明一下printf("%d", xiaofeixiang);return 0;}
我们运行看看结果:
运行一切正常
如果我们这个时候在变量旁边添加static的话:
static int xiaofeixiang = 666;
这个时候我们如果运行的话,就会发现运行错误:
结论:
static相当于改变作用域一个全局变量被static修饰,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使用。
本质原因是**全局变量默认是具有外部链接属性的**,在外部的文件中想使用,只要适当的声明就可以使用;但是全局变量被 static 修饰之后,外部链接属性就变成了内部链接属性 ,只能在自己所在的源文件内部使用了,其他源文件,即使声明了,也是无法正常使用的。
使用建议:如果一个全局变量,只想在所在的源文件内部使用,不想被其他文件发现,就可以使用static修饰。
2.5 static修饰函数
直接来个例子:
int add(int x, int y)
{return x + y;
}
#include<stdio.h>
extern int add(int x, int y);
int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);int c = add(a,b);//声明一下printf("%d",c );return 0;}
这里的extern是指声明外部函数的作用
我们运行一下,结果没有问题:
如果我们在函数前面加上static的话,会发生什么呢,我们来看看:
static int add(int x,int y);
看看结果,直接就报错了:
其实 static 修饰函数和 static 修饰全局变量是一模一样的,一个函数在整个工程都可以使用,被static修饰后,只能在本文件内部使用,其他文件无法正常的链接使用了。
本质是因为 函数默认是具有外部链接属性,具有外部链接属性,使得函数在整个工程中只要适当的声明就可以被使用。但是 被 static 修饰后变成了内部链接属性,使得函数只能在自己所在源文件内部使用。
使用建议:一个函数只想在所在的源文件内部使用,不想被其他源文件使用,就可以使用 static 修饰。
三、结尾
这一课的内容就到这里了,下节课继续学习递归的其他一些知识
如果内容有什么问题的话欢迎指正,有什么问题也可以问我!
相关文章:

于灵动的变量变幻间:函数与计算逻辑的浪漫交织(下)
大家好啊,我是小象٩(๑ω๑)۶ 我的博客:Xiao Xiangζั͡ޓއއ 很高兴见到大家,希望能够和大家一起交流学习,共同进步。 这一节我们主要来学习单个函数的声明与定义,static和extern… 这里写目录标题 一、单个函数…...

python实现pdf转word和excel
一、引言 在办公中,我们经常遇收到pdf文件格式,因为pdf格式文件不易修改,当我们需要编辑这些pdf文件时,经常需要开通会员或收费功能才能使用编辑功能。今天,我要和大家分享的,是如何使用python编程实现…...
Pandas使用笔记
个人学习笔记 日期转换 索引日期格式:2023-09-12 15:00:00 转换为:2023-09-12 import pandas as pd# 假设你的 DataFrame 名为 df,索引是 2023-09-12 15:00:00 # 这里创建一个示例 DataFrame 用于演示 data {value: [1, 2, 3]} index pd…...
高等数学学习笔记 ☞ 定积分与积分公式
1. 定积分的基本概念 1.1 定积分的定义 1. 定义:设函数在闭区间上有界。在闭区间上任意插入若干个分点,即, 此时每个小区间的长度记作(不一定是等分的)。然后在每个小区间上任意取,对应的函数值为。 为保证每段的值(即矩形面积)无…...

wow-agent---task2使用llama-index创建Agent
一:创造俩个函数,multiply和add作为fuction calling被LLM当做工具来使用,实现计算一个简单的计算题: from llama_index.llms.ollama import Ollama from llama_index.core.agent import ReActAgent from llama_index.core.tools …...

RabbitMQ实现延迟消息发送——实战篇
在项目中,我们经常需要使用消息队列来实现延迟任务,本篇文章就向各位介绍使用RabbitMQ如何实现延迟消息发送,由于是实战篇,所以不会讲太多理论的知识,还不太理解的可以先看看MQ的延迟消息的一个实现原理再来看这篇文章…...
Oracle 拉链式merge sort join 原理
Oracle 拉链式Merge Sort Join 的原理,我用一个生活中的比喻来解释。 --- 比喻场景:匹配快递包裹和收件人 1. 快递包裹清单 想象我们有一个快递公司送货的包裹清单,清单按照收件人的邮编(ZIP Code)排序: …...
QModbusTCPClient占用内存持续增长
最近使用QModbusTCPClient通信,需要频繁发送读写请求,发现软件占用内存一直在增减,经过不断咨询和尝试,终于解决了。 1.方案一(失败) 最开始以为是访问太频繁,导致创建reply的对象比delete re…...
代码中使用 Iterable<T> 作为方法参数的解释
/*** 根据课程 id 集合查询课程简单信息* param ids id 集合* return 课程简单信息的列表*/ GetMapping("/courses/simpleInfo/list") List<CourseSimpleInfoDTO> getSimpleInfoList(RequestParam("ids") Iterable<Long> ids); 一、代码解释&…...
Oracle数据库传统审计怎么用
Oracle数据库传统审计怎么用 审计功能开启与关闭By Session还是By AccessWhenever Successful数据库语句审计数据库对象审计查看审计策略和记录Oracle数据库审计功能分为传统审计(Traditional Auditing)和统一审计(Unified Auditing)。统一审计是从Oracle 12c版本开始引入的…...
leetcode-买卖股票问题
309. 买卖股票的最佳时机含冷冻期 - 力扣(LeetCode) 动态规划解题思路: 1、暴力递归(难点如何定义递归函数) 2、记忆化搜索-傻缓存法(根据暴力递归可变参数确定缓存数组维度) 3、严格表结构依…...

MYSQL学习笔记(三):分组、排序、分页查询
前言: 学习和使用数据库可以说是程序员必须具备能力,这里将更新关于MYSQL的使用讲解,大概应该会更新30篇,涵盖入门、进阶、高级(一些原理分析);这一篇是讲解分组、排序、分页查询,并且结合案例进行讲解;虽…...

上位机工作感想-2024年工作总结和来年计划
随着工作年限的增增长,发现自己越来越不喜欢在博客里面写一些掺杂自己感想的东西了,或许是逐渐被工作逼得“成熟”了吧。2024年,学到了很多东西,做了很多项目,也帮别人解决了很多问题,唯独没有涨工资。来这…...
【视觉惯性SLAM:十六、 ORB-SLAM3 中的多地图系统】
16.1 多地图的基本概念 多地图系统是机器人和计算机视觉领域中的一种关键技术,尤其在 SLAM 系统中具有重要意义。单一地图通常用于表示机器人或相机在环境中的位置和构建的空间结构,但单一地图在以下情况下可能无法满足需求: 大规模场景建图…...

【C++笔记】红黑树封装map和set深度剖析
【C笔记】红黑树封装map和set深度剖析 🔥个人主页:大白的编程日记 🔥专栏:C笔记 文章目录 【C笔记】红黑树封装map和set深度剖析前言一. 源码及框架分析1.1 源码框架分析 二. 模拟实现map和set2.1封装map和set 三.迭代器3.1思路…...

4.若依 BaseController
若依的BaseController是其他所有Controller的基类,一起来看下BaseController定义了什么 1. 定义请求返回内容的格式 code/msg/data 返回数据格式不是必须是AjaxResult,开发者可以自定义返回格式,注意与前端取值方式一致即可。 2. 获取调用…...
vue项目配置多语言
本文详细介绍如何在 Vue 项目中集成 vue-i18n 和 Element-UI ,实现多语言切换;首先通过 npm 安装 vue-i18n 和相关语言包,接着在配置文件中设置中文和英文的语言信息;最后在 main.js 中导入并挂载多语言实例,实现切换地…...
数据可视化大屏设计与实现
本文将带你一步步了解如何使用 ECharts 实现一个数据可视化大屏,并且如何动态加载天气数据展示。通过整合 HTML、CSS、JavaScript 以及后端接口请求,我们可以构建一个响应式的数据可视化页面。 1. 页面结构介绍 在此例中,整个页面分为几个主…...

PDF文件提取开源工具调研总结
概述 PDF是一种日常工作中广泛使用的跨平台文档格式,常常包含丰富的内容:包括文本、图表、表格、公式、图像。在现代信息处理工作流中发挥了重要的作用,尤其是RAG项目中,通过将非结构化数据转化为结构化和可访问的信息࿰…...

多监控m3u8视频流,怎么获取每个监控的封面图(纯前端)
文章目录 1.背景2.问题分析3.解决方案3.1解决思路3.2解决过程3.2.1 封装播放组件3.2.2 隐形的视频div3.2.3 截取封面图 3.3 结束 1.背景 有这样一个需求: 给你一个监控列表,每页展示多个监控(至少12个,m3u8格式)&…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...

wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...

【无标题】湖北理元理律师事务所:债务优化中的生活保障与法律平衡之道
文/法律实务观察组 在债务重组领域,专业机构的核心价值不仅在于减轻债务数字,更在于帮助债务人在履行义务的同时维持基本生活尊严。湖北理元理律师事务所的服务实践表明,合法债务优化需同步实现三重平衡: 法律刚性(债…...
Python 高级应用10:在python 大型项目中 FastAPI 和 Django 的相互配合
无论是python,或者java 的大型项目中,都会涉及到 自身平台微服务之间的相互调用,以及和第三发平台的 接口对接,那在python 中是怎么实现的呢? 在 Python Web 开发中,FastAPI 和 Django 是两个重要但定位不…...