目录
- 函数的概念
- 库函数
- 自定义函数
- 形参和实参
- return语句
- 数组做函数参数
- 嵌套调用和链式访问
- 函数的声明和定义
- 编译静态库调用
1. 函数的概念
函数 (funtion)也叫子程序,函数是完成某项特定任务的一段代码,c语言的程序由很多函数组成,一个大的任务可以拆分成许多函数,可以复用,提高了精简性和复用性
- 库函数
- 自定义函数
2. 库函数
2.1 标准库和头文件
c语言不提供库函数,规定了一些常用函数的标准。编译器根据标准实现,就是库函数
库函数可以直接使用,提升了效率。库函数的功能,在头文件中声明,可以在下网址查看:
https://zh.cppreference.com/w/cpp/standard_library
2.2 库函数的使用方法
库函数使用必须先包含对应的头文件
在上面网址查看库函数的说明:
以上内容包含:函数原型、功能说明、参数、返回值、注意事项、示例、输出结果、其他参阅
3. 自定义函数
自定义函数更加重要
3.2 语法形式
ret_type fun_name()
{
函数实现
}
ret_type
表示函数计算结果的类型,可以是void,什么都不返回fun_name
方便使用函数,要根据函数的功能取名,有意义- 函数的参数相当于工厂加工的原材料,可以是void,表示明确没有参数,如果什么都不写,调用的函数的时候可以随意传入,但不会产生数据
- { } 括起来的是函数体,函数的实现内容
3.3 函数的举例
完成两个变量加法的函数
int Add(int x, int y)
{
int z = 0;
z = x+y;
return z;
}
int main()
{
int a = 0;
int b = 0;
//输⼊
scanf("%d %d", &a, &b);
//调⽤加法函数,完成a和b的相加
//求和的结果放在r中
int r = Add(a, b);
//输出
printf("%d\n", r);
return 0;
}
4. 形参和实参
上述代码在调用的时候传入的a和b就是实际参数,是真正参与计算的值
函数定义的时候的x和y是形式参数,因为不调用的话不会申请空间,不是真实存在的,调用的时候传入值,称为形式的实例化
实参和形参的关系
实参传递给形参,但这两个是独立的空间,地址是不一样的
5. return语句
注意事项:
- 后面可以是数值也可以是表达式,如果是表达式先计算再返回
- 可以什么都没有,直接return,这种是返回值类型为void
- return返回的值和返回类型不一致,会隐式转换为返回类型
- return执行后彻底返回,后面的代码不再执行
- 如果有if语句,要保证每个条件都返回,否则会编译错误
- 如果返回类型为void,可以返回值,但不能接收。如果返回为int,只写return,默认返回
6. 数组做函数参数
在自定义函数时,难免需要传入数组做参数,对数组进行操作。如果要传递数组,必须同时传递数组的元素个数,才能对数组遍历
- 函数的形参和实参个数匹配
- 实参和形参都可以写成数组形式
- 形参如果是一维数组,数组大小可以省略不写
- 形参如果是二维数组,行可以省略,列不可以
- 数组传参,形参不会创建新的数组
- 形参和实参操作的是统一数组
7.嵌套调用和链式访问
7.1 嵌套调用
嵌套调用就是函数之间的互相调用
int is_leap_year(int y)
{
if(((y%4==0)&&(y%100!=0))||(y%400==0))
return 1;
else
return 0;
}
int get_days_of_month(int y, int m)
{
int days[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int day = days[m];
if (is_leap_year(y) && m == 2)
day += 1;
return day;
}
int main()
{
int y = 0;
int m = 0;
scanf("%d %d", &y, &m);
int d = get_days_of_month(y, m);
printf("%d\n", d);
return 0;
}
main函数调用了scanf、printf、get_days_of_month
get_days_of_month调用了is_leap_year
7.2 链式访问
将函数的返回值作为参数
#include <stdio.h>
int main()
{
printf("%d\n", strlen("abcdef"));//链式访问
return 0;
}
strlen的返回值作为printf的参数
printf("%d", printf("%d", printf("%d", 43)));
这个代码会打印多少呢?
首先了解一下printf的返回值,返回打印在屏幕上的字符个数
最里层的pirntf先打印43,然后返回成功打印2个字符,然后打印这个返回值,继续返回打印成功1个,最后打印1。所以最终结果: 4321
8. 函数的声明和定义
函数的定义,如果函数在调用时后面定义就会报错,这时需要在调用前声明函数,函数声明需要写出函数的返回值类型、函数名、参数,然后加个分号就行了
//函数声明
void ptt(int arr[]);
int main()
{
int n = 4;
int ary[3] = { 1,2 };
ptt(ary);
printf("%d", n);
return 0;
}
void ptt(int arr[]) {
return 5;
}
一般将函数定义在调用前,可以不用声明
8.2 多个文件
代码较多时,函数的声明、类型放在头文件(.h)中,实现放在源文件(.c)中,
函数声明在头文件,实现在对应的源文件,主函数使用的时候直接调用,实现和使用都需要包含对应头文件
8.3 static和extern
都是c语言的关键字,static是静态的意思
- 修饰局部变量
- 修饰全局变量
- 修饰函数
extern是声明外部符号的
在讲解这两个符号之前,先说一下作用域和生命周期
作用域(scope): 程序设计概念,一段代码用的名字并不总是有效的,限定这个名字可用的范围就是作用域
- 局部变量的作用域是变量所在的局部范围
- 全局变量的作用域是整个工程(项目)
生命周期: 变量的创建(申请内存)到变量的销毁(收回内存)之间的一个时间段
- 局部变量的生命周期: 进入作用域创建变量,生命周期开始,出作用域生命周期结束
- 全局变量的生命周期: 整个程序的生命周期
8.4 static修饰的局部变量
对比两个代码,左边的打印的都是2,右边的是2,3,4,5
代码1: 变量i在每次进入test函数后会重新创建,并赋值为0,然后++再打印,存放在栈区,是临时的,出函数后生命周期就结束(释放内存)
代码2: i值会累加,所以i值创建好后出函数不会销毁,static修饰后存放在静态区
static修饰局部变量
本质上影响了变量的存储类型,存到了静态区,生命周期变长了,作用域不变
8.5 static修饰全局变量
全局变量具有外部链接属性,想在另一个c文件使用需要extern来声明外部符号。但用了static修饰后,变成了内部连接属性,只能在本文件使用
如果一个变量只想在本文件内部使用,不想被其他文件用,可以用static修饰
8.6 extern声明函数
如果一个另一个文件的函数想在main文件或其他文件使用,可以直接调用,因为函数默认具有外部链接属性,但会报警告错误,因为这个文件并不认识另一个文件的函数,所以完整的写法,应该用exterm来声明外部的函数。如果用static修饰函数,这个函数也成了内部连接属性,一般用来只在本文件使用的函数这样定义,不需要被其他文件使用,方便函数隐藏
9.编译静态库调用
项目属性→配置类型→改为lib静态库
该文件内容查看为乱码,为了让别人能看懂,将。h声明文件也一起放到需要的工程目录下,加上导入静态库的代码