五、函数
1.函数的概念
C语言中的函数就是一个完成某项特定的任务的一段代码。有些翻译为子程序。C语言代码其实就是由无数个小的函数组合而成的。一个函数如果能完成某项特定任务的话,这个函数是可以复用的,进而提供软件开发效率。
C语言中常见两类函数:(一)库函数(二)自定义函数
2.库函数
(一)标准库和头文件
C语言的国际标准ANSI C规定了一些常用函数的标准,成为标准库,不同的编译器根据标准给出了一系列函数的实现,这些函数就称为库函数。有了库函数,一些功能就不需要程序员自己实现,一定程度上提高了效率。库函数根据功能划分,在不同的头文件中进行了声明。
使用库函数需要包含对应的头文件。库函数种类繁多,不能一口气记住所有,在日常多积累即可。
(二)库函数的使用方法
库函数的学习和查找工具很多,可以在C/C++官网查看学习。建议练习英文阅读。
3.自定义函数
函数的语法形式
语法形式:
ret_type fun_name(形式参数)
{
}
ret_type是函数返回类型;fun_name是函数名;()括起来的是形式参数;{}括起来的是函数体。
ret_type:函数计算结果的类型。有些函数的返回类型可以是void,表示什么都不返回。
fun_name:函数名尽量根据功能起名。
函数参数:有参数要声明参数的类型和名字,以及交代清楚参数个数。参数也可以是void,明确函数没有参数。
函数体:函数实现的具体过程。
4.形参和实参
函数使用过程中,把函数的参数分为实参和形参
(一)实参:函数调用时,传递给函数的参数叫做实际参数,简称实参。
(二)形参:函数定义时,函数名后面()括起来的参数叫形式参数,简称形参。
*定义函数,但没有调用函数时,函数参数只是形式上存在,不会向内存申请空间。形式参数只有在函数调用过程中为了存放实参传过来的值,才会向内存申请空间,这个过程就是形参的实例化。
(三)实参和形参的关系
实参和形参是完全不同的内存空间,形参是实参的一份临时拷贝,形参的修改不会影响实参。
5.return语句
使用注意事项:
(一)return后面可以是数值,也可以是表达式,如果是表达式,则要先计算表达式,再返回表达式的值; (二)return后面可以什么都没有,适用于函数的返回类型是void的情况; (三)return返回值类型和函数的返回类型不一致,系统会自动将返回值隐式转换为函数的返回类型; (四)return语句执行后,函数就彻底返回,不再执行后面的代码; (五)如果函数存在if等分支语句,则要保证每种情况下都有return返回,否则会出现编译错误。
6.数组做函数参数
数组传参注意:
(一)函数的形式参数和实参个数匹配; (二)函数的实参是数组,形参也可以写成数组形式; (三)形参如果是一维数组,数组大小可以省略; (四)形参如果是二维数组,行可以省略,列不可以; (五)数组传参,形参是不会创建新的数组; (六)形参操作的数组和实参的数组是同一个数组; (七)数组传参时传过去数组名即可。
7.嵌套调用和链式访问
(一)嵌套调用:函数之间互相调用
例:计算某年某月的天数。
#include <stdio.h>
#include <stdbool.h>
_Bool is_leap_year(int year)//判断闰年
{
if (((year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0)))
return true;
else
return false;
}
int days_of_mouth(int year, int mouth)//计算天数
{
int day[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
//0是为了让天数和月数一一对应补位用的
int days = day[mouth];
if (is_leap_year(year) && mouth == 2)
days += 1;
return days;
}
int main()
{
int year = 0;
int mouth = 0;
scanf("%d %d", &year, &mouth);
int days = days_of_mouth(year, mouth);
printf("%d", days);
return 0;
}
*函数可以嵌套调用,但是不能嵌套定义。
(二)链式访问:将函数的返回值作为另一个函数的参数
*printf函数返回的是打印字符的个数。
8.作用域和生命周期
(一)作用域:通常来说,一段程序代码中所用到的名字并不总是有效的,而限定这个名字的可用性的代码范围就是这个名字的作用域。 *局部变量的作用域:变量所在的局部范围。 *全局变量的作用域:整个工程。
(二)生命周期:指变量的创建(申请内存)到变量的销毁(收回内存)之间的时间段。 *局部变量的生命周期:进入作用域变量创建,生命周期开始,出作用域生命周期结束。 *全局变量的生命周期:整个程序的生命周期。
9.函数的声明和定义
(一)单个文件
如果将函数的定义放在函数调用之后,就需要在函数调用前进行函数声明。函数声明需要交代清楚:函数名,函数返回类型和函数参数。函数声明中参数只保留类型,省略掉名字也可以。 函数定义是一种特殊的声明,所以函数定义放在函数调用前是可以的。
(二)多个文件
实际写代码的时候,往往会根据程序功能将代码放在多个文件中。一般情况下,函数的声明、类型的声明放在头文件(.h)中,函数的实现放在源文件(.c)中。
(三)static和extern:C语言中的关键字。
static:静态的
static修饰局部变量:static修饰局部变量改变了变量的生命周期,生命周期改变的本质是改变了变量的存储类型,局部变量本来是存储在内存的栈区,但是被static修饰后存储到了静态区。存储在静态区的变量的生命周期和程序的生命周期是一样的,只有程序结束,变量才销毁。但作用域不变。 *当一个变量出了函数,我们想保留值,等下次进入函数继续使用,就可以使用static修饰。
static修饰全局变量:当全局变量被static修饰,这个全局变量只能在本源文件中使用,不能在其他源文件中使用。全局变量具有外部链接属性,但是被static修饰后,外部链接属性变成了内部链接属性,只能在自己文件内部使用,其他源文件即使声明,也无法使用。 *如果一个全局变量,只想在所在源文件中使用,不想被其他文件使用,就可以用static修饰。
static修饰函数:一个函数在整个工程中都可以使用,但是被static修饰后,这个函数只能在本源文件中使用,不能在其他源文件中使用。函数具有外部链接属性,但是被static修饰后,外部链接属性变成了内部链接属性,就只能在自己文件内部使用。 *如果一个函数,只想在所在源文件中使用,不想被其他文件使用,就可以用static修饰。
extern:用来声明外部符号的。


