函数要义回顾
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;
}