【C函数】函数详解

news2024/11/25 23:45:26

函数

  • 前言
  • 一、函数是什么
  • 二、C语言中函数的分类
    • (一)库函数
      • 1.printf类
      • 2.strcpy类
      • 3.math类
      • 4.概念
      • 5.小知识
      • 6.总结
    • (二)自定义函数
      • 1.概念
      • 2.函数的组成
      • 3.例子1(求出两个数中的最大值)
      • 4.例子2(交换两个整数变量的内容)
      • 5.小知识
      • 6.总结
  • 三、函数的参数
    • (一)实际参数(实参)
    • (二)形式参数(形参)
  • 四、函数的调用
    • (一)传值调用
    • (二)传址调用
    • (三)练习
      • 1.写一个函数打印100到200之间的素数
      • 2.写一个函数输出1000年到2000年的闰年
      • 3.写一个函数,实现一个整形有序数组的二分查找
        • (1)详细解题思路:
        • (2)小知识:
        • (3)代码:
      • 4.写一个函数,每调用一次这个函数,就会将num的值增加
    • (四)函数调用实质
  • 五、函数的嵌套调用和链式访问
    • (一)嵌套调用
    • (二)链式访问
      • 1.概念
      • 2.常见例子:
  • 六、函数的声明和定义
    • (一)函数声明
      • 1.概念
      • 2.解析
    • (二)函数定义
    • (三)拓展(模块化开发)
  • 七、函数递归
    • (一)什么是递归
      • 1.概念
      • 2.简单代码
        • (1)史上最简单递归
        • (2)出现的问题
        • (3)推荐的网站
    • (二)递归的两个必要条件
      • 1.概念
      • 2.经典练习题1
        • (1)思路
        • (2)代码
        • (3)分析
      • 3.经典练习题2
        • (1)思路
        • (2)代码
        • (3)分析
    • (三)递归与迭代
      • 1.经典练习题3
        • (1)思路
        • (2)代码
        • (3)分析
      • 2.迭代概念
      • 3.经典练习题4
        • (1)思路
        • (2)代码
        • (3)分析
        • (4)问题
        • (5)迭代做法
      • 4.改进
      • 5.解决方法
      • 6.提示
    • (四)递归的经典题目
      • 1.汉诺塔问题
        • (1)题目描述和思路
        • (2)代码
      • 2.青蛙跳台阶问题
        • (1)题目描述和思路
        • (2)代码
  • 总结


前言

函数相信大家并不陌生,那函数具体是什么呢?函数很常见,就是大家平时用的函数,利用几个函数的分装过后,程序就会很清晰,很简单地运行,以下,听我细细讲解一下函数吧!


一、函数是什么

维基百科中对函数是这样定义的,它是一个子程序。

在计算机科学中,子程序,是一个大型程序中的某部分代码,由一个或多个语句块组成,它负责完成某项特定任务,而且相较于其他代码,具备独立性。
一般会有输入参数并有返回值,提供对过程的封装和对细节的隐藏,这些代码通常被称为软件库。

那我们利用 MSDN app看一看函数吧!
在这里插入图片描述


二、C语言中函数的分类

(一)库函数

1.printf类

当我们编译完程序后,我们迫不及待地想要知道结果,想把这个结果打印在屏幕上一看的时候,这个时候就非常需要一个函数将这个信息按照一定的格式打印在屏幕上,这就是printf,与之相关联的是scanf(输入)。

2.strcpy类

在编译的过程中我们会频繁地做一些字符串拷贝(strcpy),比较(strcmp)和计算长度(strlen)这类函数。关于如何简单明了的知道这类库函数的使用,我放三个博客在下面大家可以去看看。
模拟strcpy函数的使用
模拟strcmp函数的使用
模拟strlen函数的使用

3.math类

在编译的过程中,我们也会进行计算,电脑计算能力一流,原因是因为它运算速度快,那我们需要引进会计算的库函数来帮助我们进行计算,例如pow函数可以计算一个数的n次方。

4.概念

