C语言---函数
1、函数是什么
学习库函数网站:
- https://cplusplus.com/reference/
- http://en.cppreference.com
- http://zh.cppreference.com
我们参考文档,学习几个库函数
2、库函数
3、自定义函数
自定义函数和库函数一样,有函数名,返回值类型和函数参数
函数的组成:
ret_type fun_name(para1,*)
{statement; //语句项 //函数体
}ret_type //返回值类型
fun_name //函数名
para1 //函数参数
4、函数参数
4.1、创建函数实现两个整形变量交换值
通过下面有问题的代码,讲解实参和形参:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>void Swap(int x,int y)
{int z = 0;z = x;x = y;y = z;
}int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);printf("原数字:a=%d,b=%d\n", a, b);Swap(a,b);printf("原数字:a=%d,b=%d", a, b);return 0;
}
输出:

我们会发现,变量a、b的值并没有交换!!!
那问题出现在哪里呢?
经过调试后发现Swap()函数里面的x,y值没有转给a,b,所以a,b一直都是10,20。
原因总结:变量a,b叫做实参。变量x,y叫做形参。当实参传递给形参的时候,形参是实参的临时拷贝。对形参的修改不会影响到实参。
那知道了问题后,怎么解决呢?
利用我们前面学到的指针变量的方法:
int main()
{int a = 0;int* p = &a;a = 20; //直接修改a的值*p = 30; //间接修改a的值return 0;
}
也就是说我们可以利用a,b的地址,然后赋值进行修改,从而达到交换值得目的
代码实现:(好好体会)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>void Swap(int* px,int* py)
{int z = *px; //z = a*px = *py; //a = b*py = z; //b = a
}int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);printf("原数字:a=%d,b=%d\n", a, b);Swap(&a,&b);printf("原数字:a=%d,b=%d", a, b);return 0;
}
那下面我们再来实现一下两个函数的加法:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>Add(int x,int y)
{int z = 0;z = x + y;return z;
}int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);int c = Add(a, b);printf("%d\n", c);return 0;
}
提问:为什么实现两个变量值的交换需要传指针参数,而实现两个值相加不需要传指针参数呢?
那我们什么时候需要传指针参数,什么时候不需要呢?
- 当我们需要改变变量a,b的值时就需要使用指针变量
- 否则不需要
4.2、实际参数(实参)
真是传给函数的参数,叫实参。
实参可以是:常量、变量、表达式、函数等。
无论实参是何种类型的的量,在进行函数调用时,它们都必须有确定的值,一边把这些值传给形参。
4.3、形式参数(形参)
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成后就自动销毁了,因此形式参数只在函数中有效。
并且形参实例化后相当于实参的临时拷贝。
实参和形参的参数名可以相同也可以不相同。
5、函数调用
5.1、传值调用
函数的形参和实参分别占有不同的内存块,对形参的修改不会影响实参。
5.2、传址调用
- 传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
- 这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
Swap1(a,b) //传值调用Swap(&a,&b) //传址调用
6、函数的嵌套调用和链式访问
函数和函数之间可以根据实际的需求进行组合,也就是互相调用。
6.1、链式访问
把一个函数的返回值作为另一个函数的参数。特别注意:是一个函数的返回值作为另一个函数的参数
所以链式访问的前提是函数必须要有返回值。
那我们来看下面的代码:
#include <stdio.h>
int main()
{printf("%d", printf("%d", printf("%d", 43)));return 0;
}
输出:

