C语言---函数

news2024/11/24 16:55:24

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、递归的几个经典问题

  • 汉诺塔问题
  • 青蛙跳台阶问题

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

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

相关文章

TLS协议在ISO13400-2-2019文档中的内容解析

我很久之前写过解读ISO13400文档的系列文章:《详解ISO 13400文档(DoIP协议)》。当时没有说明解读的是哪一版13400,现在回过头看,应该是ISO13400-2-2012版本。那一版里没有TLS协议的相关内容,所以写的文章中也没有体现。 而2019版相比2012版,有两个方面的变化: 增加TLS…

1.AI绘画简介

1.1简介 ​ AI绘画即指人工智能绘画&#xff0c;是一种计算机生成绘画的方式。 ​ AI绘画主要包含两个部分&#xff0c;一个是对图像的分析与判断&#xff0c;即“学习”&#xff0c;一个是对图像的处理和还原&#xff0c;即“输出”。人工智能通过对数以万计的图像及绘画作品…

汽车充电桩主板的构成及7kw交流桩的优势阐述

汽车充电桩主板的构成通常包括&#xff1a;中央处理器、存储器、协议转换芯片、电源管理系统、信号输入输出、充电状态指示灯、电容、电感、电阻等元件。 其中&#xff0c;中央处理器是充电桩的核心&#xff0c;可以对电动汽车进行识别和匹配&#xff0c;根据电动车需求充电&am…

SpringMVC01:SpringMVC的分析和创建

目录 一、Spring MVC:回顾MVC 1、什么是MVC&#xff1f; 2、Model1时代 3、Model2时代 4、回顾Servlet 1.新建一个Maven工程当作父工程&#xff01;pom依赖 2.建立一个Moudle&#xff1a;springmvc-01-servlet&#xff0c;添加Web app的支持 3.导入servlet和jsp的jar依…

如何使用 Python 进行机器学习?

全套学习路线图、课程&#xff0c;机器学习工作流程如下。 Python人工智能 入门&#xff1a; Python基础→Python数据挖掘中级&#xff1a; 机器学习进阶&#xff1a; NLP自然语言高级&#xff1a; OpenCV基础→深度学习 人工智能学习路线图2023版-黑马程序员人工智能技术路…

Facebook广告投放怎么使用?Facebook广告投放的教程

做跨境电商的&#xff0c;多多少少都离不开广告投放&#xff0c;Facebook广告投放更是很多人的首选&#xff0c;所以东哥今天就来分享一个Facebook广告投放的教程&#xff0c;看完不信你还不会Facebook广告投放&#xff01; Facebook广告投放的教程 1、用科学上网的方式注册一个…

2023 华为 Datacom-HCIE 真题题库 10--含解析