标准库 - 提供了C语言的库函数。
如果大家都运用这些函数,都用的是printf,strlen,pow这类函数,那是不是直接放到标准库里面,我们用的时候只需要去一调用,直接一行代码就可以使用了,是非常方便的,提高了可移植性和提高程序的效率。那假如说同样都是pow函数,中国程序员写:ncifang(),而英国程序员写:pow(),这样是不是有差异了,中国程序员看不懂英国程序员写的,英国程序员看不懂中国程序员写的,这可移植性是很差的,写的程序给别人别人看不懂那一大串是啥意思,这时候标准库的出现解决了这一问题,只需要有一样的标准即可,大家都可以调用这个函数且大家都能看懂,方便多了。

5.小知识

如何学习库函数的使用:
法一:下载MSDN软件,进行索引查询
法二:访问库函数查询网站

法三:访问库函数查询网址2(英文版)或库函数查询网址3(中文版)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.总结

1.C语言常用的库函数有:
I/O函数 – input/output 输入输出函数 例如:scanf,printf
字符串操作函数 – strlen,strcmp,strcpy
字符操作函数 – 大小写转换/字符分类
内存操作函数 – memcpy,memmoove,memset
时间/日期函数 – time
数学函数 – pow,sqrt
其他库函数
2.要借助工具学习库函数,在小知识中已经包含了。
3.英文很重要,要看得懂文献。

(二)自定义函数

1.概念

要是所有都可以使用库函数,都从一个标准库里面能够调出来,那程序员不是失业了吗?所以随之而来的是有了自定义函数,就是程序员可以自己写函数并进行调用,但是函数名需要让大家能够看懂,如果写了几千行代码的函数,其函数名叫a,好家伙,程序员得看几千行代码才能知道这个a函数是干啥的,那如果你命名很通俗易懂,那很简单,程序员拿到函数的程序,稍微一检查,直接调用,是不是很方便呢?

2.函数的组成

ps:(*)表示还有很多,是等等的意思。
在这里插入图片描述

3.例子1(求出两个数中的最大值)

代码如下:

#include<stdio.h>
//写一个函数可以找出两个整数中的最大值

//定义函数
int Max(int x,int y) {
	if (x > y) {
		return x;
	}
	else {
		return y;
	}
}

int main() {
	int a = 0;
	int b = 0;
	//输入
	scanf("%d %d", &a, &b);  //推演函数的使用场景
	//找出a和b中的最大值
	int max = Max(a, b); //传参
	//打印
	printf("%d\n", max);

	return 0;
}

以下是函数框架所代表的信息,与函数的组成相一致:
在这里插入图片描述

以下为函数调用过程图:
在这里插入图片描述

调试结果如下:
在这里插入图片描述
在这里插入图片描述

4.例子2(交换两个整数变量的内容)

这个例子不用函数编写其实很简单,但是一旦利用函数那么就要有所讲究了,不能是简单的传值的操作,而应当是传地址的操作。这一部分涉及到了函数的栈帧的创建与销毁,进入调试窗口你会发现底下两个变量的地址与进入函数的那两个变量的地址不相同,大家有兴趣可以去看一下函数栈帧与创建的博客。

函数栈帧的创建与销毁

相信大家看完那篇博客,已经对函数有了更深的理解,接下来,看一个错误示范寻找一下问题。
在这里插入图片描述
修改了x,y之后怎样,a和b的值没有进行改变,你只是交换的是形参的值,在内存中形参也是有地址存储的,它们进行改变没有影响实际参数的值,所以结果不变,可以说形参只是实参的一份临时拷贝是完全没有错误的。

那怎么做呢?那就用取地址呀,取到那个地址,使函数外部变量与函数内部变量产生联系,在同一个空间进行交换不就能做了吗?大家可能会有点疑惑,这个指针似乎不理解,其实大家只需要知道取了那个变量的地址存放到另一个变量中,在对这个变量进行修改即可,即:
在这里插入图片描述
如果大家还想提前深入了解指针,我放一篇博客供大家先去预习一下。
初阶指针

接下来我们回来,进行交换值的操作:

