函数(2)

本文介绍了函数的嵌套调用和链式访问的概念,展示了如何在C语言中实现。接着,讨论了函数的声明和定义的区别,强调了先声明后使用的原则。然后,讲解了递归的基本思想和实例,包括简单的递归调用和递归求解字符串长度、阶乘及斐波那契数列的方法。最后,提到了递归效率问题以及如何通过迭代优化递归算法。
摘要由CSDN通过智能技术生成


衔接函数(1),今天将把初阶的函数总结完成


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

  • 函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的。
嵌套调用
void A()
{

}

void B()
{

}

int main()
{

	return 0;
}

但是函数不能嵌套定义,如:

void A()
{
void B()
{

}
}
链式访问
#include <stdio.h>
#include<string.h>
int main()
{
	/*int len = strlen("abcdef");
	printf("%d\n", len);*/

	printf("%d\n", strlen("abcdef"));//链式访问
	return 0;
}
#include <stdio.h>
#include<string.h>
int main()
{
	/*int len = strlen("abcdef");
	printf("%d\n", len);*/

	//printf("%d\n", strlen("abcdef"));
	printf("%d", printf("%d", printf("%d", 43)));
	return 0;
}

在这里插入图片描述
printf 被写的字符个数将会被返回,即printf("%d", printf("%d", printf("%d", 43)));中的printf("%d",43)被读取两个字符后,printf("%d", printf("%d", 2));,同理,printf("%d",2)读取一个字符后返回1,即printf("%d",1);所以最终结果为4321

函数的声明和定义

  • 变量也有声明和定义
函数声明
  1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数
    声明决定不了。
  2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用
  3. 函数的声明一般要放在头文件中的。
#include <stdio.h>

int a;//声明
int main()
{
	printf("%d\n", a);
	return 0;
}
int a = 10;//定义
#include <stdio.h>
int a;//定义
int main()
{
	printf("%d\n", a);
	return 0;
}

无论是变量还是函数,都得满足先定义(声明)后使用。

#include <stdio.h>
//函数的声明
int Add(int x, int y);

int main()
{
	int a = 10;
	int b = 20;
	int c = Add(a, b);
	printf("%d\n", c);
	return 0;
}
//函数的定义
int Add(int x ,int y)
{
	return x + y;
}

实际的写法我们可以:
在这里插入图片描述

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

test.h的内容
放置函数的声明

#ifndef __TEST_H__
#define __TEST_H__
//函数的声明
int Add(int x, int y);
#endif //__TEST_H__

test.c的内容
放置函数的实现

#include "test.h"
//函数Add的实现
int Add(int x, int y)
{
return x+y;
}

函数递归

什么是递归
  • 程序调用自身的编程技巧称为递归( recursion)。
  • 递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
  • 递归的主要思考方式在于:把大事化小

  • 最简单递归:
#include<stdio.h>
int main()
{
	printf("hehe\n");
	main();
	return 0;
}

无限打印hehe,程序崩溃。
在这里插入图片描述

练习1
  • 接受一个整型值(无符号),按照顺序打印它的每一位。
    例如:
    输入:1234,输出 1 2 3 4
  • 思路:
    1234
    1234%10=4
    1234/10=123
    123%10=3
    123/10=12
    12%10=2
    12/10=1
    1%10=1
#include<stdio.h>
//%u -无符号的整数
//%d -有符号的整数
void Printf(unsigned int n)
{
	if (n > 9)
	{
		Printf(n / 10);
	}
	printf("%d ", n % 10);
}

//Printf(1234)
//Printf(123) 4
//Printf(12) 3 4
//Printf(1) 2 3 4

int main()
{
	unsigned int num = 0;
	scanf("%u", &num);
	Printf(num);
	return 0;
}

在这里插入图片描述

递归的两个必要条件
  • 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
  • 每次递归调用之后越来越接近这个限制条件
练习2
  • 编写函数不允许创建临时变量,求字符串的长度。

在此之前,先做一个小铺垫,

  • 编写函数,求字符串的长度。
#include <stdio.h>
#include <string.h>
int main()
{
	char arr[10] = "abcdef";
	int len = strlen(arr);
	printf("%d\n", len);
	return 0;
}

在这里插入图片描述

#include <stdio.h>
#include <string.h>
int my_strlen(char* str)
{
	int count = 0;
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
int main()
{
	char arr[10] = "abcdef";
	// [a b c d e f \0 _ _ _]

	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}
  • tip:数组名是数组首元素的地址
    有临时变量int count = 0;
    在此思路上,回归练习2.。

  • 思路:
    在这里插入图片描述
#include <stdio.h>
#include <string.h>


int my_strlen(char* str)
{
	if (*str != '\0')
	    return 1 + my_strlen(str + 1);
	else
		return 0;
	
}
int main()
{
	char arr[10] = "abcdef";
	// [a b c d e f \0 _ _ _]

	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

在这里插入图片描述

  • 说明:
    在这里插入图片描述
递归与迭代
  • 循环其实就是一种迭代
练习3
  • 求n的阶乘。(不考虑溢出)
#include<stdio.h>

int fac(int n)
{
	int i = 0;
	int ret = 1;
	for (i = 1; i <= n; i++)
	{
		ret = ret * i;
	}
	return ret;
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = fac(n);
	printf("%d\n", ret);
	return 0;
}

在这里插入图片描述

#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;
}
练习4
  • 求第n个斐波那契数。(不考虑溢出)

  • 斐波那契数列:
    1 1 2 3 5 8 13 21 34 55…

  • 思路:
    在这里插入图片描述
#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\n", ret);
	return 0;
}

但是,当我们输入50时,使用 fib 这个函数的时候如果我们要计算第50个斐波那契数字的时候特别耗费时间。
在这里插入图片描述
程序中会出现大量的重复计算,我们不妨定义一个变量count,来看程序中第3个斐波那契数运算的次数。
即:

#include<stdio.h>
int count = 0;
int Fib(int n)
{
//统计的是第三个斐波那契数列被重复运算的次数
	if (n == 3)
		count++;
	if (n <= 2)
		return 1;
	else
		return Fib(n - 1) + Fib(n - 2);
}

int main()
{
	int n = 0;
	scanf("%d", &n);//40
	int ret = Fib(n);
	printf("%d\n", ret);
	printf("count = %d\n", count);
	return 0;
}

在这里插入图片描述

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

  1. 将递归改写成非递归。
  2. 使用static对象替代 nonstatic 局部对象。在递归函数设计中,可以使用 static 对象替代
    nonstatic 局部对象(即栈对象),这不
    仅可以减少每次递归调用和返回时产生和释放 nonstatic 对象的开销,而且 static 对象还可以保
    存递归调用的中间状态,并且可为
    各个调用层所访问。
  • 迭代的方式
//求第n个斐波那契数。(不考虑溢出)
#include<stdio.h>
//1 1 2 3 5 8 13 21 34 55........
int Fib(int  n)
{
	int a = 1;
	int b = 1;
	int c = 1;
	while (n >= 3)
	{
		c = a + b;
		a = b;
		b = c;
		n--;
	}
	return c;
}
int main()
{
	int n = 0;
	scanf("%d", &n);//40
	int ret = Fib(n);
	printf("%d\n", ret);
	return 0;
}
  • tip:
  1. 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。
  2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
  3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。

感谢观看!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值