1. 简介
函数是C程序的基本组成模块,是可以重复执行一段代码的工具,以简化代码工作量。
函数分为系统函数和自定义函数。系统函数是C标准库提供的库函数,库函数是已经编写好的,存放于库文件中,通过导入对应的库文件可以使用库函数,比如我们之前用到的printf()函数。自定义函数是程序员自己编写的函数,本节主要是自定义函数。
构建自定义函数的过程主要分为三个部分:
- 声明函数:创建函数信息
- 定义函数:定义函数主体(实际的代码)
- 调用函数:使用函数
2. 函数的声明
函数要先声明才能使用,函数声明就是提前告诉编译器函数的信息。函数声明包含返回类型、函数名、参数列表,而函数体则在函数定义中。函数声明的表现形式为:返回类型 函数名(参数列表);
int func(int arg);//声明一个函数,int为返回类型、func是函数名,int arg为参数
函数声明应放在调用函数的文件顶部,或者在一个头文件中,然后在源文件中包含这个头文件,不能声明在函数的内部。
如果想要不声明函数,那可以将函数定义在main函数的前面,因为main函数首先执行,因此要放到最后。如果多个源文件或者函数比较多的情况下,还是要声明函数,易于管理和维护。
3. 函数的定义
函数的定义包含了函数的主体部分,是函数执行的实际代码。函数的定义包含返回类型、函数名、参数列表、函数的主体代码、return语句。函数的声明可以很多次,但只能被定义一次,降低了代码的重复性。
int func(int arg){ //定义函数func
int a = 5;
int b = 10 + arg;
return a+b;
}
int func2(){ //定义无参函数func2,小括号不能省略
int a = 5;
int b = 10;
return a+b;
}
上面的示例代码中定义了一个函数func,返回类型为int,小括号(int arg)里是参数列表,大括号{}里的代码是函数主体,功能是计算变量a和变量b的和,return语句是返回对应类型的值,即int类型的值。
注意:这里的参数列表是形式参数列表,只有真正调用函数的时候才是实际的参数列表。形参的个数、类型、顺序要和实参一致。
4. 函数的调用
函数声明和定义好后,需要调用函数才能执行到函数的实际代码。函数调用就是需要使用到的地方执行函数的主体部分,程序的控制权也随之转移到该调用的函数,当return语句执行后,再把控制权交给主程序。函数调用的形式为:函数名(实参列表);
int func(int arg);//声明函数
int func(int arg){ //定义函数func
int a = 5;
int b = 10 + arg;
return a+b;
}
int a = 5;
func(a);//调用函数
5. 函数的传参
5.1 函数参数副本
函数调用时,实参的列表会被拷贝一份传递给形参列表,带入到函数主体中执行。这就意味着,在执行函数体内的代码时,操作的不是实参本身,而是实参的副本,不会对真正的实参产生影响。
void func(int arg){
arg += 5;
return;
}
int a = 10;
func(a); //传递的是a的副本,a本身不会改变
printf("a的值为%d\n", a);//等于10
5.2 传递指针参数
函数参数的传递默认是传递参数副本,而传递指针变量,传递的就是参数本身,在函数体内进行操作,可以更改实参的值,原因是传递的是地址,地址只有一份。
void func(int *arg){
*arg += 5;
return;
}
int a = 10;
func(&a); //传递的是a的地址
printf("a的值为%d\n", a);
5.3 可变参数
某些情况下,函数的参数是不确定的,函数可以接受可变数量的参数,这就是可变参数函数。C语言中使用省略号(…)来定义可变参数列表。
int func(int arg1,...);//声明可变参数函数
arg1是一个固定的参数,后面的(…)表示可以跟多个数量的参数,至少有一个固定参数。
C语言提供了一系列宏来处理可变参数,存放于stdarg.h头文件中。
- va_list宏:数据类型,用于存储可变参数列表;
- va_start宏:函数,用于初始化va_list(可变参数列表)变量,指向第一个可变参数;
- va_arg宏:函数,获取参数列表的下一个参数,并移动指针;
- va_end宏:函数,清理va_list(可变参数列表),不再指向有效的内存地址。
void test(int arg1,...);//声明可变参数函数
int main(void){
test(3,1,2,3,4,5);//调用可变参数函数
return 0;
}
void test(int arg1,...){ //定义可变参数函数
va_list args;//可变参数列表
va_start(args,arg1);//初始化va_list
int count;
for(int i = 1;i <= arg1;++i){//遍历获取可变参数
count = va_arg(args,int);
}
va_end(args);//结束
}
6. main()函数
C语言中,main()函数是一个特殊的函数,它是程序的入口函数,是程序的起点和终点。每个程序都必须有一个main()函数,其他的函数都必须在这个函数中直接或间接调用。
//main函数的基本形式
int main(void){
printf("hello\n");
return 0;
}
main函数的返回值类型通常是int,返回0表示程序运行结束,如果不是返回0,则表示程序出错导致异常退出,此外main函数还可以接受带参数的形式。
int main(int argc,char *argv[]){
...
return 0;
}
带参的main函数,主要用于接受命令行输入的参数,argc参数代表命令行参数的个数,argv则是一个指向字符数组的指针,用于存储实际输入的命令行参数。
7. 函数修饰符
7.1 extern修饰符
函数声明默认是extern修饰的,在多个源文件的情况下,使用extern修饰函数表示函数的定义来自于其他源文件,这一点和修饰变量是一样的,相当于“引入”。
//func.c文件
int func(int arg){
return arg + 1;
}
//test.c文件
extern int func(int arg);
int main(void){
int a = func(1);
return a;
}
7.2 static修饰符
static修饰符修饰的函数表示静态函数,只能在当前文件中使用,其他文件中不能使用。
static int func(int arg){ //只能在当前文件中使用
int a = arg + 1;
return a;
}
7.3 const修饰符
const只能修饰函数内部的局部变量和形式参数,和之前修饰的变量一样。
8. 总结
- 函数先声明后定义,再调用;
- 函数传参默认传递的是实参副本,改为指针变量就可以传递实参本身;
- C语言支持可变参数的函数,使用一系列宏来处理可变参数;
- main函数是程序的入口函数,也是程序的终点;
- 在函数前加上修饰符,可以改变函数的行为。