分析:首先我们要知道printf()函数的打印和返回值不是一回事,printf()函数的返回值是返回数据的个数,比如这里:打印的是43,43是两个数字,所以printf()函数的返回值是2。那好,我们从左向右依次分析,首先最里面的printf()函数打印出43,并且返回2传给中间的那个printf()函数,那后中间的那个函数打印2,因为2是一个数字,所以中间的printf()返回值1并传给最左侧的printf()函数,然后左侧的printf()函数打印1。因此输出结果为4321。
7、函数的声明和定义
7.1、函数的声明
1、告诉编译器有一个函数叫什么,参数是什么,返回类型是什么,但是具体是不是不存在,函数声明决定不了。
2、函数的声明一般出现在函数的使用之前,要满足先声明后使用。
3、函数的声明一般要放在头文件中的。
我们在写两个数相加时,Add()函数就写在main()函数上面,但是如果我们将Add()函数写在main()函数下面,程序也可以运行,就是会警告,如果想取消警告我们就需要声明:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//函数声明
int Add(int, int); //两种写法都可以
int Add(int x, int y);int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);int ret = Add(a, b);printf("%d", ret);return 0;
}//函数的定义
int Add(int x,int y)
{return x + y;
}
但是在实际业务中,我们不会这样写,我们会怎么办呢?我们可以创建个add.c源文件和add.h头文件。然后把Add()函数部分放进add.c源文件中,把函数声明—>int Add(int, int); 放进add.h头文件中,最后在main函数所在的.c源文件中,引入add.h头文件即可。(这里add.c和add.h文件不在演示创建,只演示引入头文件):
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include "add.h"; //引入add.h头文件int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);int ret = Add(a, b);printf("%d", ret);return 0;
}
7.2、函数的定义
函数的定义是指函数的具体实现,交代函数的功能实现。
8、函数递归和迭代
迭代就是非递归
8.1、什么是递归?
程序调用自身的编程技巧称为递归(recursion)。
递归作为一种算法在程序设计语言中广泛应用。一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。
递归策略只需要少量的程序就可以描述出问题过程所需要的多次重复计算,大大的减少了程序的代码量。
递归的主要思考方式在于:把大事化小。
8.2、递归的两个必要条件
- 存在限制条件,当满足这个限制条件的时候,递归便不在继续。
- 每次递归调用之后越来越接近这个限制条件。
8.3、做几个例题
8.3.1、接收一个整型值(无符号),按照顺序打印它的每一位。
例如:
输入:1234,输出:1 2 3 4。
分析:要想分别拿到1 2 3 4我们可以这样做:将1234 % 10 ,会得到4,然后将1234 / 10会得到123,然后将123 % 10会得到3,然后将123 / 10会得到12,将12 % 10会得到2,将1 % 10会得到1,然后1 / 10会得到0。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>void print(unsigned int n)
{if (n > 9){print(n / 10);}printf("%d ", n % 10);
}int main()
{unsigned int num = 0;scanf("%d", &num);print(num);return 0;
}
我们想一下如果不加if语句,那就会死递归的调用print()函数,最终会导致栈溢出(stack overflow)。
因为每一次函数的调用都会在栈区申请空间,反复调用就会一直申请空间,最终导致栈溢出。

