一、自定义函数
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); }