在这里插入图片描述
其实设计的很巧妙,是把a,b的地址找两个变量进行存放,再通过这个地址去找到a,b这两个变量,引入tmp进行交换。相当于给两个人两个门牌号,通过这个门牌号去找到房间里面的人并进行交换。

代码如下:

void Swap(int* x, int* y) {
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

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\n", a, b);

	return 0;
}

5.小知识

Q1.如何调出监视窗口?
答:先按F10,再去找如下图所示:
在这里插入图片描述

Q2.如何进入函数?
答:按F11进入函数内部。

Q3.为什么我的F10和F11按了以后锁屏幕了!?
答:因为刚开始的电脑没那么多的娱乐化按键,大多数都是进行编译用的,但电脑逐渐普及了以后,电脑生产厂商看大家都不怎么用这几个按键,那好嘛,就全部换成了调节声音,调节屏幕亮度,锁屏等此类的按键了,那怎么解决这个问题呢?其实很简单,按Fn+F10即可,因为Fn为辅助按键,一起按电脑才知道原来执行的是这个命令。

6.总结

在进行函数的使用的时候,一定要根据函数的构成每一步都不能落下,一旦落下,编译器就会报错。那当然,如果你不想传参或者不需要返回值,那很简单,写一个void哦,千万不能不写,返回类型不写的话编译器默认返回值是int类型,而参数那里什么都不写的话,编译器直接报错。
例如:在这里插入图片描述
在这里插入图片描述


三、函数的参数

(一)实际参数(实参)

真实传递给函数的参数,叫实参。
实参可以是:变量、常量、表达式、函数等。
无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。
在这里插入图片描述

(二)形式参数(形参)

形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的时候才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。

形参实例化之后其实相当于实参的一份临时拷贝。


四、函数的调用

(一)传值调用

函数的形参和实参分别占有不同的内存块,对形参的修改不会影响实参,就例如上面所讲的Max函数,是传值调用。

(二)传址调用

传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。就例如上面所讲的交换两个整数变量的内容的Swap函数一样。

(三)练习

1.写一个函数打印100到200之间的素数

//输出100到200之间的素数
#include<stdio.h>
#include<math.h>

is_prime(int i) {
	int j = 0;
	for (j = 2; j < sqrt(i); j++) {
		if (i % j == 0) {
			return 0;  //不是素数
		}
	}
	return 1;  //是素数
}

int main() {
	int i = 0;
	for (i = 100; i <= 200; i++) {
		//判断i是不是素数
		if (is_prime(i)) {
			printf("%d ", i);
		}
	}
	
	return 0;
}

2.写一个函数输出1000年到2000年的闰年

//写一个函数判断一年是不是闰年
// 
//1.能被4整除,并且不能被100整除为闰年
//2.能被400整除为闰年
//
int is_loop(int i) {
	if ((i % 400 == 0) || (i % 100 != 0) && (i % 4 == 0)) {
		return 1;
	}
	else {
		return 0;
	}
}


int main() {
	int i = 0;
	for (i = 1000; i <= 2000; i++) {
		//判断i是不是闰年
		//是闰年返回1
		//不是闰年返回0
		if (is_loop(i)) {
			printf("%d ", i);
		}
	}

	return 0;
}

3.写一个函数,实现一个整形有序数组的二分查找

(1)详细解题思路:

在这里插入图片描述
在这里插入图片描述

(2)小知识:

sizeof是求元素的占内存空间的字节长度。
在这里插入图片描述

(3)代码:

//写一个函数,实现一个整形有序数组的二分查找

#include<stdio.h>

int binary_search(int arr[], int k, int sz) {
	int left = 0;
	int right = sz - 1;
	while (left <= right) {
		int mid = (left + right) / 2;
		if (arr[mid] < k) {
			left = mid + 1;
		}
		else if (arr[mid] > k) {
			right = mid - 1;
		}
		else {
			return mid;  //返回下标
		}
	}
	return -1; //找不到
}
int main() {
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int k = 0;
	scanf("%d", &k);  //输入需要找的值
	int sz = sizeof(arr) / sizeof(arr[0]); //算出数组元素个数,数组总体元素的字节长度除以数组第一个元素字节的长度
	//找到就返回下标
	//找不到返回-1
	int pos = binary_search(arr, k, sz);
	if (-1 == pos) {
		printf("没找到\n");
	}
	else {
		printf("找到了,下标为:%d\n", pos);
	}

	return 0;
}

