目录
函数实现的原理
代码都要被翻译成机器指令,这些指令在内存中跑起来,每个函数在内存上都有自己的空间,这些指令在内存中都只存了一份,不论被调用多少次,只是将值传入相应空间完成数据处理或者相应指令。
函数名代表函数的入口地址。
函数调用的本质:利用栈的结构——先进后出(FILO),保证了函数可以层层嵌套调用。
计算机在调用函数时,会将当前函数的环境保存起来(保存现场),然后将控制权转交给被调用的函数。被调用的函数执行完毕后,会将控制权交还给保存的环境(还原现场),继续执行后续的指令。
栈
栈:是一种数据结构——表示数据组织形式,特点为先进后出(FILO)。
C语言角度的栈:它的本质还是一块内存空间,只是按照栈这种数据结构来处理和使用
补充:C语言程序把内存划分成5个区域:栈、堆、字符串常量区、静态区(全局区)、代码区。
栈:主要用来存放自动变量或者函数调用的数据。linux系统中栈的大小默认为8Mb,可以修改。
堆:空间大,堆上的空间使用时需要手动申请,手动释放。
字符串常量区:该区域只读,不可修改。
静态区:存放全局变量和静态变量
代码区:只读,不可修改
函数调用的关系:
1.调用者:使用另一个函数的函数。
2.被调用者:被另一个函数使用的函数。
//main是调用者
//main函数是整个程序的入口,只能是调用者
int main()
{
int s[10];
//sizeof在此处是被调用者
printf("%d\n",sizeof(s));
return 0;
}
//函数不支持嵌套定义,但是支持嵌套调用。
特殊的嵌套调用:递归关系——自己调用自己。
1.直接递归:函数直接调用函数本身;
2.间接递归:函数本身不调用自己,但是调用别的函数时,别的函数掉用来自己。
递归和循环类似,是一种特殊的循环
因为栈的大小是有限的,递归在使用时,如果递退下去的时候没有找到停止的条件或者调用自己太多次,栈中存不下多次调用函数的环境参数,栈就会崩溃,使得程序发生段错误。
思想:想要求问题n,需要先求出n-1的结果,直到推到结束条件,然后回归。
实现思路:先考虑递推关系,如何从问题n到问题n-1;然后考虑递推结束的条件;
实列: 求解n的阶乘
先将问题往前看,要算n的阶乘,只需要知道n-1的阶乘然后乘以n,在往前看,现在是要求n-1的阶乘,只需要知道n-2的阶乘再乘以n-1,……最后求到0的阶乘,就是1,就可以往后走了
#include <stdio.h>
int factorial(int n)
{
int ret;
//0的阶乘为1,求解到此时,就可以回归了,往上依次求出1、2、3、……、n的阶乘
if (n == 0)
{
ret = 1;
}
else
{
//要求n的阶乘,可以递推成用n-1的阶乘乘以当前的n
ret = factorial(n-1) * n;
}
return ret;
}
int main()
{
int n,f;
printf("input a num:");
scanf("%d",&n);
f = factorial(n);
printf("%d! = %d\n",n,f);
return 0;
}
数组作为函数的参数
#include <stdio.h>
//打印整个数组的值
void printArray(int a[],int len)
{
int i;
for (i = 0;i < len;i++)
{
printf("%d\n",a[i]);
}
return;
}
//给数组循环赋值
void assignment(int a[],int len)
{
int i;
for (i = 0;i < len;i++)
{
scanf("%d",a[i]);
}
}
int main()
{
int n;
printf("input a num:");
scanf("%d",&n);
int a[n];
assignment(a,n);
printArray(a,n);
return 0;
}
1.一维数组本身做函数参数:形参写成数组形式,形参还需要数组长度,实参写数组名和数组长度,实参传入的是数组的首地址,无法在函数中算出该数组的长度。
2.一维数组元素作为函数的参数:数组元素作为函数的参数时,相当于同一类型的变量作为参数。