当前位置: 首页 > news >正文

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、函数是什么 学习库函数网站&#xff1a; https://cplusplus.com/reference/http://en.cppreference.comhttp://zh.cppreference.com 我们参考文档&#xff0c;学习几个库函数 2、库函数 3、自定义函数 自定义函数和库函数一样&#xff0c;有函数名&#xff0c;返回值类…...

【JVM】什么是双亲委派机制?

一、为什么会有这种机制&#xff1f; 类加载器将.class类加载到内存中时&#xff0c;为了避免重复加载&#xff08;确保Class对象的唯一性&#xff09;以及JVM的安全性&#xff0c;需要使用某一种方式来实现只加载一次&#xff0c;加载过就不能被修改或再次加载。 二、什么是双…...

Vulkan Tutorial 7 纹理贴图

目录 23 图像 图片库 暂存缓冲区 纹理图像 布局转换 将缓冲区复制到图像上 准备纹理图像 传输屏障掩码 清除 24 图像视图和采样器 纹理图像视图 采样器 Anisotropy 设备特征 25 组合图像采样器 更新描述符 纹理坐标系 着色器 23 图像 添加纹理将涉及以下步骤&am…...

LinkedBlockingQueue阻塞队列

➢ LinkedBlockingQueue阻塞队列 LinkedBlockingQueue类图 LinkedBlockingQueue 中也有两个 Node 分别用来存放首尾节点&#xff0c;并且里面有个初始值为 0 的原子变量 count 用来记录队列元素个数&#xff0c;另外里面有两个ReentrantLock的独占锁&#xff0c;分别用来控制…...

面试-Redis 常见问题,后续面试遇到新的在补充

面试-Redis 1.谈谈Redis 缓存穿透&#xff0c;击穿&#xff0c;雪崩及如何避免 缓存穿透&#xff1a;是指大量访问请求在访问一个不存在的key&#xff0c;由于key 不存在&#xff0c;就会去查询数据库&#xff0c;数据库中也不存在该数据&#xff0c;无法将数据存储到redis 中…...

2023年上半年数据库系统工程师上午真题及答案解析

1.计算机中, 系统总线用于( )连接。 A.接口和外设 B.运算器、控制器和寄存器 C.主存及外设部件 D.DMA控制器和中断控制器 2.在由高速缓存、主存和硬盘构成的三级存储体系中&#xff0c;CPU执行指令时需要读取数据&#xff0c;那么DMA控制器和中断CPU发出的数据地…...

设计模式概念

设计模式是软件工程领域中常用的解决问题的经验总结和最佳实践。它们提供了一套被广泛接受的解决方案&#xff0c;用于处理常见的设计问题&#xff0c;并促进可重用、可扩展和易于维护的代码。 设计模式的主要目标是提高软件的可重用性、可扩展性和灵活性&#xff0c;同时降低…...

arcpy批量对EXCE经纬度L进行投点,设置为wgs84坐标系,并利用该点计算每个区域内的核密度

以下是在 ArcPy 中批量对 Excel 经纬度 L 进行投点&#xff0c;设置为 WGS84 坐标系&#xff0c;并利用该点计算每个区域内的核密度的详细步骤&#xff1a; 1. 准备数据: 准备包含经纬度信息的 Excel 数据表格&#xff0c;我们假设文件路径为 "C:/Data/locations.xlsx&qu…...

Yolov5训练自己的数据集

先看下模型pt说明 YOLOv5s&#xff1a;这是 YOLOv5 系列中最小的模型。“s” 代表 “small”&#xff08;小&#xff09;。该模型在计算资源有限的设备上表现最佳&#xff0c;如移动设备或边缘设备。YOLOv5s 的检测速度最快&#xff0c;但准确度相对较低。 YOLOv5m&#xff1…...

Bert+FGSM中文文本分类

我上一篇博客已经分别用BertFGSM和BertPGD实现了中文文本分类&#xff0c;这篇文章与我上一篇文章BertFGSM/PGD实现中文文本分类&#xff08;Loss0.5L10.5L2)_Dr.sky_的博客-CSDN博客的不同之处在于主要在对抗训练函数和embedding添加扰动部分、模型定义部分、Loss函数传到部分…...

爬楼梯问题-从暴力递归到动态规划(java)

