本人0基础开始学编程,我能学会的,你也一定可以,学会多少写多少。
下载安装请从官网入手,社区版本即可,这里主要使用的软件是VS2019,图标如下。
上一篇:从0开始学c语言-14-关于(2)函数的嵌套调用和链式访问、函数的声明和定义、静态库_阿秋的阿秋不是阿秋的博客-优快云博客
目录
七、函数递归
1·递归是什么
2·递归的两个必要条件
3·代码演示
例如:输入1234,打印1 2 3 4
先写出主函数,
int main()
{
unsigned int a = 0; //无符号整形变量a
scanf("%u", &a); //%u打印无符号整型,别忘记取地址
aqiu(a); //我们定义这个函数打印输入值的每一位
return 0;
}
那么关键是如何设计这个函数,我们采用递归的方式来思考,
1·大事化小
2·有限制条件
3·每次调用接近限制条件
首先考虑,1234怎么运算就可以输出4了呢?我们可以让1234除上10,那么余数就会是4。
那么该怎么得到3呢?让123除10,余数是3。
以此类推就像是这样
1·大事化小
2·限制条件
也许你想不到限制条件,那是因为你不知道限制条件该用在哪里,起到什么作用。
这里提醒一下,每次我们调用输入值的时候都是通过输入值除10来获得下一个输入值,那么我们什么时候就不需要在除10来获得下一个输入值了呢?这里放一个图。
当输入值为9的时候,我们所获得的下一个输入值为0,这显然不是我们想要的下一个输入值,所以我们这里设定限制条件:输入值>9的时候需要/10。
3·每次调用接近限制条件
可以看到,每次/10来获得的下一个输入值在不断减小,我们已经达到每次调用接近限制条件。
那么现在可以写出这个函数了。
void aqiu(unsigned int b)
{
if (b > 9) //限制条件
{
aqiu(b / 10); //调用接近限制条件
}
printf("%u ", b%10);
}
int main()
{
unsigned int a = 0;
scanf("%u", &a); //%u打印无符号整型,别忘记取地址
aqiu(a); //这个函数打印输入值的每一位
return 0;
}
可能你还不是很理解,我们画图示意一下。
Stack overflow 栈溢出
我们用这段代码看看什么叫栈溢出。
void qiu(int b)
{
if (b <900000)
{
qiu(b + 1); //开始调用,一下子走完这里会出警告
}
}
int main()
{
qiu(1);
return 0;
}
每一次调用函数都要在栈区分配栈帧空间,递归太多次就不够用了~
所以我们可以总结一下递归:
秉持大事化小的思维
1、设置跳出条件
2、调用会逼近跳出条件
3、递归层次不能太深
练习
题目:编写函数不允许创建临时变量,求字符串的长度。
请使用递归思想。
***(新知识)***这里我先介绍一下应该知道的知识点。
代码是这样的,不需要运行,调试看数组和函数参数关系就好。
int bbc(char* s)
{
printf("%p\n", s);
*s++;
s++;
//printf("%p\n", *s);
return 1;
}
int main()
{
char qiu[] = "sadday";
printf("%p\n", qiu); //数组本身就是地址,不需要&
bbc(qiu);
return 0;
}
从上图我们可以看到qiu数组中有6个元素,qiu[0]代表数组的首元素。
可以看到,上图中的s是传过去的qiu数组的首元素地址。
s++可以得到下一个元素的地址,并且首元素变为s++所对应的那个元素。
*s代表的是元素,不包含地址。
*(s+1)可以得到下一个元素,但是*s+1则代表了元素所对应的ASCII码表所对应的十进制数字。
s和s+1是char*类型,并且包含所对应的char类型元素。
*s和*(s+1)是char类型。
有了这些知识就可以写出我们需要的代码了。
编写函数不允许创建临时变量,求字符串的长度。
int len(char* s)
{
int count = 0;
while (*s != '\0')
{
count++;
s++; //写成*s++也能运行会警告,但是我没懂具体原因是什么
}
return count;
}
int main()
{
char a[] = "sadasd";
printf("%d\n", len(a)); //打印数组a的字符串长度
return 0;
}
但是这不是递归思想。
拿出来我们之前总结的东西来思考
秉持大事化小的思维
我们把sad字符拆成求sa和d的长度,再把sa拆成求s和a的长度。
1、设置跳出条件
条件挺明显的把?这里再提示一下:我们传过去的是首元素的地址哦!
2、调用会逼近跳出条件
试着每次调用把参数发生变化一下,既然是字符串的话,你能改变什么呢?
3、递归层次不能太深
这个应该不会涉及到。
然后就敲出来了。
int len(char* s)
{
if (*s != '\0')
{
return 1 + len(s + 1); //和上面的递归思维一样,大事化小
}
else
return 0;
}
int main()
{
char aqiu[] = "asdadd";
printf("%d\n", len(aqiu));
}
这里不再画图示意,大家要学着自己画图梳理思路。
4·递归和迭代
练习1
我们写一个数的阶乘代码,会是这样。这叫迭代。
int main()
{
int a = 0;
scanf("%d", &a); //输入想阶乘的数,别随便加空格和\n
int b = 0; //设定需要的数
int jiec = 1; //需要输出的阶乘结果
for (b = 1; b <= a; b++)
{
jiec = jiec * b;
}
printf("%d\n", jiec);
return 0;
}
现在采用递归的思想。
那么代码就会是这样
int jiec(int b)
{
if (b <= 1)
return 1;
else
return b * jiec(b - 1); //不懂就画图理解
}
int main()
{
int a = 0;
scanf("%d", &a);
printf("%d\n", jiec(a));
return 0;
}
练习2
这就是这个数的内部逻辑。


用递归的思想就会写成这样
int fib(int n)
{
if (n <= 2)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
解决栈溢出的问题

while(n>2)
{
c=a+b;
a=b;
b=c;
n--;
}
总结:
验证一下你学会了多少
A.return这两个数
B.形参用数组
C.形参用两个指针
D.用两个全局变量
答案:a
下一篇:从0开始学c语言-15-一维数组与二维数组的创建、初始化以及在内存中的储存、数组越界和数组作为函数参数_阿秋的阿秋不是阿秋的博客-优快云博客