认识函数
1. 函数的概念
函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数。
函数还有很多叫法,比如方法、子例程或程序,等等。
2. 库函数
 库函数(Library function)是将函数封装入库,供用户使用的一种方式。方法是把一些常用到的函数编完放到一个文件里,供不同的人进行调用。调用的时候把它所在的文件名用#include<>加到里面就可以了。一般是放到lib文件里的。
相关的函数有 pow 、sqrt 、strlen 、time 等。
想了解更多库函数可参考: https://zh.cppreference.com/w/c/header 和 https://legacy.cplusplus.com/reference/clibrary/
3.自定义函数
自定义函数基本格式如下:
re_type name (形式参数)
{
}
re_type 表示的是返回类型,最常见的就有 int (表示返回的)、void (表示什么都不返回)。
name 是函数名(有意义且不与其他函数冲突即可)。
举例:(自定义函数实现两数相乘)
#include<stdio.h>
int multiply(int x,int y) //定义 x , y 用于接收 a b 的值
{
int z = 0;
z = x * y;
return z;
//也可直接写成:
//return x * y;
}
int main()
{
int a,b;
printf("请输入两个值:");
scanf("%d%d",&a,&b);
int answer = multiply(a, b); //调用multiply函数,传入 a b 的值,返回的值放入answer中
printf("相乘结果为:%d",answer);
return 0;
}
4. 形参和实参
自定义函数使用的过程中用到的函数分为形参和实参。
4.1 实参
实际参数简称“实参”。在调用有参函数时,函数名后面括号中的参数称为“实际参数”,实参可以是常量、变量或表达式。
以上方代码为例,
multiply(a, b) 其中的 a b 就是实参。
4.2 形参
自定义函数中的“形参”全称为"形式参数" 由于它不是实际存在变量,所以又称虚拟变量。实参和形参可以重名。
简单来说,形参只是形式上存在的参数,并不是实际存在的。
int multiply(int x,int y) 其中 x y 就是形参
注意:
虽然形参 x y 接收了 a b 的值,但是 x y 和 a b 存放的地址并不同,所以也可理解为形参是实参的一份临时拷贝-。
5. return语句
通常希望通过函数调用使主函数能得到一个确定的值,这就是函数的返回值。
最常见的是 return 0;(停止函数的调用)
- return 后可以为数值,也可以为表达式,若是表达式就会返回表达式计算之后的值。另外, return 后也可以什么都不加,一般适用于函数返回类型是 void 的情况。
- 如果返回的值与函数返回类型不同,系统会自动转换为返回类型。
#include<stdio.h>
int add(float x, float y)
{
float z = 0;
z = x + y;
return z; //float类型转为int类型
}
int main()
{
float a = 5;
float b = 8;
int x = add(a, b);
printf("%d",x);
return 0;
}
- 在介绍循环的时候,提到了 break 和 continue 语句,两者的程度都不如 return,遇到 return 语句函数就会彻底返回,后面的代码不执行。如果非要排个序,那么一定是(return > break > continue) ,return 的程度更深。
6. 嵌套调用和链式访问
6.1 嵌套调用
嵌套调用就是某个函数调用另外一个函数(即函数嵌套允许在一个函数中调用另外一个函数)。
#include<stdio.h>
int multiply(int m, int n)
{
int num = m * n;
return num;
}
int add(int x, int y)
{
int z = 0;
z = x + y;
int other = multiply(x, z);//在 add 函数中又调用 multiply 函数
return other;
}
int main()
{
int a = 5;
int b = 8;
int x = add(a, b);//调用 add 函数
printf("%d",x);
return 0;
}
6.2 链式访问
调用函数时,如果有需要的返回值,一般前面就会创建一个变量来接收返回的值。这样就可以大致理解为链式访问。
#include<stdio.h>
int main()
{
printf("%d", strlen("abcdef"));
//或者分为两条语句
//int len=strlen("abcdef");
//printf("%d",len);
return 0;
}
7. 函数的声明和定义
7.1 单个文件
上文举的例子中,不难发现要调用的函数都是放在主函数之前,那如果放在主函数后会不会出现问题呢?
在弄清楚这个问题之前,我们要了解编译器是如何运行的。C语言的编译器在编译时是从第一行往下进行扫描的,当系统读到调用函数那一行时,系统在之前没有找到,就会报 add 未定义的错误。
#include<stdio.h>
int mian()
{
int a = 5;
int b = 10;
int c=add(a,b);//调用的 add 函数在main()函数之后
return 0;
}
int add(int x,int y)
{
return x + y;
}
既然系统没有找到要调用的函数,我们就在main() 函数之前声明一下,或者直接将要调用的函数放在 main()函数之前。
#include<stdio.h>
int add(int x, int y); //函数的声明
int main()
{
int a = 5;
int b = 10;
int c=add(a,b);
printf("%d",c);
return 0;
}
int add(int x,int y)
{
return x + y;
}
7.2 多个文件
有时要写的代码有些复杂,我们就会分多个文件写代码,让代码逻辑显得更清晰。扫雷游戏就是使用这种方式实现的。
函数的声明、类型的声明放在头⽂件(.h),函数的实现是放在原⽂件(.c)⽂件中。
add.c :
int add(int x,int y) { int z=0; z=x+y; return 0; }
add.h :
int add(int x, int y); //函数的声明
test.c :
#include <stdio.h> #include "add.h" //需要包含头文件 int main() { int a = 10; int b = 20; int c = add(a, b); printf("%d\n", c); return 0; }
7.3 static 和 extern
7.3.1 static修饰局部变量
局部变量生命周期仅仅只在作用域之内,当程序走到作用域之外,这个变量生命周期结束,变量被销毁,内存被回收。
如果局部变量前加上了static,变量就被存储到了静态区,简单来说局部变量的生命周期就和全局变量一样,等到整个程序结束变量才被销毁,内存才被回收。
7.3.2 static修饰全局变量
既然 static 能让局部变大,也就能让全局缩小。
一个全局变量如果被 static 修饰,这个全局变量就只能在本源⽂件内使用,不能在其他源文件内使用,即使声明了,也是⽆法正常使用。
static 修饰函数和 static 修饰全局变量是相同的。
7.3.3 extern
extern 是⽤来声明外部符号的,如果⼀个全局的符号在A文件中定义的,在B文件中想使用,就可以使用extern进行声明,然后使用。
有一种情况例外,当A文件中有 static 修饰全局变量时,即使是 extern 也无法声明使用。