4.写一个函数,每调用一次这个函数,就会将num的值增加

//写一个函数,每调用一次这个函数,就会将num的值增加

#include<stdio.h>

void Add(int* p) {
	//*p = *p + 1;
	(*p)++;
}

int main() {
	int num = 0;
	Add(&num);
	printf("%d\n", num);  //1
	Add(&num);
	printf("%d\n", num);  //2
	Add(&num);
	printf("%d\n", num);  //3

	return 0;
}

(四)函数调用实质

在这里插入图片描述

就好像,你在看一个电影,看到20分钟的时候,妈妈叫你去打瓶酱油,你先摁下暂停键,再去打酱油,打完酱油回来再摁开始键继续看电影,这是一个顺序的问题,在一个时间轴上,只有干完这件事,才继续去干原先是事。


五、函数的嵌套调用和链式访问

(一)嵌套调用

先举个小栗子:
在这里插入图片描述

函数之间是可以互相调用的,调用了一个函数以后,我们又需要在这个函数里再调用一个函数进行另外的计算,但是切记,函数不能嵌套定义,就是不能在函数内部再定义一个函数(在一个函数内部再写一个函数的内容),因为每个函数之间都是平等的,是平等的关系。

(二)链式访问

1.概念

定义:把一个函数的返回值作为另一个函数的参数。

举个例子:
在这里插入图片描述

2.常见例子:

int main() {
	printf("%d", printf("%d", printf("%d", 43)));
	
	return 0;
}

大家可以想想这个奇怪的式子打印出来的是什么?
接下来分析一下,大家有了上面概念的基础的加持,知道打印的是另一个函数的参数,那就是printf的参数咯!那我们打开MSDN一看关于printf函数的描述和返回值。
在这里插入图片描述
发现,printf的返回值是所指向的字符数,那就好办了,先打印43,43俩字符,打印2,2一个字符,再打印1。
在这里插入图片描述
在这里插入图片描述


六、函数的声明和定义

(一)函数声明

1.概念

1.告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。
2.函数的声明一般出现在函数的使用之前。要先声明后使用。
3.函数的声明一般要放在头文件中的。
4.声明中可以不加形参的名字,只要形参的类型即可。

2.解析

大家可能在看书或者做题的时候不像我上面写的那些函数那样,很多情况都是以下代码:

#inclue<stdio.h>

//函数的声明
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\n", ret);

	return 0;
}

int Add(int x, int y) {
	return x + y;
}

很简单,Add(int x, int y);这个就是函数的声明,那这个声明有什么用呢,或者怎么用呢?接下来我将细细分析!
在这里插入图片描述

(二)函数定义

函数的定义是指函数的具体实现,交代函数的功能实现,而函数定义也是一种特殊的声明。

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

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

	return 0;
}

(三)拓展(模块化开发)

函数的声明和定义在很多地方不像上面所述的那么简单,而在工程当中是需要创建头文件和其他工程的,如下图所示:

创建三个文件,一个头文件,两个工程文件,即两个模块(add.c+add.h和test.c):
在这里插入图片描述

函数的主要部分放到test.c中:
在这里插入图片描述

函数的定义放到add.c中:
在这里插入图片描述

函数的声明放到头文件add.h中:
在这里插入图片描述

模块化开发的优势:
在这里插入图片描述
在这里插入图片描述

大家如果对这个模块化开发很感兴趣的话,可以看一下我写的三子棋和扫雷游戏的博客:
三子棋
扫雷


七、函数递归

(一)什么是递归

1.概念

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

递归作为一种算法在程序设计语言中广泛应用,一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法。

特点:它通常是把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。

