1.什么是函数递归?
编程语言中,函数直接或间接调用函数本身,则该函数称为递归函数。递归函数不能定义为内联函数。
在数学上,关于递归函数的定义如下:对于某一函数f(x),其定义域是集合A,那么若对于A集合中的某一个值X0,其函数值f(x0)由f(f(x0))决定,那么就称f(x)为递归函数。
递归的主要思考方式在于:把大事化小
2.递归的两个必要条件
一个含直接或间接调用本函数语句的函数被称之为递归函数,在上面的例子中能够看出,它必须满足以下两个条件:
1) 在每一次调用自己时,必须是(在某种意义上)更接近于解;
2) 必须有一个终止处理或计算的准则。
3.递归函数的实际应用
#define _CRT_SECURE_NO_WARNINGS
//函数的递归
//例:接受一个整型值(无符号),按照顺序打印他的每一位
//例如:输入1234 输出 1 2 3 4
//unsigned int num; 无符号整型
// %d 打印有符号的整数(有正负数)
// %u 打印无符号数,无负数。
void print(unsigned int n)
{
if (n > 9)
{
print(n/10);
}
printf("%d ", n % 10);
}
#include <stdio.h>
int main()
{
unsigned int num = 0;
scanf("%u", &num);
print(num);
//print(123) 4
//print(12) 3 4
//print (1) 2 3 4
return 0;
}
4.模拟实现strlen函数
//求字符串长度,模拟实现strlen
int My_strlen(char* str)
{
int count = 0;
while (*str !='\0')
{
count++;
str++;
}
return count;
}
#include <stdio.h>
int main()
{
char arr[] = "abc";
int ret = My_strlen(arr);
printf("%d", ret);
return 0;
}
用递归的方式实现
//函数递归求解
//1+My_strlen("bc")
//1+1+My_strlen('c')
//1+1+1+My_strlen('\0')
//执行方案
int my_strlen(char* str)
{
if (*str != '\0')
return 1 + my_strlen(str + 1);
else
return 0;
}
#include <stdio.h>
int main()
{
char arr[] = "abc";
int len = my_strlen(arr);
printf("%d", len);
return 0;
}
5.如果函数调用不设限制条件,会导致栈溢出,程序就会报错
栈溢出是指在编程中,当向栈中写入的数据超出了栈的容量限制时,会发生数据溢出。栈是一种具有“后进先出”特性的线性数据结构,其操作主要在栈顶进行。在C语言中,常见的操作包括PUSH(入栈)和POP(出栈),分别用于在栈顶加入元素和移去栈顶元素。当栈空间被完全占满时,如果继续向栈中写入数据,就会发生栈溢出。12
栈溢出的原因可能包括:
- 内存泄漏:如果创建的对象数量超过了堆的最大容量,可能会导致内存泄漏,进而影响栈的容量。3
- 内存溢出:如果程序中创建的对象生命周期过长或存储设计不合理,可能会导致内存溢出。
为了预防栈溢出,开发者需要在编程时注意内存使用,避免定义过大的数组或复杂的函数,如多个形参等。2
栈溢出的利用方式包括:
- 通过编写特定的溢出数据,覆盖栈中其他数据,从而影响程序的运行流程。4
- 利用C语言中的危险函数(如gets())进行溢出攻击,这些函数可能不进行边界检查,导致输入的数据超过预期长度,从而发生溢出。
在处理栈溢出时,可以通过性能监测工具获取堆内存快照,查看导致溢出的对象是否必要,如果是内存泄漏,则根据引用链找到具体的泄漏位置进行修改;如果是内存溢出,则检查JVM的堆参数设置,看是否还有向上调整的空间,或者从代码上检查是否有些对象的生命周期过长或存储设计不合理等问题。