爬楼梯&#xff0c;每次只能爬一阶或者两阶&#xff0c;计算有多少种爬楼的情况 爬楼梯--题目描述暴力递归递归缓存动态规划暴力递归到动态规划专题 爬楼梯–题目描述 一个总共N 阶的楼梯&#xff08;N > 0&#xff09; 每次只能上一阶或者两阶。问总共有多少种爬楼方式。 示…...

浏览器如何验证SSL证书?

浏览器如何验证SSL证书&#xff1f;当前SSL证书应用越来越广泛&#xff0c;我们看见的HTTPS网站也越来越多。点击HTTPS链接签名的绿色小锁&#xff0c;我们可以看见SSL证书的详细信息。那么浏览器是如何验证SSL证书的呢? 浏览器如何验证SSL证书&#xff1f; 在浏览器的菜单中…...

Linux :: 【基础指令篇 :: 文件及目录操作:(10)】:: ll 指令 :: 查看指定目录下的文件详细信息

前言&#xff1a;本篇是 Linux 基本操作篇章的内容&#xff01; 笔者使用的环境是基于腾讯云服务器&#xff1a;CentOS 7.6 64bit。 学习集&#xff1a; C 入门到入土&#xff01;&#xff01;&#xff01;学习合集Linux 从命令到网络再到内核&#xff01;学习合集 目录索引&am…...

Java字符集/编码集

1 字符集/编码集 基础知识 计算机中储存的信息都是用二进制数表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果 按照某种规则, 将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码。这里强调一下: 按照…...

Apache配置与应用

目录 虚拟web主机httpd服务支持的虚拟主机类型基于域名配置方法基于IP配置方法基于端口配置方法 apache连接保持构建Web虚拟目录与用户授权限制Apache日志分割 虚拟web主机 虚拟Web主机指的是在同一台服务器中运行多个Web站点&#xff0c;其中每一个站点实际上并不独立占用整个…...

API自动化测试【postman生成报告】

PostMan生成测试报告有两种&#xff1a; 1、控制台的模式 2、HTML的测试报告 使用到一个工具newman Node.js是前端的一个组件&#xff0c;主要可以使用它来开发异步的程序。 一、控制台的模式 1、安装node.js 双击node.js进行安装&#xff0c;安装成功后在控制台输入node …...

探索OpenAI插件:ChatWithGit,memecreator,boolio

引言 在当今的技术世界中&#xff0c;插件扮演着至关重要的角色&#xff0c;它们提供了一种简单有效的方式来扩展和增强现有的软件功能。在本文中&#xff0c;我们将探索三个OpenAI的插件&#xff1a;ChatWithGit&#xff0c;memecreator&#xff0c;和boolio&#xff0c;它们…...

linux irq

中断上下部 软中断、tasklet、工作对列 软中断优点&#xff1a;运行在软中断上下文&#xff0c;优先级比普通进程高&#xff0c;调度速度快。 缺点&#xff1a;由于处于中断上下文&#xff0c;所以不能睡眠。 相对于软中断/tasklet&#xff0c;工作对列运行在进程上下文 h…...

串口流控(CTS/RTS)使用详解

1.流控概念 在两个设备正常通信时&#xff0c;由于处理速度不同&#xff0c;就存在这样一个问题&#xff0c;有的快&#xff0c;有的慢&#xff0c;在某些情况下&#xff0c;就可能导致丢失数据的情况。 如台式机与单片机之间的通讯&#xff0c;接收端数据缓冲区已满&#xff0…...

kube-proxy模式详解

1 kube-proxy概述 kubernetes里kube-proxy支持三种模式&#xff0c;在v1.8之前我们使用的是iptables 以及 userspace两种模式&#xff0c;在kubernetes 1.8之后引入了ipvs模式&#xff0c;并且在v1.11中正式使用&#xff0c;其中iptables和ipvs都是内核态也就是基于netfilter&…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

MySQL JOIN 表过多的优化思路

当 MySQL 查询涉及大量表 JOIN 时&#xff0c;性能会显著下降。以下是优化思路和简易实现方法&#xff1a; 一、核心优化思路 减少 JOIN 数量 数据冗余&#xff1a;添加必要的冗余字段&#xff08;如订单表直接存储用户名&#xff09;合并表&#xff1a;将频繁关联的小表合并成…...

LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)

在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...

Sklearn 机器学习 缺失值处理 获取填充失值的统计值

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...

02.运算符

目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&&#xff1a;逻辑与 ||&#xff1a;逻辑或 &#xff01;&#xff1a;逻辑非 短路求值 位运算符 按位与&&#xff1a; 按位或 | 按位取反~ …...