只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。

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

2.简单代码

(1)史上最简单递归

在这里插入图片描述

(2)出现的问题

在这里插入图片描述

(3)推荐的网站

stackoverflow.com
《程序员问答社区》(国外)
在这里插入图片描述
segment.com
《思否》(国内)
在这里插入图片描述

(二)递归的两个必要条件

1.概念

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

2.经典练习题1

接受一个整型值(无符号),按照顺序打印它的每一位。
例如:输入1234 输出1 2 3 4

(1)思路

在这里插入图片描述

(2)代码

#incldue<stdio.h>
void Print(unsigned int num) {
	if (num > 9) {
		Print(num / 10);
	}
	printf("%d ", num % 10);
}

int main() {
	unsigned int num = 0;
	scanf("%u", &num);
	//写一个函数打印num的每一位
	Print(num);
	
	return 0;
}

(3)分析

在这里插入图片描述

3.经典练习题2

模拟strlen函数,求字符串长度。
给大家分享一篇博客,是关于详解strlen函数的博客!
模拟strlen函数

(1)思路

不创建临时变量,只要第一个字符不是‘\0’,则拿出第一个字符,并求其他字符,递归下去:
arr+1表示指针指向往后移一位,*arr表示访问指向的这块空间。
在这里插入图片描述

在这里插入图片描述

(2)代码

#include<stdio.h>
int my_strlen(char* arr) {
	if (*arr != '\0') {
		return 1 + my_strlen(arr + 1);  //指针指向往后移一位
	}
	else{
		return 0;
	}
	
}


int main() {
	char arr[] = "bit";
	//{b i t \0}
	int len = my_strlen(arr);
	printf("%d\n", len);
	
	return 0;
}

(3)分析

在这里插入图片描述

(三)递归与迭代

1.经典练习题3

求n的阶乘

(1)思路

n!可以转化为n*(n-1)!来进行计算。
在这里插入图片描述

(2)代码

//写一个函数求n的阶乘

#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;
}

(3)分析

在这里插入图片描述
大家可能有疑问了,这个n为什么调用完变了,返回的时候那个n还是原先那个值,不是应该改变的吗?这其实涉及到了一个比较难的知识,是函数栈帧的创建与销毁,大家可以先去看看我写的关于函数栈帧创建与销毁的博客:
函数栈帧的创建与销毁

以下是简单介绍,大家看完介绍可以去看看函数栈帧的创建与销毁的那篇博客。
在这里插入图片描述
在这里插入图片描述

2.迭代概念

如果产生很大的数字,超过42亿的数了,用递归程序就容易崩掉,这是由于出现栈溢出的问题,所以就出现了迭代的思想,而迭代是什么,迭代其实就是循环,栈溢出的现象就会迎刃而解。

3.经典练习题4

求第n个斐波那契数(兔子繁殖问题)(不考虑栈溢出问题)

(1)思路

在这里插入图片描述

(2)代码

//求第n个斐波那契数(兔子繁殖问题)
//前两个数相加等于第三个数

#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);
	//写一个函数求第n个斐波那契数
	int ret = Fib(n);
	printf("%d\n", ret);
	
	
	return 0;
}

(3)分析

在这里插入图片描述

(4)问题

递归解法太低效了,算n=50,需要好久。
我们可以看一下n=40时第三个斐波那契数需要被计算多少次:
在这里插入图片描述

(5)迭代做法

计算效率过于低,但是可以写成循环的方式,也就是迭代,速度非常快。
如下:前两个值相加赋给第三个值,然后第二个值赋给第一个值,第三个值赋给第二个值,一直加到相应的n的值。
在这里插入图片描述

#include<stdio.h>

int Fib(int n) {
	int a = 1;
	int b = 1;
	int c = 1;
	while (n > 2) {
		c = a + b;
		a = b;
		b = c;
		n--;  //次数逐渐减一
	}
	return c;
}

int main() {
	int n = 0;
	scanf("%d", &n);
	//写一个函数求第n个斐波那契数
	int ret = Fib(n);
	printf("%d\n", ret);
	
	return 0;
}

