目录
1.概念
函数在c语言中是一个完成某项特定任务的一小段代码,也叫子程序。函数需要特定写法和调用方法。
c语言程序由无数个小的函数组成。一个任务包含了若干个函数。
函数一般有2类:库函数、自定义函数。
2.库函数
2.1标准库和头文件
c语言不提供函数,只是规定了语法规则。c语言的国际标准ANSI C有一些常用函数的标准,叫标准库。不同编译器厂商根据ANSI提供的标准给出了一系列的新函数,称为库函数。
库函数相关头文件:C 标准库头文件 - cppreference.com
2.2使用
库函数需要提前在标准库中的头文件中声明,才能使用。
学习查看工具:
c/c++官方链接:C 标准库头文件 - cppreference.com
cplusplus.com:C library - C++ Reference (cplusplus.com)
#include <stdio.h> #include <stdlib.h> int main() { srand((unsigned int)time(NULL)); int r=rand()%100-1; printf("随机数:%d",r); return 0; }
2.3格式
库函数文档格式:
函数原型;函数功能介绍;参数和返回类型说明;代码举例;代码输出;相关知识链接
3.自定义函数
3.1语法格式
ret_type fun_name(形式参数) { } //ret_type是函数返回类型 //fun_name 是函数名 //括号中放的是形式参数 //{}这里是函数体
ret_type是用来表示函数计算结果类型,void表示什么都不返回。
fun_name是为了方便使用函数,方便调用。名字也要有意义
参数也可以是void,表示没有参数。有参数,要交代函数类型和名字、个数
函数体是完成计算的过程
3.2例子
#include <stdio.h>
int ADD(int x, int y)//参数,具体值由主函数给出
{
int z = 0;
z = x + y;
return z;//返回值是一个z的变量存储的值
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int r = ADD(a, b);//a,b对应x,y.整个add(a,b)对应的就是add函数中的返回值即z变量存储的值
printf("%d\n", r);
return 0;
}
函数部分要交代清楚:参数个数,参数类型,参数名字。
3.3形参和实参
函数的参数分为形参和实参
3.3.1实参
根据上面的代码,在主函数中括号内的a,b就是传递给函数的参数,称为实际参数,实参。实参就是实际给函数的参数
3.3.2形参
在add函数定义中,x,y就是形式参数,形参
不调用add函数的话,x,y只是形式上存在,不占内存空间,不真实存在。当函数被调用,有实参传递过来后,才会占用内存空间,这个过程称为形参的实例化。
3.3.3形参实参
形参和实参存储的地址是不同的,实参的传递类似于将实参的值拷贝给形参。
3.4.return
return 后面可以什么都没有,适用于函数返回类型void
return返回的值和函数返回类型不一样,系统会自动将返回值隐式转换为函数的返回类型。
当return执行后,函数就已经返回了值,在这个函数内return后面的语句不会被执行,因此通常放最后。
若函数中有if等分支语句,每种情况下的参数应该都有return返回,否则会错误。
3.5.数组做函数参数
数组传参:
1.形参和实参个数匹配
2.实参是数组,形参也能写成数组
3.形参是一维数组,数组大小可省略
4.形参是二维,行可省,列不可省
5.数组传参,形参不会创建新数组
6.形参操作的数组和实参操作的数组实际是同一个
举例:将一个整型数组的内全部写为-1,并且打印。
#include<stdio.h> void set_arr(int arr[], int sz)//此处的arr[]和sz是形参,具体的值在主函数中给入 { int i = 0; for (i = 0; i < sz; i++)//赋值-1 { arr[i] = -1; } } void print_arr(int arr[], int sz)//此处的arr[]和sz是形参,具体的值在主函数中给入 { int i = 0; for (i = 0; i < sz; i++)//打印数组 { printf("%d", arr[i]); } } int main() { int arr[] = { 1,2,3,4,5,6,7,8 };//初始化数组 int sz = sizeof(arr) / sizeof(arr[0]) ;//计算数组大小 set_arr(arr, sz);//调用函数 print_arr(arr, sz); return 0; }
3.6.嵌套调用和链式访问
3.6.1嵌套调用
嵌套调用是函数之间的互相调用。
例子:计算某年某月有多少天。
实现函数:rn() shu()
#include <stdio.h> int rn(int nian)//判断闰年的函数 { int z = 0; if (nian % 4 == 0 && nian % 100 != 0 || nian % 400 == 0) { z = 1; } else { z = 0; } return z;//返回值 } int shu(int nian,int y)//判断月份天数 { int days[] = { 0,31,28,31,30,31,30,31,30,31,30,31 }; int day = days[y]; if (rn(nian) == 1 && y == 2) { day++; } return day; } int main() { int nian = 0; int y = 0; scanf("%d %d", &nian, &y); int da = shu(nian, y); printf("%d", da); return 0; }
注意,函数不能嵌套定义
3.6.2链式访问
链式访问指的是将一个函数的返回值作为另外一个函数的参数。包括库函数和自定义函数
举例1:
#include <stdio.h> int main() { printf("%d", strlen("www"));//链式访问,将strlen函数作为一个参数放入printf函数 return 0 ; }
注意,在链式访问时要注意函数的返回值究竟是什么,比如strlen返回值是字符串长度
printf函数的返回值是打印的字符个数
第一个printf打印了100,返回值是3
第二个pirntf打印了3,返回值是1
第三个printf打印了1,返回值1
所以最终是(第一个)100(第二个)3(第三个)1
3.7函数声明和定义
3.7.1单个文件
一般使用函数时,直接写出来
比如上面月份天数的代码
#include <stdio.h> int rn(int nian)//判断闰年的函数 { int z = 0; if (nian % 4 == 0 && nian % 100 != 0 || nian % 400 == 0) { z = 1; } else { z = 0; } return z;//返回值 } int shu(int nian,int y)//判断月份天数 { int days[] = { 0,31,28,31,30,31,30,31,30,31,30,31 }; int day = days[y]; if (rn(nian) == 1 && y == 2) { day++; } return day; } int main() { int nian = 0; int y = 0; scanf("%d %d", &nian, &y); int da = shu(nian, y); printf("%d", da); return 0; }
int rn和int shu的部分都是函数的定义,而直接将函数写入函数体的部分则为函数的调用。
但要注意:因为c语言程序是逐行执行的,如果先调用再定义,编译器就会报错。
为了避免这种报错,可以选择声明函数
声明函数需要交代:函数名、函数返回类型、函数参数
比如int rn(int nian); 参数可以只给类型,省略名字
下面的代码提前声明了rn函数,尽管函数的调用在定义前,但还是能识别,函数的定义也是一种特殊的声明。
#include <stdio.h> int rn(int nian); int shu(int nian,int y)//判断月份天数 { int days[] = { 0,31,28,31,30,31,30,31,30,31,30,31 }; int day = days[y]; if (rn(nian) == 1 && y == 2) { day++; } return day; } int main() { int nian = 0; int y = 0; scanf("%d %d", &nian, &y); int da = shu(nian, y); printf("%d", da); return 0; } int rn(int nian)//判断闰年的函数 { int z = 0; if (nian % 4 == 0 && nian % 100 != 0 || nian % 400 == 0) { z = 1; } else { z = 0; } return z;//返回值 }
3.7.2多个文件
实际写程序时,代码量可能比较多,因此不会将所有代码都放在一个文件中。
一般情况,函数声明、类型的声明都放在头文件(.h)中,函数的定义放在一个源文件(.c/.cpp)中,函数的调用在主函数源文件(.c/.cpp)中,从而方便书写。
3.7.3static和extern
static和extern都是c语言的关键字
static是静态,用来:修饰局部变量、修饰全局变量、修饰函数
extern是用来声明外部符号
3.7.3.1作用域
程序中用到的名字不一定都是有效的,限定名字的可用范围就是名字的作用域
1.局部变量的作用域是变量所在的局部范围,比如一个for循环里定义的变量只能在这个for循坏里使用,不能在这个for循坏外面使用
2.全局变量的作用域是整个工程
3.7.3.2生命周期
指变量的创建(申请内存)到变成的销毁(收回内存)之间的一个时间段
1.局部变量:进入作用域生命周期开始,到出作用域生命周期结束
2.全局变量的生命周期是:整个程序的开始到结束
3.7.3.3static
修饰局部变量时,可以让变量的生命周期延长,达到全局变量的生命周期,也就是说变量存储的值在出了作用域也会一直存储,而不是释放内存。但作用域没有变化。
本来局部变量是存储在内存的栈区,但static修饰局部变量后会改为存储在静态区。
3.7.3.4extern
当想将一个a文件定义的符号在b文件里调用,就需要用extern来声明
例如extern int a;
当在一个源文件定义一个全局变量后,其他源文件通过extern就能调用这个变量,但是如果用static来修饰全局变量后,就不能在被其他源文件调用。
全局变量默认是带有外部链接属性的,而static修饰全局变量后,就变成内部链接属性了,无法被外部文件调用。
函数也是如此,当被static修饰后,就无法被其他文件调用,作用域被限定在本源文件,生命周期没有变。函数默认也是外部链接属性