单项选择题 1.[试题编号&#xff1a;190585] &#xff08;单选题&#xff09;华为SD-WAN解决方案中&#xff0c;当CPE位 于NAT设备后的私网时&#xff0c;特别是两个站点的CPE同时位于NAT设备后的私网时&#xff0c;CPE之 间需要使用NAT穿越技术。华为SD-WAN解决方案中使用以下…

vulnhub dc-8

1.信息搜集 端口 22,80,31337 存活ip 192.168.85.136 2.访问网站&#xff0c;进行信息搜集 在欢迎页面发现sql注入 sqlmap进行跑数据 python sqlmap.py -u "http://192.168.85.136/?nid1" --batch -D d7db -T users -C name,pass --dump尝试robots.txt,发现后他登…

保姆级讲解,让ChatGPT成为机器人的智慧大脑

文 / 高扬&#xff08;微信公众号&#xff1a;量子论&#xff09; ChatGPT是生成式人工智能&#xff0c;如果能接入机器人&#xff0c;可以让机器人更加智能。 我手上没有硬件&#xff0c;但我们可以模拟尝试机器人的制作逻辑&#xff0c;这个设计分成两部分&#xff1a;硬件、…

大数据分析平台:即需即用,告别“等一下”

数据分散在不同的业务系统中&#xff0c;整合清洗后才能用于数据分析&#xff0c;且由于IT不如业务那般清楚分析逻辑、需求等&#xff0c;很难及时响应新的分析需求&#xff0c;导致每次要数据、要报表时得到的回答都是“等一下”。那怎么办&#xff1f;那就用大数据分析平台&a…

【Linux】Firewalld防火墙

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、Firewalld概述二、Firewalld和iptables的关系三、Firewalld网络区域1.firewalld检查数据包的源地址的规则2.Firewalld防火墙预定义了9个区城 四、firewalld防火…

jvm之分析调优

写在前面 jvm调优不管是工作中还是面试中都异常重要&#xff0c;是衡量一个开发人员技术水平的重要指标&#xff0c;这也是个人的一个弱项&#xff0c;希望通过本文能够提高自我&#xff0c;也更能帮助到正在阅读文章的你&#xff0c;我们就开始吧&#xff01; 1&#xff1a;…

Nginx 安装及部署项目

1. 下载nginx安装包 首先从官网下载 nginx,大家可以自己在百度搜索 nginx&#xff0c;进入官 网&#xff0c;或者在浏览中直接输入 nginx 的官方网址: http://nginx.org/ &#xff0c;在此我直接提供 nginx 的下载链接&#xff0c;大家点进去之后可以按照 自己的需求下载自己所…

Flutter 笔记 | Flutter 核心原理(三)布局(Layout )过程

布局过程 Layout&#xff08;布局&#xff09;过程主要是确定每一个组件的布局信息&#xff08;大小和位置&#xff09;&#xff0c;Flutter 的布局过程如下&#xff1a; 父节点向子节点传递约束&#xff08;constraints&#xff09;信息&#xff0c;限制子节点的最大和最小宽…

Vue项目iconfont新增svg图标

最近接手一个开发一半的Vue3的后台管理项目&#xff0c;由于样式需要&#xff0c;需要新增一些svg图标&#xff0c;项目使用的是iconfont接下来我会通过几个步骤在原来iconfont基础上去新增一些自己的图标&#xff1b; 一、iconfont文件转换为svg源文件&#xff1b; 通过访问ic…

电子商务客户消费购物预测模型-基于数千万真实在线零售数据__企业调研_论文科研_毕业设计(智慧营销_精准营销_机器学习_人工智能)

之前发过 《谁主沉浮&#xff1f;银行&#xff0c;消金&#xff0c;互联网公司的精准营销_智慧营销完全解读》介绍了智慧营销/精准营销目的是降低运营成本。但精准营销可以带来很多额外收益&#xff0c;例如提高销售利润&#xff0c;提高客户忠诚度&#xff0c;降低客户流失率&…

Ribbon入门使用 RestTemplate loadbance 负载均衡

一、概念 1. Ribbon Ribbon是实现一套客户端&#xff0c;负载均衡的工具&#xff0c;简单的说&#xff0c;ribbon是一个开源项目&#xff0c;主要提供给客户端软件负载均衡算法和服务调用。 负载均衡和服务调用的提供者 主要用于: 负载均衡 将用户的请求平均到分配多个微服务…

华为OD机试之二元组个数(Java源码)

二元组个数 题目描述 给定两个数组a&#xff0c;b&#xff0c;若a[i] b[j] 则称 [i, j] 为一个**二元组**&#xff0c;求在给定的两个数组中&#xff0c;二元组的个数。 输入描述 第一行输入 m 第二行输入m个数&#xff0c;表示第一个数组 第三行输入 n 第四行输入n个数&…

linux 安装、卸载docker(一)

安装流程 # 1.设置镜像仓库 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo# 2.安装、更新yum软件包索引 // 安装yum&#xff0c;docker需要的安装包 yum install -y yum-utils // 更新yum软件包索引 yum makecache fast# …

pmp新手报名需要的步骤

球很多大中型企业&#xff0c;如华为、腾讯、字节、联想等&#xff0c;招聘项目管理相关人员时&#xff0c;都会把持有PMP证书当作必选或优选的招聘条件。 那么PMP的报考条件是什么呢&#xff1f;以下是PMI官网发布的PMP考试报考条件&#xff1a; 一、报名考生必须具备35小时…