4.改进

1.在调试factorial 函数的时候,如果你的参数比较大,那就会报错:stackoverflow(栈溢出)这样的信息。
2.系统分配给程序的栈空间是有限的,但是如果出现了死循环,或者(死递归),这样有可能导致一直开辟栈空间,最终产生栈空间耗尽的情况,这样的现象我们称为栈溢出。

5.解决方法

1.将递归改写成非递归。
2.使用static对象替代nonstatic局部对象。在递归函数设计中,可以使用static对象替代nonstatic局部对象(即栈对象),这不仅可以减少每次递归调用和返回时产生和释放 nonstatic 对象的开销,而且 static 对象还可以保存递归调用的中间状态,并且可为各个调用层所访问。

6.提示

1.许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。
2.但是这些问题的选代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
3.当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。
4.递归可以转化为迭代(循环)(非递归),这是递归与非递归之间的转换。

(四)递归的经典题目

1.汉诺塔问题

(1)题目描述和思路

在这里插入图片描述

描述:一次移动一个方块,将A柱子上面的方块全部移动到C柱子上,前提是大的方块不能压在小的方块下面。
思路:三个方块:1->C,2->B,1->B,3->C,1->A,2->C,1->C。

四个盘子就是三个盘子的递归加一个盘子,依次类推。

(2)代码

#include<stdio.h>

void move(char A, char C, int n)
{
	printf("把第%d个方块从%c--->%c\n", n, A, C);
}

void HanoiTower(char A, char B, char C, int n)
{
	if (1 == n)
	{
		move(A, C, n);
	}
	else
	{
		//把n-1个方块从A柱借助于C柱移动到B柱上
		HanoiTower(A, C, B, n - 1);
		//把A柱子上最后一个方块移动到C柱上
		move(A, C, n);
		//把n-1个方块从B柱借助于A柱移动到C柱上
		HanoiTower(B, A, C, n - 1);
	}
}

int main()
{
	int n = 0;
	printf("输入A柱子上的方块个数>\n");
	scanf("%d", &n);

	//将n个方块从A柱借助于B柱移动到C柱上
	HanoiTower('A', 'B', 'C', n);
	return 0;
}

具体我会出一篇博客细细讲解一下汉诺塔问题。

2.青蛙跳台阶问题

(1)题目描述和思路

描述:有n个台阶,青蛙一次可选择跳1个台阶或者2个台阶,那总共有多少种跳法?
思路:在这里插入图片描述
其实就是一个斐波那契数列。

(2)代码

代码如下:

#include<stdio.h>
int Add(int n) {
	int a = 1;
	int b = 2;
	int c = 0;
	while (n > 2) {
		c = a + b;
		a = b;
		b = c;
		n--;
	}
	return c;
	if (n <= 2) {
		return n;
	}
}

int main() {
	int n = 0;
	//输入总共有多少台阶
	printf("请输入总共有多少个台阶>");
	scanf("%d", &n);

	//调用一个函数青蛙跳台阶次数
	int ret = Add(n);

	//打印总共跳了多少次
	printf("青蛙总共跳了多少次:%d\n", ret);;

	return 0;
}

int Add(int n) {
	if (1 == n) {
		return 1;
	}
	else if (2 == n) {
		return 2;
	}
	else {
		return Add(n - 1) + Add(n - 2);
	}
}

int main() {
	int n = 0;
	//输入总共有多少台阶
	printf("请输入总共有多少个台阶>");
	scanf("%d", &n);

	//调用一个函数青蛙跳台阶次数
	int ret = Add(n);

	//打印总共跳了多少次
	printf("青蛙总共跳了多少次:%d\n", ret);;
	
	return 0;
}

具体关于汉诺塔问题和青蛙跳台阶问题我会再出一篇博客细细讲解。


总结

