什么是递归:程序调用自身的编程技巧称为递归
递归的主要思考方式:可以将一个大型复杂的问题层层转化为为一个与原问题相似的规模较小的问题来求解,即大事化小
下面是一个简单的函数递归:
int main()
{
printf("套娃");
main(); //自己调用自己,但这是个死递归,一般不会这么写
return 0;
}
由此可以总结递归的两个必要条件:
1、存在限制条件,当满足这个限制条件的时候,递归便不再继续
2、每次递归调用后越来越接近这个限制条件
下面通过两个例题来了解函数递归的用法:
(全部用函数递归实现)
题1:输入一个数,按顺序打印它的每一位(无符号)
思路:假设输入1234,逐个打印出1234
1234%10=4,1234/10=123
123%10=3,123/10=12
12%10=2,12/10=1
1%10=1
代码实现:
void print(unsigned int n)
{
if (n > 9)
{
print(n / 10);
}
printf("%u ", n % 10);
}
int main()
{
unsigned int num = 0; //unsigned代表无符号,无符号定义默认为正数
scanf("%u", &num); //%u 代表无符号型
print(num); //调用自定义的print()函数
return 0;
}
运行结果:
题2:模拟一个strlen()函数,不允许创建临时变量,求字符串长度
思路:strlen()函数返回一个遇到'\0'为止的字符串长度。如果字符串的首地址不为'\0',则返回值+1且字符串向右偏移一位继续判断。
代码实现:
int my_strlen(char* str)
{
if (*str != '\0')
return 1 + my_strlen(str + 1);
else
return 0;
}
int main()
{
char arr[] = "abc";
printf("%d", my_strlen(&arr));
return 0;
}
运行结果:
3
代码解析:
1、首先在主函数中调用自定义函数my_strlen(),将arr的地址传给自定义函数my_strlen()
2、在自定义函数my_strlen()中接收到了arr的地址,传给形参str
我们要求长度的字符串是'a','b','c','\0'
3、在自定义函数my_strlen()中,当*str进入if循环时,*str是字符串的首地址,即'a','a'不是'\0',接着执行1 + my_strlen(str + 1)
注意:在1 + my_strlen(str + 1)这个表达式中,str + 1值得是地址向右偏移一位,字符'a'的右一位是'b',
4、所以此时传入第二层my_strlen()函数的首地址是'b',要求长度的字符串是'b','c','\0'
5、以此类推,'b'不等于'\0',执行1 + my_strlen(str + 1),向右偏移一位,字符'b'的右一位是'c',
'c'不等于'\0',执行1 + my_strlen(str + 1),向右偏移一位,字符'c'的右一位是'\0',
6、此时在第三次递归:my_strlen('\0')时,return 0
7、因此回到第一层自定义函数my_strlen()中,返回结果就是1+1+1+0
--------------------------------栈溢出 Stack overflow--------------------------------
内存中有三块区:栈区、堆区、静态区
栈区:存放 局部变量、函数形参(都是临时变量)
堆区:存放 malloc/free、calloc、realloc(都是动态内存分配的)
静态区:存放 全局变量、静态变量
下面是一个栈溢出的函数递归例子:
void test(int n)
{
if (n < 10000)
test(n + 1);
}
int main()
{
test(1);
return 0;
}
每一个函数调用都需要在栈区分配一块空间,分配的空间名称为该函数的栈帧空间。
上述代码中,每次调用test()函数都要分配一块空间,但是递归层次过深,栈区空间耗尽,就会栈溢出。
总结:递归层次不能太深,否则会栈溢出
程序员的知乎网站:https://stackoverflow.com/ 全英文