【C语言】函数详解(嵌套调用和链式访问、声明及定义、递归)

news2025/1/16 7:19:34

简单不先于复杂,而是在复杂之后。

目录

1.函数的嵌套调用和链式访问 

1.1 嵌套调用 

1.2 链式访问

2. 函数的声明和定义 

2.1 函数声明 

2.2 函数定义

3. 函数递归

3.1 什么是递归? 

3.2 递归的两个必要条件 

3.2.1 练习1(需要画图)

3.2.2 练习2:(画图) 

3.3 递归与迭代

 3.3.1 求n的阶乘(不考虑溢出)

3.3.2 求第 n 个斐波那契数(不考虑溢出) 

 

 

 


1.函数的嵌套调用和链式访问 

函数和函数之间可以根据实际的需求进行组合的,也就是可以互相调用的。 

1.1 嵌套调用 

 

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>  
void new_line()
{
printf("hehe\n");
}
void three_line()
{
	int i = 0;  
	for (i = 0; i < 3; i++)
	{
		new_line();
	}
}
int main()
{
	three_line();  

	return 0;
}

函数可以嵌套调用,但是不能嵌套定义。

1.2 链式访问

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

int main()
{
	int len = strlen("abcdef");
	printf("%d\n", len);
	//链式访问
	printf("%d\n", strlen("abcdef"));

	return 0;
}

这是 printf 函数的返回值介绍,意思是这些函数的每一个都返回打印的字符数如果发生错误,则返回 -1. 

链式访问的前提是函数要有返回值。

函数不写返回值时默认返回类型是 int。

//明确说明main函数不需要参数
//本质上main函数是有参数的
int main(void)
{
    return 0;
}
//main函数有3个参数
int main(int argc, char* argv[], char* envp[])
{
    return 0;
}

2. 函数的声明和定义 

2.1 函数声明 

1. 告诉编译器函数叫什么,参数类型是什么,返回类型是什么。但具体是不是存在,函数声明决定不了。

2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。

3. 函数的声明一般要放在头文件中的

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//函数的声明和定义

int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	//加法
	int sum = Add(a, b);
	printf("%d\n", sum);

	return 0;
}

//函数的定义
Add(int x, int y)
{
	return x + y;
}

2.2 函数定义

 函数的定义是指函数的具体实现,交代函数的功能实现。

 

当我们正在写一个大型程序时,往往会需要写大量的函数来实现很多功能,如果声明和定义全部放到一个源程序文件中不利于我们调试和维护,分成三个文件分别为函数的声明、函数的定义、包含主函数的整体函数框架。这样可以使程序清晰,可读性大大增强。

3. 函数递归

3.1 什么是递归? 

程序调用自身的编程技巧称为递归(recursion)。

递归作为一种算法在程序设计语言中广泛应用。

一个过程或函数在其定义或说明中有间接或直接调用自身的一种方法,它通常把一个大型复杂的问题转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程中所需要的多次重复计算,大大减少了程序的代码量。 

递归的主要思考方式在于:把大事化小。

3.2 递归的两个必要条件 

  •  存在限制条件,当满足这个限制条件的时候,递归便不再继续。
  • 每次递归调用之后越来越接近这个限制条件。

3.2.1 练习1(需要画图)

接收一个整型值(无符号),按照顺序打印它的每一位。

例如:

输入1234, 输出1 2 3 4  

首先先写一个不用递归的版本。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
//%d是打印有符号的整数(会有正负数)
//%u是打印无符号的整数
int main()
{
	unsigned int num = 0;
	scanf("%u", &num);//1234

	while (num != 0)
	{
		printf("%d\n", num % 10);
		num /= 10;
	}

	return 0;
}

如果用递归实现,我们的思路是大事化小。

//print(123)4
//print(12) 3 4
//print(1) 2 3 4
//1 2 3 4

就像剥洋葱一样层层打印。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

void print(unsigned int n)
{
	if (n > 9)
	{
		print(n / 10);
	}
	printf("%d\n", n % 10);
}

int main()
{
	unsigned int num = 0;
	scanf("%u", &num);//1234
	print(num);//接收一个无符号整型值,打印它的每一位

	return 0;
}

 

3.2.2 练习2:(画图) 

编写函数不允许创建临时变量,求字符串的长度。 

我们先写一个需要创建临时变量的版本:

也就是模拟实现 strlen

#define _CRT_SECURE_NO_WARNINGS 1、
#include<stdio.h>
//int my_strlen(char* str[])  参数写成数组形式
int my_strlen(char* str)//参数写成指针形式
{
	int count = 0;
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}

int main()
{
	char arr[] = "abc";
	int len = my_strlen(arr);
	printf("%d", len);

	return;
}

接下来我们用递归方式求解:

//my_strlen("abc")
//1+my_strlen("bc")
//1+1+my_strlen("c")
//1+1+1+my_strlen("")
//1+1+1+0

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