如果说指针是C语言的灵魂,那么函数就是C语言的根本,函数在C语言当中是很重要的,我们重点分析了递归,递归难度很大,要慢慢理解。这一单元,我们详细了解了函数就是在栈区开辟一块属于自己的空间并进行运算,大家最需要了解到的是函数栈帧的创建与销毁,还有就是需要看一部分指针的内容,可以不用看的很熟练,后续的指针单元讲解大家会很轻松的掌握指针的。


客官,码字不易,来个三连支持一下吧!!!

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

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

相关文章

mac释放“其他”内存空间的解决方法

官方解释Mac设备储存空间中的“其他”数据包含这不可移除的移动资源&#xff0c;例如&#xff0c;Siri 语音、字体、词典、不可移除的日志和缓存、聚焦索引以及系统数据如钥匙串和 CloudKit 数据库、系统无法删除缓存的文件等之外&#xff0c;还包含了一些无法识别的文件。当“…

ROS2 基础概念 节点

ROS2 基础概念 节点1. Nodes2. 重映射3. 环境设置3.1. ROS_DOMAIN_ID3.2. ROS_LOCALHOST_ONLY1. Nodes 每个节点应负责单个模块用途&#xff08;例如&#xff0c;一个节点用于控制车轮电机&#xff0c;一个用于控制激光测距仪等&#xff09; 可以通过话题、服务、操作或参数向…

C++-----模板

举个例子&#xff0c;如果要你交换两个数值&#xff0c;你会怎么做呢&#xff1f; ————你肯定会说&#xff0c;那就写一个Swap交换函数吧&#xff01; 没错&#xff01;Swap函数确实可以实现交换&#xff0c;但如果我想让你同时进行不能类型的数值呢&#xff0c;比如floa…

F - Permutation Distance(去绝对值数据结构)[AtCoder Beginner Contest 283]

题目如下&#xff1a; 题目链接 题解 or 思路&#xff1a; 去掉绝对值后 有 2242 \times 2 4224 中情况 虚线括起来的是需要维护的&#xff0c;其他直接枚举就行! 对于 pi<pjp_i < p_jpi​<pj​ 的情况&#xff0c;设我们维护的式子为 xxx 那我们每次枚举查找的范围…

hadoop生产调优之HDFS—集群压测

在企业中非常关心每天从 Java 后台拉取过来的数据&#xff0c;需要多久能上传到集群&#xff1f;消费者关心多久能从 HDFS 上拉取需要的数据&#xff1f; 为了搞清楚 HDFS 的读写性能&#xff0c;生产环境上非常需要对集群进行压测。 HDFS 的读写性能主要受网络和磁盘影响比较大…

【matplotlib】3-绘制统计图形

文章目录绘制统计图形1.柱状图1.1 应用场景--定性数据的分布展示1.2 绘制原理2.条形图3.堆积图3.1 堆积柱状图3.2 堆积条形图4.分块图4.1 多数据并列柱状图4.2 多数据平行条形图5.参数探索6.堆积折线图、间断条形图和阶梯图6.1 用函数stackplot()绘制堆积折线图6.2 用函数broke…

Matlab 方位角计算