8.3.2、编写函数不允许创建临时变量,求字符串的长度
我们先来写个允许创建临时变量的代码:
#include <stdio.h>my_strlen(char* str)
{ int count = 0; //这个就是创建的临时变量while (*str != '\0'){count++;str++;}return count;
}int main()
{char arr[] = "1234";int len = my_strlen(arr);printf("%d\n", len);return 0;
}
但是现在要求不能创建临时变量,所以需要使用递归的方法来实现。
#include <stdio.h>my_strlen(char* str)
{if (*str != '\0'){return 1 + my_strlen(str + 1);}else{return 0;}
}int main()
{char arr[] = "1234";int len = my_strlen(arr);printf("%d\n", len);return 0;
}
8.3.3、使用递归实现n!
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int fac(int x)
{if (x <= 1){return 1;}else{return x * fac(x - 1);}}int main()
{int n = 0;scanf("%d", &n);int ret = fac(n);printf("%d\n", ret);return 0;
}
8.3.4、求第n个斐波那契数
斐波那契数:1 1 2 3 5 8 13 21 34 55…
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int Fib(int n)
{if (n <= 2){return 1;}else{return Fib(n - 1) + Fib(n - 2);}
}int main()
{int n = 0;scanf("%d", &n);int ret = Fib(n);printf("%d", ret);return 0;
}
但是这个用递归就不太适合,当n=50时,会计算很长时间。那就用迭代的方法:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int Fib(int n)
{int a = 1;int b = 1;int c = 0;int i = 0;if (n <= 2){return 1;}else{for (i=1; i <= n - 2;i++){c = a + b;a = b;b = c;}return c;}}int main()
{int n = 0;scanf("%d", &n);int ret = Fib(n);printf("%d", ret);return 0;
}
8.4、递归的几个经典问题
- 汉诺塔问题
- 青蛙跳台阶问题
相关文章:
C语言---函数
1、函数是什么 学习库函数网站: https://cplusplus.com/reference/http://en.cppreference.comhttp://zh.cppreference.com 我们参考文档,学习几个库函数 2、库函数 3、自定义函数 自定义函数和库函数一样,有函数名,返回值类…...
【JVM】什么是双亲委派机制?
一、为什么会有这种机制? 类加载器将.class类加载到内存中时,为了避免重复加载(确保Class对象的唯一性)以及JVM的安全性,需要使用某一种方式来实现只加载一次,加载过就不能被修改或再次加载。 二、什么是双…...
Vulkan Tutorial 7 纹理贴图
目录 23 图像 图片库 暂存缓冲区 纹理图像 布局转换 将缓冲区复制到图像上 准备纹理图像 传输屏障掩码 清除 24 图像视图和采样器 纹理图像视图 采样器 Anisotropy 设备特征 25 组合图像采样器 更新描述符 纹理坐标系 着色器 23 图像 添加纹理将涉及以下步骤&am…...
LinkedBlockingQueue阻塞队列
➢ LinkedBlockingQueue阻塞队列 LinkedBlockingQueue类图 LinkedBlockingQueue 中也有两个 Node 分别用来存放首尾节点,并且里面有个初始值为 0 的原子变量 count 用来记录队列元素个数,另外里面有两个ReentrantLock的独占锁,分别用来控制…...
面试-Redis 常见问题,后续面试遇到新的在补充
面试-Redis 1.谈谈Redis 缓存穿透,击穿,雪崩及如何避免 缓存穿透:是指大量访问请求在访问一个不存在的key,由于key 不存在,就会去查询数据库,数据库中也不存在该数据,无法将数据存储到redis 中…...
2023年上半年数据库系统工程师上午真题及答案解析
1.计算机中, 系统总线用于( )连接。 A.接口和外设 B.运算器、控制器和寄存器 C.主存及外设部件 D.DMA控制器和中断控制器 2.在由高速缓存、主存和硬盘构成的三级存储体系中,CPU执行指令时需要读取数据,那么DMA控制器和中断CPU发出的数据地…...
设计模式概念
设计模式是软件工程领域中常用的解决问题的经验总结和最佳实践。它们提供了一套被广泛接受的解决方案,用于处理常见的设计问题,并促进可重用、可扩展和易于维护的代码。 设计模式的主要目标是提高软件的可重用性、可扩展性和灵活性,同时降低…...
arcpy批量对EXCE经纬度L进行投点,设置为wgs84坐标系,并利用该点计算每个区域内的核密度
以下是在 ArcPy 中批量对 Excel 经纬度 L 进行投点,设置为 WGS84 坐标系,并利用该点计算每个区域内的核密度的详细步骤: 1. 准备数据: 准备包含经纬度信息的 Excel 数据表格,我们假设文件路径为 "C:/Data/locations.xlsx&qu…...
Yolov5训练自己的数据集
先看下模型pt说明 YOLOv5s:这是 YOLOv5 系列中最小的模型。“s” 代表 “small”(小)。该模型在计算资源有限的设备上表现最佳,如移动设备或边缘设备。YOLOv5s 的检测速度最快,但准确度相对较低。 YOLOv5m࿱…...
Bert+FGSM中文文本分类
我上一篇博客已经分别用BertFGSM和BertPGD实现了中文文本分类,这篇文章与我上一篇文章BertFGSM/PGD实现中文文本分类(Loss0.5L10.5L2)_Dr.sky_的博客-CSDN博客的不同之处在于主要在对抗训练函数和embedding添加扰动部分、模型定义部分、Loss函数传到部分…...
爬楼梯问题-从暴力递归到动态规划(java)
爬楼梯,每次只能爬一阶或者两阶,计算有多少种爬楼的情况 爬楼梯--题目描述暴力递归递归缓存动态规划暴力递归到动态规划专题 爬楼梯–题目描述 一个总共N 阶的楼梯(N > 0) 每次只能上一阶或者两阶。问总共有多少种爬楼方式。 示…...
浏览器如何验证SSL证书?
浏览器如何验证SSL证书?当前SSL证书应用越来越广泛,我们看见的HTTPS网站也越来越多。点击HTTPS链接签名的绿色小锁,我们可以看见SSL证书的详细信息。那么浏览器是如何验证SSL证书的呢? 浏览器如何验证SSL证书? 在浏览器的菜单中…...
Linux :: 【基础指令篇 :: 文件及目录操作:(10)】:: ll 指令 :: 查看指定目录下的文件详细信息
前言:本篇是 Linux 基本操作篇章的内容! 笔者使用的环境是基于腾讯云服务器:CentOS 7.6 64bit。 学习集: C 入门到入土!!!学习合集Linux 从命令到网络再到内核!学习合集 目录索引&am…...
Java字符集/编码集
1 字符集/编码集 基础知识 计算机中储存的信息都是用二进制数表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果 按照某种规则, 将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码。这里强调一下: 按照…...
Apache配置与应用
目录 虚拟web主机httpd服务支持的虚拟主机类型基于域名配置方法基于IP配置方法基于端口配置方法 apache连接保持构建Web虚拟目录与用户授权限制Apache日志分割 虚拟web主机 虚拟Web主机指的是在同一台服务器中运行多个Web站点,其中每一个站点实际上并不独立占用整个…...
API自动化测试【postman生成报告】
PostMan生成测试报告有两种: 1、控制台的模式 2、HTML的测试报告 使用到一个工具newman Node.js是前端的一个组件,主要可以使用它来开发异步的程序。 一、控制台的模式 1、安装node.js 双击node.js进行安装,安装成功后在控制台输入node …...
探索OpenAI插件:ChatWithGit,memecreator,boolio
引言 在当今的技术世界中,插件扮演着至关重要的角色,它们提供了一种简单有效的方式来扩展和增强现有的软件功能。在本文中,我们将探索三个OpenAI的插件:ChatWithGit,memecreator,和boolio,它们…...
linux irq
中断上下部 软中断、tasklet、工作对列 软中断优点:运行在软中断上下文,优先级比普通进程高,调度速度快。 缺点:由于处于中断上下文,所以不能睡眠。 相对于软中断/tasklet,工作对列运行在进程上下文 h…...
串口流控(CTS/RTS)使用详解
1.流控概念 在两个设备正常通信时,由于处理速度不同,就存在这样一个问题,有的快,有的慢,在某些情况下,就可能导致丢失数据的情况。 如台式机与单片机之间的通讯,接收端数据缓冲区已满࿰…...
kube-proxy模式详解
1 kube-proxy概述 kubernetes里kube-proxy支持三种模式,在v1.8之前我们使用的是iptables 以及 userspace两种模式,在kubernetes 1.8之后引入了ipvs模式,并且在v1.11中正式使用,其中iptables和ipvs都是内核态也就是基于netfilter&…...
IDEA插件实战:CodeGeeX4不只是补全代码,这5个隐藏用法让效率翻倍
IDEA插件实战:CodeGeeX4不只是补全代码,这5个隐藏用法让效率翻倍 在JetBrains生态中,AI编程助手早已不是新鲜事物,但大多数开发者对CodeGeeX4的认知仍停留在"智能补全"层面。当我在团队内部做技术分享时,发现…...
原创:黄大年茶思屋难题揭榜第11期|5道核心题精简公开·被退稿求技术指正
黄大年茶思屋难题揭榜第11期|5道核心题精简公开被退稿求技术指正 作者:华夏之光永存 摘要 这五道题我们已完整解题并提交黄大年茶思屋难题揭榜,最终被直接退稿,但平台未给出任何具体技术驳回理由、未指明缺陷、未提供修改方向。我…...
ASP.NET Core 认证鉴权实战:JWT、Policy 与权限边界怎么落地
实现场:一个后台退款接口原本只允许财务角色调用,但线上排查发现,普通运营账号只要拿到有效 token,也能调用成功。根因并不复杂:接口加了 [Authorize]系统只校验“是否登录”没有继续校验角色、权限和资源归属结果就是…...
Atmosphere-stable开源项目实战指南:从基础到进阶的完整路径
Atmosphere-stable开源项目实战指南:从基础到进阶的完整路径 【免费下载链接】Atmosphere-stable 大气层整合包系统稳定版 项目地址: https://gitcode.com/gh_mirrors/at/Atmosphere-stable 一、认知基础:如何理解Atmosphere自定义固件࿱…...
服装设计降本增效:Nano-Banana软萌拆拆屋缩短打样周期实证
服装设计降本增效:Nano-Banana软萌拆拆屋缩短打样周期实证 在服装设计行业,从创意草图到实物样衣,打样环节往往是成本最高、耗时最长的“拦路虎”。设计师需要反复与版师、样衣工沟通,绘制复杂的工艺图,一个款式来回修…...
Codex CLI 多环境配置秘籍:如何用 profiles 一键切换 OpenAI/Mistral/Ollama
Codex CLI 多环境配置秘籍:如何用 profiles 一键切换 OpenAI/Mistral/Ollama 当你的开发工作流需要同时对接多个AI模型提供商时——比如公司项目使用OpenAI的GPT-4,个人实验采用本地Ollama托管的Mistral,而临时调试又需要连接Azure的API端点—…...
毕业设计实战:基于SSM的学生宿舍设备报修管理系统设计与实现全攻略
毕业设计实战:基于SSM的学生宿舍设备报修管理系统设计与实现全攻略 在开发“学生宿舍设备报修管理系统”这套毕设时,我曾因“故障上报与维修派单流程脱节”踩过一个关键坑。初期设计时,我将“学生报修”和“维修人员接单”视为两个独立的模块…...
如何用免费AI助手提升3倍编码效率?DeepSeek-Coder-V2全解析
如何用免费AI助手提升3倍编码效率?DeepSeek-Coder-V2全解析 【免费下载链接】DeepSeek-Coder-V2 项目地址: https://gitcode.com/GitHub_Trending/de/DeepSeek-Coder-V2 在AI编程工具层出不穷的今天,开发者面临着一个关键选择:是为商…...
QAnything混合检索实战:ElasticSearch与向量搜索的协同优化
QAnything混合检索实战:ElasticSearch与向量搜索的协同优化 1. 为什么电商搜索总在“猜”用户心思? 你有没有遇到过这样的情况:在电商平台搜索“轻便透气运动鞋”,结果首页全是厚重的登山靴?或者搜“适合夏天穿的连衣…...
WDMHDA:Windows 旧系统高清音频驱动的突破与挑战
【导语:WDMHDA 是一款适用于 Windows 98SE / ME 的高清音频驱动程序,为旧系统的音频功能带来新可能。但目前处于 Alpha 阶段,存在诸多待解决问题,其发展对旧系统音频生态有重要影响。】WDMHDA:旧系统音频驱动新选择WDM…...
