目录
一、函数调用的关系
调用者
被调用者
int main(void)
{
printf("%d\n",getMonthDays());
return 0;
}
这个里面:
main ---成为调用者 ---main函数是整个程序的入口,只能调用者
getMonthDays --- 在此处是 被调用者
getMonthDays()
{
isLeapYear();
} //函数的嵌套调用
main -->getMonthDays --> isLeapYear
注:
函数不支持 嵌套定义,但是可以嵌套调用
函数名 --- 函数的入口地址
二、函数调用的本质
实际是利用的栈的结构 ---先进后出 --保证了函数可以层层嵌套调用
栈:
数据结构 --- (表示数组组织形式)
特点:
先进后出 (First In Last Out) //FILO
C语言角度的栈:
1.本质上是一块内存空间
2.只是按照 栈 这种数据结构 来处理和使用的
栈:存放局部变量 //空间 自动申请 自动释放
C语言程序:
把内存划分了5个区域
栈 //主要 用来存放, 自动变量 或 函数调用的数据
堆 //空间大 堆上的空间 ,手动申请,手动释放
字符串常量区 // 如:"hello" (只读)
静态区(全局区) // 全局变量 和 静态变量
代码区 // 只读的
程序 = 代码 + 数据
三、递归
特殊嵌套调用 --- 递归
递归:可以认为是 自己调用自己(类似循环 --- 递归是一种特殊的循环 )
直接递归
间接递归
eg:1到100的累加求和
sum(100)
|--sum(99) + 100
|--sum(98)+99
|--sum(97)+98
...
|--sum(3)+4
|--sum(2)+3
|--sum(1) + 2
1
递归思路:
要求问题n
依赖于问题n-1的解决
递归代码实现思路:
1.递推关系
怎么从 问题 n 到 问题n-1
sum(100) => sum(99)+100
sum(99) => sum(98)+99
sum(n) = sum(n-1)+n;
2.递推结束条件
n = 1
递归是两个过程,先逐次向下推,直到结束条件,然后回归。
这个过程就需要用到栈从而保存现场和恢复现场,但是栈的大小是有限的,在64位平台上,一般默认为8M。所以当数字很大时,会出现栈崩溃。
#include <stdio.h>
int sum(int n)
{
if(n==1)
{
return 1;
}
else
{
return sum(n-1)+n;
}
}
int main(void)
{
int a=0;
scanf("%d",&a);
int ret = sum (a);
printf("%d\n",ret);
return 0;
}
斐波拉契数列也可以通过递归实现
1 1 2 3 5 8
求斐波拉契数列第n项
fibo(5)
|--fibo(4)+fibo(3)
| |-- fibo(2) + fibo(1)
|--fibo(3) + fibo(2)
|-- fibo(2) + fibo(1)
1 1
fibo(n)
|--fibo(n-1) + fibo(n-2)
#include <stdio.h>
int fibon(int n)
{
if(n==1)
{
return 1;
}
else if(n==2)
{
return 1;
}
else
{
return fibon(n-1)+fibon(n-2);
}
}
int main(void)
{
int a=0;
scanf("%d",&a);
int ret = fibon(a);
printf("%d\n",ret);
return 0;
}
汉诺塔(通过三个柱子A、B、C,每次只能搬一个盘子,盘子必须从大的在下,小的在上,将所有盘子从A移到C)是一个古老的数学问题,也可以通过递归实现
#include <stdio.h>
void move(int pole1,int pole2)
{
printf("%c--->%c \n",pole1,pole2);
}
//起始 辅助 目标
void hanoi(int n,int A,int B,int C)
{
if(n==1)
{
move(A,C);
}
else
{
hanoi(n-1,A,C,B);
move(A,C);
hanoi(n-1,B,A,C);
}
}
int main(void)
{
int a=0;
scanf("%d",&a);
hanoi(a,'A','B','C');
return 0;
}
只有一个盘子的时候,直接将盘子从A移到C
n个盘子时,考虑现将n-1个盘子移到B,再将最后一个大盘子从A移到C。这时n-1个盘子在B,移到C去,完成移动。对于n-1个盘子而言,一开始的起始位置是A,辅助位置是B,目标位置是C。移动到B之后等最后一个盘子移动完成,要移到C,才算结束。此时n-1个盘子的起始位置是B,要移到C,所以目标位置是C,辅助位置是A。
四、数组作为函数参数
1.数组元素作为函数参数
int add(int a,int b)
{
return a + b;
}
int a[10] = {1,2,3};
add(a[0],a[1]); //这种就是将数组元素作为函数参数
2.数组本身作为函数参数
#include <stdio.h>
void printArray(int a[],int len)
{
int i=0;
for(i=0;i<len;++i)
{
printf("a[%d] = %d\n",i,a[i]);
}
}
int main(void)
{
int a[]={1,2,3,4,5,6,7,8,9};
int len=sizeof(a)/sizeof(a[0]);
printArray(a,len);
return 0;
}
注意在函数中,int a[ ]表明是个数组,实际上编译器是将其当成int * a理解的,所以不能在函数里计算数组的长度,函数里的sizeof(a)认为是这个数组的地址,而不是这个数组整个的大小
所以实际上形参写int a[10]也没关系,中间的长度并不重要,编译器都当成int *处理,但在函数里照样当数组处理。
总结:
1. 一维整型数组 做函数参数
形参 --写成数组形式 还需要数组长度
实参 --数组名,数组长度
eg:
printArrray(int a[],int len) //形参
//printArrray (int *a,int len) ---编译器最终理解的形式
printArray(a,len); //调用
int a[10];
数组名 代表类型 ---int[10] 这种数组类型
数组名 代表的值 ---首元素的地址 //(数组所占内存空间的首地址)