int my_strlen(char* str)
{
	if (*str != '\0')
	{
		return 1 + my_strlen(str + 1);
	}
	else
		return 0;
}

int main()
{
	char arr[] = "abc";
	int len = my_strlen(arr);
	printf("%d", len);

	return 0;
}

3.3 递归与迭代

 3.3.1 求n的阶乘(不考虑溢出)

递归的方式: 

 

 

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int fac(int n)
{
	if (n <= 1)
		return 1;
	else
		return n * fac(n - 1);
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = fac(n);
	printf("%d\n", ret);

	return 0;
}

迭代的方式: 

 

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int fac(int n)
{
    int i = 0;
    int ret = 1;
    for (i = 1; i <= n; i++)
    {
        ret *= i;
    }
    return ret;
}

int main()
{
    int n = 0;
    scanf("%d", &n);
    int ret = fac(n);
    printf("%d\n", ret);

    return 0;
}

3.3.2 求第 n 个斐波那契数(不考虑溢出) 

递归实现: 

 

 

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

//求第n个斐波那契数
//斐波那契数列
//1 2 3 5 8 13 21 34 55······
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;
}

迭代实现: 

  

 在调试 factorial 函数的时候,如果你的参数比较大,那就会报错:

stack overflow(栈溢出)这样的信息。

系统分配给程序的栈空间是有限的,但是如果出现了死循环,或者(死递归),这样有可能导致一直开辟栈空间,最终产生栈空间耗尽的情况,这样的现象我们称之为栈溢出。

那如何解决上述的问题:

将递归写成非递归。

使用 stack 对象替代 nonstatic 局部对象。在递归函数设计中,可以使用 static 对象替代

nonstatic 局部对象(即栈对象),这不仅可以减少每次递归调用和返回时产生和释放

nonstatic 对象的开销,而且 static 对象还可以保存递归调用的中间状态,并且可为各个调用

层所访问。

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

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

相关文章

Spring Security实战(三)—— 自动登录与注销登录

目录 一、实现自动登录 1. 散列加密方案 2. 持久化令牌方案 二、注销登录 一、实现自动登录 自动登录是将用户的登录信息保存在用户浏览器的cookie中&#xff0c;当用户下次访问时&#xff0c;自动实现校验并建立登录态的一种机制。 Spring Security 提供了两种非常好的令牌&a…

C ++ 基础入门。加强变量、指针、结构体理解

1、 const放外面&#xff0c;值不可以改。只读 同理于指针 看const右侧紧跟着的是指针还是常量, 是指针就是常量指针&#xff0c;是常量就是指针常量 const 放外面&#xff0c;值不可以改 2、 所有的指针类型&#xff0c;包括结构体指针 double * int *都是和操作系统位数…

补充——spark RDD序列化和持久化

目录 RDD序列化 闭包检查&#xff1a; 序列化方法和属性 Kryo序列化框架&#xff1a; RDD持久化&#xff08;RDD persistence&#xff09; RDDCache缓存 RDD persist缓存 什么时候使用persist()? RDD CheckPoint 检查点 缓存和检查点区别 RDD序列化 闭包检查&#x…

JavaScript 的基础函数有哪些?

1、在 JavaScript 中将数组本地转换为对象 JavaScript 有一个原生函数 Object.fromEntries&#xff0c;可用于将任何输入数组转换为对象。 1.const anArray [ 2. [firstname, Paul], 3. [surname, Knulst], 4. [address, worldwide], 5. [role, Senior Engineer], 6. […

Java中的异常Exception和捕获,自定义异常

文章目录1. 异常概述1.1 什么是程序的异常1.2 异常的抛出机制1.3 如何对待异常2. Java异常体系2.1 Throwable2.2 Error 和 Exception2.3 编译时异常和运行时异常3. 常见的错误和异常3.1 Error3.2 运行时异常3.3 编译时异常4. 异常的处理4.1 异常处理概述4.2 捕获异常&#xff0…

springboot整合websocket

1.创建springboot项目&#xff0c;引入spring-boot-starter-websocket依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>全部依赖如下&#xff1a; &l…

JDBC之DAO层封装思想超详解

Mysql版本&#xff1a;8.0.26 可视化客户端&#xff1a;sql yog 编译软件&#xff1a;IntelliJ IDEA 2019.2.4 x64 运行环境&#xff1a;win10 家庭中文版 jdk版本&#xff1a;1.8.0_361 目录一、DAO是什么&#xff1f;二、案例演示2.1 准备数据2.2 创建bean包2.3 建立DAO包2.2…

Houdini>RBD(搅拌大米效果)并导出FBX到unity

Houdini&#xff1e;RBD(搅拌大米效果) 效果展示&#xff1a; 动图录制软件&#xff1a;Cockos Incorporated | LICEcap 参考链接&#xff1a;导出除了ABC外&#xff0c;比较小的FBX文件用法 目录&#xff1a; 一、引用模型的处理&#xff1a; 1、大米 模型创建 多层复制 …

