c语言--函数与递归

时间:2022-12-12 16:26:01

1.函数又叫方法,是指实现某项功能或完成某项任务的代码块

//函数的主体从大括号开始,从大括号结束
//函数组成
//main函数,是给系统调用的函数
//函数组成: 返回值, 函数名, 传入参数
//如: 实现两个整数相加,返回它们的和


void show(void)
{
    printf("hello world!\n");
}

int add(int x, int y)
{
    return x+y;
}

int main(int argc, const char * argv[])
{
    int val;
    show();
    val = add(3, 5);
    printf("%d\n",val);
    return 0;
}

 

//函数组成: 函数名 函数接口 函数体 返回值
//函数调用
//带参数的函数

//函数的返回值就是函数运行的结果
//形式参数也叫参变量,实际上就是变量
//实际参数是实际存在的数值
//形参相当于函数中定义的变量,调用函数传递参数的过程相当于定义形参的变量用实参的值来初始化

//我们把在函数内部定义的变量称为局部变量

void print_time1(int hour, int minute)
{
    printf("%02d:%02d\n", hour, minute);
    printf("%p %p\n", &hour, &minute);
}
void print_time2(int hour, int minute)
{
    print_time1(hour, minute);
    printf("%p %p\n", &hour, &minute);
}
int main(int argc, const char *argv[])
{
    int hour, minute;
    hour = 12;
    minute = 43;
    printf("%p %p\n", &hour, &minute);
    print_time2(hour,minute);
    
    return 0;
}

 

局部变量就是在函数体内部声明的变量,它只在函数体内部有效,也就是说只能在本函数内部使用它,在本函数外是不能使用的,只有在函数被调用的使用,变量才会在内存上分配空间,调用结束,内存释放;

测试局部变量每次分配内存位置不确定

全局变量是在函数体外部定义的变量(包括main函数),它不属于哪个函数,而是属于源程序,因此全局变量可以为程序中得所有函数调用,它的有效范围从定义开始到源程序结束;

全局变量的生命很长,它在程序的整个执行过程中都占用内存,当程序结束时,该变量的"寿命"才结束,系统这才释放它所占用的内存

全局变量的缺点:值容易被修改

作用域,局部变量的作用域是从变量的定义开始到右大括号结束,全局变量的作用域是从变量的定义开始到源程序的结束

 

函数的声明及定义

函数的声明就是告诉编译器知道有什么样的函数存在,可以在后面找到它,假如没有声明,那么就有可能会出现找不到函数的错误

库函数的使用: 条用库函数时 , 需要包含库函数的头文件

 

2.函数的递归调用

 

一个函数在它的函数体内调用它本身,称之为递归函数

C语言中允许递归调用, 递归调用中,主调函数又是被调函数

 

为了防止递归无终止进行,必须在函数体内有终止递归调用的条件

常用的办法就是加条件判断语句,满足某种条件后就不在递归调用,逐层返回

数学上有这样的定义 n的阶乘等于n乘以n-1的阶乘,n-1的阶乘等于(n-1)*(n-2)!

这样下去永远也没有完,所以需要定义一个基础条件

基础条件: 0的阶乘为1

n! = n*(n-1)!

0! = 1

因此 3! = 3*2! = 3*2*1!= 3*2*1*0!= 3*2*1 = 6;

正因为有了基础条件,才没有无休止的进行下去

那么我们用程序怎么样去实现呢

 

一个过程或函数在其定义或说明中又直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。用递归思想写出的程序往往十分简洁易懂。

一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。

注意:

(1) 递归就是在过程或函数里调用自身;

(2) 在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口,否则将无限进行下去(死锁)。

 

递归算法一般用于解决三类问题:

(1)数据的定义是按递归定义的。(Fibonacci函数)

(2)问题解法按递归算法实现。(回溯)

(3)数据的结构形式是按递归定义的。(树的遍历,图的搜索)

 

递归的缺点:

递归算法解题的运行效率较低。在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。

 

int factorial(int n)
{
    if (n==0)
    {
        return 1;
    }
    else
    {
        int recurse = factorial(n-1);
        int result = recurse * n;
        return result;
    }
}
//斐波那契数列
//1 1 2 3 5 8 13 21 34 55 89 144 ....
int fibonacci(int n)
{
    if (n==0||n==1)     {
        return 1;
    }
    else {
        int val = fibonacci(n-1) + fibonacci(n-2);
        return  val;
    }
}

 

eg.编写递归函数求两个正整数a和b的最大公约数(GCD,Greatest Common Divisor),使用Euclid算法!

//如果a除以b能整除,则最大公约数是b。
//
//否则,最大公约数等于b和a%b的最大公约数。
//
//Euclid算法是很容易证明的,请读者自己证明一下为什么这么算就能算出最大公约数。最后,修改你的程序使之适用于所有整数,而不仅仅是正整数。

int euclid(int a, int b)
{
    if(a%b==0)
    {
        return b;
    }
    else
    {
        return euclid(b, a%b);
    }
}

 

eg.趣味问题——年龄。有5个人坐在一起,问第五个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第三个人,又说比第2人大两岁。问第2个人,说比第一个人大两岁。最后问第一个人,他说是10岁。请问第五个人多大?用递归算法实现

int age(int n)
{
    if (n==1) {
        return 10;
    }
    else{
        return age(n-1)+2;
    }
}