传参调用:调用数字
传址调用:改变数字
一、函数的嵌套调用和链式访问
函数不能嵌套定义、可以嵌套调用、链式访问
嵌套调用是指一个函数内可以调用另一个函数
链式访问是指一个函数内可以调用另一个已赋值参数的函数作为参数
#include<stdio.h>
int main()
{
printf("%d", printf("%d",printf("%d",43)));
return 0;
}
结果是4321
printf的返回值是打印字符的个数
二、函数的递归
函数调用自身的技巧叫做递归。
#include<stdio.h>
int main()
{
main();
return 0;
}
大大减少了程序的代码量,思考方式在于:把大事化小
1、练习一
接受一个整型值(无符号),按照顺序打印他的每一位。例如:输入1234,打印1 2 3 4 .
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函数可以打印参数部分数字的每一位
return 0;
}
递归的两个必要条件:
存在限制条件,当满足这个限制条件时,递归便不再继续
每次递归调用之后越来越接近这个限制条件
递归次数太多、太深,容易发生栈溢出。
Stack overflow
内存分为栈区、堆区、静态区
局部变量、函数的形参放到栈区
堆区用于放置动态内存分配malloc/free/calloc/realloc
静态区用于放置全局变量、静态变量
写递归代码的时候:
1、不能死递归,都有跳出条件,每次递归逼近跳出条件
2、递归层次不能太深
练习二、编写函数不允许创建临时变量,求字符串的长度。
int my_strlen(char*str)
{
int count = 0;
while (*str != '\0')
{
count++;
str++;
}
return count;
}
#include<stdio.h>
int main()
{
char arr[] = "hello world";
printf("%d", my_strlen(arr));
return 0;
}
#include<stdio.h>
int my_strlen(char* str)
{
if (*str != '\0')
{
return 1 + my_strlen(str + 1);
}
else return 0;
}
int main()
{
char arr[] = "hello world";
printf("%d", my_strlen(arr));
return 0;
}
三、递归与迭代
1、求n的阶乘
#include<stdio.h>
int factorial(int n)
{
if (n <= 1)
{
return 1;
}
else return n*factorial(n - 1);
}
int main()
{
int n = 0;
scanf("%d", &n);
printf("%d", factorial(n));
}
2、求第n个斐波那契数
1,1,2,3,5,8,13,21,34,55
//效率太低,进行了重复大量的运算
#include<stdio.h>
int Fib(int n)
{
if (n <= 2)
{
return 1;
}
else
return Fib(n-1)+Fib(n-2);
}
int main()
{
int n = 0;
scanf("%d",&n);
printf("%d", Fib(n));
return 0;
}
#include<stdio.h>
int Fib(int n)
{
int a = 1;
int b = 1;
int c = 1;
while (n>=2)
{
c = a + b;
a = b;
b = c;
n--;
}
return c;
}
int main()
{
int n = 0;
scanf("%d", &n);
printf("%d", Fib(n));
return 0;
}
汉诺塔求解、青蛙跳台阶的问题