Mybatis(六)缓存

缓存是Mybatis中非常重要的特性&#xff0c;Mybatis的一级缓存基于SqlSession实现&#xff0c;二级缓存基于Mapper实现。 一、缓存的使用 一级缓存默认开启&#xff0c;Mybatis提供了一个配置参数localCacheScope来控制一级缓存的级别&#xff0c;该参数的取值可以是session、…

【机器学习】P10 从头到尾实现一个线性回归案例

这里写自定义目录标题&#xff08;1&#xff09;导入数据&#xff08;2&#xff09;画出城市人口与利润图&#xff08;3&#xff09;计算损失值&#xff08;4&#xff09;计算梯度下降&#xff08;5&#xff09;开始训练&#xff08;6&#xff09;画出训练好的模型&#xff08;…

参加Matlab与AI讲座:使用深度强化学习训练走路机器人观后感

时间&#xff1a;2023年4月12日&#xff0c;周三&#xff0c;天气晴 地址&#xff1a;大连理工大学研教楼303 前言&#xff1a;Matlab其实有很多功能&#xff0c;我们所用的只是最基础最简单的部分&#xff0c;例如矩阵计算&#xff0c;画图等等。 随着强化学习的发展&#xff…

一般形式的S曲线公式推导

文章目录一、背景二、目标三、计算3.1 S曲线基本形式3.2 S曲线变换3.3 参数计算3.4 S曲线中心对称条件四、总结五、附件一、背景 S曲线因具备良好可控的平滑性、单调性、连续可导性等优点&#xff0c;常作为各类电机升降速曲线。当前多数S曲线的介绍文章未给出推导过程&#x…

SpringCloud微服务技术栈.黑马跟学(五)

SpringCloud微服务技术栈.黑马跟学 五今日目标1.初识elasticsearch1.1.了解ES1.1.1.elasticsearch的作用1.1.2.ELK技术栈1.1.3.elasticsearch和lucene1.1.4.为什么不是其他搜索技术&#xff1f;1.1.5.总结1.2.倒排索引1.2.1.正向索引1.2.2.倒排索引1.2.3.正向和倒排1.3.es的一些…

SpringMVC基本注解的使用和理解

SpringMVC基本注解的使用和理解 RequestParam注解 使用在方法入参位置&#xff0c;用于指定请求参数名称&#xff0c;将该请求参数绑定到注解参数位置。 属性&#xff1a;name:指定要绑定的请求参数名称&#xff1b; name属性和value属性互为别名。 required 和&#xff1a;指…

Java并发编程(8) —— AQS抽象同步队列详解

上一篇&#xff1a;Java并发编程(7) —— 锁的分类概述 在上一篇中我们提到并发包中的ReentrantLock类是一种可重入独占锁&#xff0c;其锁机制是基于AQS实现的。实际上&#xff0c;并发包java.util.concurrent.locks中的锁都是基于AQS 实现的。 一、AQS是什么 AbstractQueued…

13. unity粒子特效--发射模块、各种发射器形状、粒子渐变(颜色/大小)

1. 发射模块&#xff08;Emission&#xff09; 匀速发射&#xff1a; Rate over Time&#xff1a;每秒钟发射的粒子数 Rate over Distance&#xff1a;每移动一米发射的粒子个数 两者可指定其一&#xff1a;若仅指定Rate over Time&#xff0c;则粒子根据时间的变化进行发射&a…

第三节、语言模型

目录 0、介绍 1、N-gram 模型介绍 2、困惑度 3、N-gram 模型的文本评估 4、N-gram 模型的平滑 5、基于 N-gram 模型的文本生成 6、基于统计的语言模型的缺陷 7、实验总结 0、介绍 首先&#xff0c;我们来思考这样一个问题&#xff1a;随便给你一句话&#xff0c;如何判…

MongoDB中的索引

一、说明 索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可能要花费几十秒甚至几分钟,这对网站的性能是非常致命的。索引是…

Javaweb小练习---在JSP中使用Javabean访问数据库完成用户信息的简单添加

Javaweb小练习---在JSP中使用Javabean访问数据库完成用户信息的简单添加 目录 Javaweb小练习---在JSP中使用Javabean访问数据库完成用户信息的简单添加 0.创建数据库 1. 在resources目录下创建db.properties文件 2. /** * 获取链接与释放资源的工具类--JdbcUtil类 */ 3…

UE-Ueransim-5GC全链路开发记录

目录 1. 系统配置 1.1 Ueransim配置 1.2 UE配置 2. 启动 3. 实际演示 附录 代理1&#xff1a;ueransim-5gc 代理2 ue-ueransim TCPclient TCPserver 1. 系统配置 1.1 Ueransim配置 ueransim的yaml文件如下 version: 3.8 services:ueransim2:container_name: uera…