函数基础知识
一、C语言中函数的分类
- 库函数
库函数:由系统建立的具有一定功能的函数的集合。程序员引用头文件后可以直接调用系统函数库中的函数进行使用。
库函数的分类:
- IO函数(输入输出函数)
- 字符串操作函数
- 内存操作函数
- 数学函数
- 时间/日期函数
- 其他库函数
库函数有许多,想要全部记住是十分困难的,实际上也不需要完全记住,重点是要学会如何查询与快速掌握所需库函数的用法。
下面这个网站可以快速查询所需库函数的功能和用法:
—库函数查询—
- 自定义函数
自定义函数:程序员自己创建的函数。函数的强大之处就在于程序员可以将自己创造的一个又一个相对独立的单功能代码块封装为函数,调用后便可重复使用。
函数的组成:
ret_type fun_nume(para1,……)
{
statement; //语句项
}
ret_type 返回类型
fun_name 函数名
para1 函数参数
…… 参数可以多个
二、函数的声明与定义
- 函数的声明
1、函数的声明要在函数的定义和调用之前(若将函数的定义放在main函数前,就是同时声明和定义了),要满足先声明后使用。
2、函数的声明要包含返回类型,函数名,参数类型。
3、函数的声明一般放在头(.h)文件中。
//函数的声明
int Add(int, int);
- 函数的定义
函数的定义是指函数的具体实现,交代函数的功能实现。函数定义放在源(.c)文件中。
//函数的定义
int Add(int x, int y)
{
return x + y;
}
三、函数的参数
- 实际参数(实参)
真实传递给函数的参数,叫实参。
实参可以为:常量,变量,表达式,函数等(实质是表达式的结果、函数的返回值 作为实参)。
- 形式参数(形参)
形参是指函数名后括号中的变量,因为形参只有在函数的调用过程中才实例化(分配内存空间),所以叫形式参数。
形参在函数调用完成后就会自动销毁,所以形参只在函数内部有效。
四、函数的调用
- 传值调用
将实参的值传递给函数中的形参。
传值调用时,形参其实是实参的一份临时拷贝,在函数内部修改形参是不会改变实参的。
- 传址调用
传址调用是把函数外部创建变量的内存地址传递给函数参数。
通过传址调用,函数的参数可以直接操作函数外部的变量,对其进行修改。
五、函数的嵌套调用和链式访问
- 嵌套调用
在函数内部调用其他函数就是函数的嵌套调用。
#include <stdio.h>
void new_line()
{
printf("hehe\n");
}
void three_line()
{
int i = 0;
for(i=0; i<3; i++)
{
new_line(); //也是嵌套调用
}
}
int main()
{
three_line(); //嵌套调用
return 0;
}
函数可以嵌套调用,但是不能嵌套定义(即:不能在函数内部定义函数)。
- 链式访问
链式访问:把一个函数的返回值作为另外一个函数的参数。
#include <stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
//这就属于链式访问
//结果是 4321,:printf函数的返回值是打印在屏幕上字符的个数
return 0;
}
六、函数递归
在函数的内部调用函数自身称之为递归。
递归策略通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,然后只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
递归的主要思考方式是:把大事化小。
实现递归的两个必备条件:
1. 递归存在限制条件,当满足限制条件时,递归便不再继续。
2. 每次递推都更加接近限制条件,最后会达到限制条件,然后开始回归。
//例:求斐波那契数列(不考虑溢出)
int fib(int n)
{
if (n <= 2)
return 1;
else
return fib(n - 1) + fib(n - 2); //这便是递归
}
上面的代码其实存在问题:
当 n 的数值较大时,程序会运行很久才得到答案甚至崩溃,Why?
因为 n 较大时fib函数内部要重复的次数很多,而系统分配给程序的栈空间是有限的,如果出现递归重复的次数太多,有可能导致程序一直开辟栈空间,最终产生栈空间耗尽的情况,这时便发生了栈溢出。
解决办法:用迭代(循环,非递归)的方式代替递归。
不过,在使用递归没出现错误的情况下(如栈溢出、效率过低等)一般建议使用递归,而不是使用迭代(非递归)的方式,因为递归可以比其他方式简洁很多。