文章目录 一、简介二、实现代码三、实现效果一、简介 方位角是指从某点的正北方向起顺时针旋转到某目标点方向的水平夹角,角度范围(0~360)。如下所示: 令atn= a r c t a n ( Δ Y A B / Δ X

9. SpringMvc拦截器

1. 拦截器概念和作用 拦截器&#xff08;Interceptor&#xff09;是一种动态拦截方法调用的机制&#xff0c;在SpringMVC中动态拦截控制器方法的执行作用&#xff1a; 在指定的方法调用前后执行预先设定的代码阻止原始方法的执行总结&#xff1a;增强 核心原理&#xff1a;AOP…

[CG笔记]绘制图元:三角形

学习资料是Github的一个项目Tiny renderer or how OpenGL works: software rendering in 500 lines of code 本文对应原教程的第二课的部分内容 原教程重在思路&#xff0c;主要内容是以推导为主&#xff0c;所以这里还是记录思路和为代码做注释 知乎也有人给出了中译版&…

ARM uart stdio 的移植

一、uart stdio的移植1 1. 什么是 stdio (1) #include <stdio.h> (2) stdio&#xff1a;standard input output&#xff0c;标准输入输出 (3) 标准输入输出就是操作系统定义的默认的输入和输出通道。一般在 PC 机的情况下&#xff0c;标准输入指的是键盘&#xff0c;标…

C语言——操作符详解(下)

C语言——操作符详解&#xff08;下&#xff09;一、赋值操作符二、复合赋值符三、单目操作符单目操作符介绍四、 关系操作符五、逻辑操作符六、条件操作符七、逗号表达式八、下标引用、函数调用和结构成员8.1 [ ] 下标引用操作符8.2 ( ) 函数调用操作符8.3访问一个结构的成员一…

【Linux】Linux指令串讲

大家好&#xff0c;今天要开启一个新的专题&#xff1a;Linux 今天的内容是指令还有一些基本的Linux知识补充 由于Linux的知识很难明确写出分类&#xff0c;所以目录就不会做的特别详细完全 喜欢的小伙伴点赞收藏一下不迷路哦 目录 1.目录 2.文件 3.路径 1.目录 1.创建目录…

初识Docker:(3)Docker架构

初识Docker&#xff1a;&#xff08;3&#xff09;Docker架构镜像和容器Docker和DockerHubDocker架构总结镜像和容器 镜像&#xff08;Image&#xff09;&#xff1a;Docker将应用程序及其所需的以来、函数库、环境、配置等文件打包在一起&#xff0c;成为镜像。 容器&#x…

力扣27.移除元素(双指针算法)

题目描述&#xff1a; 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考…

编译openssl支持libcurl的https访问

如果编译时不带openssl库那么无法访问https的网页&#xff0c;从网页端什么也获取不到。 在调用post请求访问翔云OCR人脸识别时无法访问&#xff0c;而使用ssl&#xff0c;需要先在系统中安装OpenSSL。 如下图安装说明&#xff1a; 所以将原先安装的libcurl库删掉&#xff0…

MySQL事务的隔离级别

&#x1f3c6;今日学习目标&#xff1a; &#x1f340;MySQL事务的隔离级别 ✅创作者&#xff1a;林在闪闪发光 ⏰预计时间&#xff1a;30分钟 &#x1f389;个人主页&#xff1a;林在闪闪发光的个人主页 &#x1f341;林在闪闪发光的个人社区&#xff0c;欢迎你的加入: 林在闪…

【C与数据结构】——寒假提高每日练习Day2

一共16日的练习&#xff0c;分为选择题与编程题&#xff0c;涵盖了C语言所学以及数据结构的重点&#xff0c;以及一些秋招、春招面试的高频考点&#xff0c;难度会随着天数而上升。1-8day为C语言&#xff0c;9-16day为数据结构。 &#xff08;建议在电脑客户端进行&#xff0c…

docker中的数据卷

目录 1. 为什么使用数据卷 2. 数据卷基本操作 2.1 创建数据卷 2.2 查看数据卷 2.3 查看数据卷详细信息 2.4 数据卷删除 3. 数据卷的使用 3.1 先创建数据卷再挂载 3.2 直接挂载宿主机目录 3.3 只读数据卷 4. 数据卷容器 4.1 新建数据卷容器 4.2 新建一个容器来使用数…

Nacos学习笔记 (8)服务发现基础应用

1. 什么是服务发现 在微服务架构中&#xff0c;整个系统会按职责能力划分为多个服务&#xff0c;通过服务之间协作来实现业务目标。这样在我们的代码中免不了要进行服务间的远程调用&#xff0c;服务的消费方要调用服务的生产方&#xff0c;为了完成一次请求&#xff0c;消费方…

求空间曲线的切线,法平面

求空间曲线的切线&#xff0c;法平面&#xff1a;归结为求空间曲线的切向量 进而用点向式直线方程表示出切线&#xff0c;点法式方程表示出法平面 情况一&#xff1a;空间曲线以参数式给出&#xff0c;求切向量时直接求导即可&#xff0c;如下题 情况二&#xff1a;空间曲线…