一、自定义函数

1.自定义函数跟库函数一样,有函数名、返回类型和函数参数。但是不一样的是这些需要自己来设计。

 函数的定义放在.c文件中,交代了函数的具体实现

2.函数的组成:函数名 函数参数 函数返回值类型 函数体

返回类型 函数名(参数类型 形参名)
{
     函数体
     (返回值)      
}

参数:真实传给函数的参数,叫作实参;实参可以是常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,他们都必须是确定的值,以便把这些值传送给形参;
形参:指函数名括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元)所以叫做形式参数。形式参数当函数调用完成之后就自动销毁了,因此形式参数只在函数中有效。

 

二、函数声明

1.函数申明的格式

  • 函数声明的格式:函数返回类型 +函数名 +参数+ ;

   eg:int add(int x, int y);

  • 函数的声明一般放在函数定义的前面;
  • 函数的声明一般放在头文件中;

 2.函数申明的目的

为了提前告诉编译器有这个函数的存在,避免在main函数之后 定义函数的时候,这种时候如果不提前声明函数,那么就会报错,因为这时候main函数中使用了我们定义的函数名,可是在mian函数前我们并没有提到这个函数名,就会导致出错;所以!这个时候,函数的声明就十分有必要了。

例1: 函数的定义和声明

src/add.c file

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

inc/add.h file

int Add(int x, int y);

test.c file

#include <stdio.h>
#include "add.h"   
//自己写的库,引入用双引号,C的标准库函数引入用<>void main()
{int a = 9;
    int b = 10;
    printf("a+b=%d\n", Add(a, b));


}

使用说明:

函数声明,写在头文件中,给别的.c文件调用时,只需要引入该声明的头文件即可
如上,Add()函数的实现(定义)在add.c文件中,声明在inc/add.h文件中, 链式访问.c中引用该头文件
编译:
gcc add.c test.c -I ../inc

运行:
./a.out

 

三、函数的嵌套调用

// main() call func_2()
// func_2 cal func_1()

#include "stdio.h"


int func_1()
{
    printf("This is function 1\n");
    return 0;
}

void func_2()
{
    int i = 0;
    for(i=0;i<3;i++)
    {
        func_1();
    }
}

void main()
{
    func_2();
}

 

四、 函数的链式访问

一个函数的返回值是另一个函数的参数

例2:func()跟func_insteresting()函数的返回值是另一个函数printf的参数

#include <stdio.h>

int func()
{
    return -1;
}

void func_interesting()
{
    printf("%d\n", printf("%d", printf("%d", 666))); // result: 66631, printf的返回值是当前打印的字符的个数
}


void main()
{
    printf("The return value of func is %d\n", func());
func_interesting();
}

 

五、函数递归

5.1 简单理解递归与循环:

  • 递归:你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门,你继续打开它。若干次之后,你打开面前的门后,发现只有一间屋子,没有门了。然后,你开始原路返回,每走回一间屋子,你数一次,走到入口的时候,你可以回答出你到底用这把钥匙打开了几扇门。
  • 循环:你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门(若前面两扇门都一样,那么这扇门和前两扇门也一样;如果第二扇门比第一扇门小,那么这扇门也比第二扇门小,你继续打开这扇门,一直这样继续下去直到打开所有的门。但是,入口处的人始终等不到你回去告诉他答案

 

5.2 递归的内涵

1、定义 (什么是递归?)

   在数学与计算机科学中,递归(Recursion)是指在函数的定义中使用函数自身的方法。实际上,递归,顾名思义,其包含了两个意思:递 和 归,这正是递归思想的精华所在。

2、递归思想的内涵(递归的精髓是什么?)

   正如上面所描述的场景,递归就是有去(递去)有回(归来),如下图所示。“有去”是指:递归问题必须可以分解为若干个规模较小,与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决,就像上面例子中的钥匙可以打开后面所有门上的锁一样;“有回”是指 : 这些问题的,演化过程是一个从大到小,由近及远的过程,并且会有一个明确的终点(临界点),一旦到达了这个临界点,就不用再往更小、更远的地方走下去。最后,从这个临界点开始,原路返回到原点,原问题解决。

 

5.3 递归的两个必要条件:

  • 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
  • 每次递归调用之后越来越接近这个限制条件。 

 

5.4 递归举例

1.接受一个整型值(无符号),按照顺序打印它的每一位。 例如: 输入:1234,输出 1 2 3 4

2..编写函数,不允许创建临时变量,求字符串的长度。

/*
1.接受一个整型值(无符号),按照顺序打印它的每一位。 例如: 输入:1234,输出 1 2 3 4

2..编写函数,不允许创建临时变量,求字符串的长度。
*/

#include <stdio.h>

void my_print(unsigned int x)
{
    if(x>9)
    {
        my_print(x/10);
    }
    printf("%d ", x%10);
}

int my_strlen(char * arr)
{
    if('\0' == *arr)
    {
        return 0;
    }
    else
    {
        return 1 + my_strlen(arr+1);
    }
}


void main()
{
    // 1.
    unsigned int num = 0;
    scanf("%u", &num);
    printf("num is %u\n", num);
    my_print(num);


    // 2.
    char arr[] = "hello world";
    int len = my_strlen(arr);
    printf("\n\nThe length is %d\n", len);



}

 

3.求n的阶乘

//求n的阶乘

#include <stdio.h>

int factorial(int n)
{
    if(1==n)
    {
        return 1;
    }
    else
    {
        return n*factorial(n-1);
    }  
}

void main()
{
    int num = 0;
    scanf("%d", &num);
    printf("The num is %d\n", num);
    int ret_val = factorial(num);

    printf("The factorial of num %d is %d\n", num, ret_val);

}

 

4.求第n个斐波那契数

//求第n个斐波那契数.c
// 1 1 2 3 5 8 13 21 34 55

#include <stdio.h>

//n<=2   1
//n>2    f(n-1)+f(n-2)
int Fibo1(int n)
{
    if(n<=2)
    {
        return 1;
    }
    else
    {
        return Fibo1(n-1)+Fibo1(n-2); //递归的方式,求大的斐波那契数时,效率比较低
    }
}


int Fibo2(int n)  //效率更高
{
    int a = 1;
    int b = 1;
    int target = 1;
    while(n>2)
    {
        target=a+b;
        a = b;
        b = target;
        n--;
    }
    return target;
}


void main()
{
    int n = 0;
    scanf("%d", &n);
    int ret_val = Fibo2(n);
    printf("The %d FIBO is %d\n", n, ret_val);
}