递归函数总结(5)

本文深入探讨了函数调用机制,包括代码区、数据区、堆区和栈区的内存分配。详细阐述了局部变量、全局变量、静态变量的生命周期和存储类型。递归函数的原理也被解析,强调了栈空间的连续性、分治策略和递归终止条件。通过实例展示了阶乘计算、反向输出数列、最大公因数查找和斐波那契数列的递归实现。
摘要由CSDN通过智能技术生成

函数要义回顾

1.根据不同的操作系统,一个进程可能被分配到不同的内存区域去执行。但是不管什么样的操作系统、什么样的计算机架构,进程使用的内存都可以按照功能大致分为以下4个部分:
1)代码区: 这个区域存储着被装入执行的二进制机器代码,处理器(CUP)会到这个区域取指并执行。
2)数据区: 用于存储全局变量,静态全局变量,静态局部变量,字符串常量等。
3)堆区: 进程可以在堆区动态地请求一定大小的内存, 并在用完之后归还给堆区。动态分配和回收是堆区的特点。4)栈区: 函数被调时分配栈区,用于存放函数的参数值,局部变量等值;还要动态地存储函数之间的关系,以保证被调用函数在返回时恢复到被调用函数中继续执行。

2.函数调用机制
局部变量占用的内存是在程序执行过程中”动态”地建立和释放的。这种"动态”是通过栈由系统自动管理进行的。当任何- -个函数调用发生时,系统都要作以下工作:
1)建立栈帧空间;
2)保护现场: 主调函数运行状态和返回地址入栈;
3)为被调函数传递数据 (进行实参和形参的结合),同时形参获得存储空间;接着给局部变量分配空间;
4)执行被调函数函数体;
5)当被调函数执行完成, 释放被调函数中局部变量占用的栈空间;调用完成即释放
6)恢复现场: 取主调函数运行状态及返回地址,释放栈帧空间;
7)继续主调函数后续语句。

3.生存期
1)存储类型决定了变量的生命期,变量生命期指从获得空间到空间释放之间的时期(程序的执行过程)。
存储类型的说明符有四个:auto, register, static和extern。前两者称为自动类型,后两者分别为静态和外部类型。本节重点掌握static和extern这两种类型的使用和区别。具体说,区分局部变量和静态局部变量,全局变量和静态全局变量。
auto:
前面提到的局部变量都是自动类型。其空间分配于块始,空间释放于块终,且由系统自动进行。自动变量保存在栈中,且是在程序运行过程中获得和释放空间,未初始化时值为随机数现在定义局部变量不在使用auto修饰。
register:
为提高程序运行效率,可以将某些变量保存在寄存器中,即说明为寄存器变量,但不提倡使用。
static:
静态变量。根据被修饰变量的位置不同,分为局部(内部)静态变量和全局(外部)静态变量。所有静态变量均存放在数据区,编译时获得存储空间,未初始化时自动全0,且只初始化一次。
extern(待探究??????????)
意为“外来的,它的作用在于告诉编译器:有这个变量,它可能不存在当前的文件中,但它肯定要存在于工程中的某一个源文件中。
2)在函数内部或块中定义的标识符具有局部生命期,其生命期开始于执行到该函数或块的标识符声明处,结束于该函数或块的结束处。具有局部生命期的标识符存放在栈区。具有局部生命期的标识符如果未被初始化,其内容是随机的,不可用。具有局部生命期的标识符必定具有局部作用域;但反之不然,
3)静态生命期指的是标识符从程序开始运行时存在,即具有存储空间,到程序运行结束时消亡,即释放存储空间。具有静态生命期的标识符存放在静态数据区,属于静态存储类型,如全局变量、静态全局变量、静态局部变量。具有静态生命期的标识符在未被用户初始化的情况下,系统会自动将其初始化为0。
(其中局部静态变量的作用域为块域,但生命期为整个文件。即当块结束时,局部静态变量的空间任然存在,直到整个文件结束时该局部静态变量空间才会被释放,生命期结束)

4)局部变量用static修饰,会被存放在程序的.data存储器中,这样可以在下一次调用的时候还可以保持原来的赋值

函数驻留在代码区,也具有静态生命期。所有具有文件作用域的标识符都具有静态生命期。

递归函数要义

栈空间必须连续
分治策略,问题不变,规模变小
直接递归:函数调用本身
函数只要被调用,就会分配栈帧,调用几次就分配几次
不能无限递归,CPU空间有限

递归函数的执行分为"递推”和“回归” 两个过程,这两个过程由递归终止条件控制,即逐层递推,直至递归终止条件满足,终止递归,然后逐层回归。
递归调用同普通的函数调用一样,每当调用发生时,就要分配新的栈帧(形参数据,现场保护,局部变量) ;而与普通的函数调用不同的是,由于递推的过程是一个逐层调用的过程, 因此存在一个逐层连续的分配栈帧过程, 直至遇到递归终止条件时,才开始回归,这时才逐层释放栈帧空间,返回到上一层, 直至最后返回到主调函数。

示例:
1.求n的阶乘

in fac(int n)
{
  if(n<=1)
     return 1;
  else
     return fac(n-1)*n;
}

图解递归过程:
在这里插入图片描述
图解栈帧分配:
在这里插入图片描述

附:

1.反向输出数列

void Printf(int x)
{
	if (x!=0)
	{
		printf("%d", x%10);
		Printf(x / 10);
	}	
}
void Printf(unsigned int x)
{
	while (x != 0)
	{
		printf("%d", x % 10);
		x = x % 10;
	}
}
int main()
{
	unsigned int x;
	scanf_s("%d", &x);
	Printf(x);
	return 0;
}

2.找两个数的最大公因数

int fun(int a, int b)
{
	int c = 0;
	if (0 == b)
		return a;
	else
	return fun(b, a % b);//return fun(a, b);
}

int fun(int a, int b)
{
	int c = 0;
	while (b != 0)
	{
		c = a % b;
		a = b;
		b = c;
	}
	return a;
}
 
int main()
{
	int a=0, b=0;
	int c = 0;
	scanf_s("%d %d", &a, &b);
	c=fun(a, b);
	printf("%d", c);
	return 0;
}

3.反向输出数组元素

void Print_Ar(int* br, int n)
{
	assert(br != NULL && n > 0);
	while(n>0)
	{
		printf("%d\n", br[n - 1]);
		n--;
	}
}

int main()
{
	int ar[] = { 12,23,34 };
	int n = sizeof(ar) / sizeof(ar[0]);
	Print_Ar(ar, n);
	return 0;
}

4.斐波那契数的输出

int fac(int n)
{
	if (n < 3)
	{
		return 1;
	}
	else //空间复杂度S(n)时间复杂度(2^n)
	{
		return fac(n - 1) + fac(n - 2);
	}
}
int main()
{
	int n;
	int sum = 0;
	scanf_s("%d", &n);
	sum = fac(n);
	printf("%d", sum);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值