函数的概念
在C语言中,存在一个函数的概念,有人也将其翻译为子程序。
在数学中,函数是一个完成特定功能的公式,比如传入x的值,就能输出满足函数的y值。C语言函数也同理,C语言的函数是一个完成某项特定任务的代码段。
而我们常见的函数,分为库函数与自定义函数,接下来我一一为大家介绍。
库函数
C语言标准中,规定了一系列常用的函数标准,称为标准库。编译器厂商就会根据这个标准来实现一系列函数,供用户使用,这些函数就称之为库函数。
要注意,库函数不是C语言官方实现的,C语言只制定标准,具体实现是编译器的厂商来完成的,所以不同的编译器下,库函数的实现可能不同,但是用法是一致的。
库函数是在标准库对应的头文件中声明的,所以库函数的使用需要包含对应的头文件。
自定义函数
顾名思义,自定义函数就是用户可以自己设计函数。
语法:
返回类型 函数名(参数) { //代码 }
返回类型:就是函数结束时返回什么样类型的值,比如我们调用rand函数,其会返回一个整数给我们,那么rand函数的返回类型就是int。
参数:就是用户使用这个函数,需要传入的值。
比如这样一个加法函数:
int add(int x, int y) { int z = x + y; return z; }
函数名就是add
,而add
左侧的int
就是函数的返回类型,说明add
函数最后会返回一个int
类型的数据。
(int x, int y)
就是函数的参数,这说明使用这个函数需要传入两个int
类型的数据。
在需要使用函数时,可以这样:
add(3, 5);
也就是函数名(参数)
的形式。
实参与形参
含函数使用的过程中,我们把参数分为形参和实参。
实参:
在以上的add
函数中,我们在调用时add(3, 5)
中的3
和5
就是实参,它们是传入函数的数据。
形参:
在定义函数时,add
函数名后面的(int x, int y)
中的x和y就是形参。
为什么叫做形参呢?实际上,如果只是定义了add
函数而不去调用的话,x
和y
只是形式上存在的,它们并不会向内存申请空间,不会真实存在,所以叫做形参。
而形参是实参的一份临时拷贝,对形参的改动不会影响实参。
比如下面的函数:
void func(int x) { x++; } int main() { int a = 0; func(a); printf("%d", a); return 0; }
上述函数中,a
就是一个实参,起初a = 0
,当我们把a
传给函数func
,形参x
得到a
的值。在函数内部对形参进行了x++
,此时x = 1
,当函数结束时,输出a的值。
最后输出的a = 0
,因为x
作为形参,x++
对a
没有影响。
return语句
当我们的函数需要返回值的时候,就需要return语句,return后面接着的值,就是函数最后返回给外界的值。
- return后面可以是数值,也可以是表达式。
- return后面也可以什么都没有,直接写return;,这种形式适合返回值为void类型。
- return后面的值类型如果与返回类型不一致,编译器则会将其隐式转化。
- 如果函数中有分支语句,要保证每种情况都有返回值。
数组做参数
数组也可以做函数的参数,比如这样:
void func(int arr[]) { }
其中int arr[]
就是一个数组类型的参数,其可以接收一个数组。
数组传参时,直接传入数组名即可:
int main() { int arr[5] = {1, 2, 3, 4, 5}; func(arr); return 0; }
- 当一维数组做形参时,数组的长度是可以省略的。
- 当二位数组做形参时,行可以省略,列不可以省略。
- 当数组做参数,实参和形参是同一个数组,修改形参会对实参造成影响。
声明与定义
C语言的函数声明和定义是定义函数的两个主要部分。
函数定义是指在函数声明的基础上,给出函数的具体实现。函数定义包含函数的返回值类型、函数名、函数的参数列表和函数体。函数定义的目的是为了给出函数的具体实现,也就是函数体中要执行的代码。
函数定义的语法格式如下:
返回类型 函数名(参数) { // 函数体 }
也就是我们先前讲的基本格式,我们实现函数的功能,就是在函数定义时完成的。
函数声明是指在函数被调用之前,在代码的某个地方提前声明函数的存在。函数声明告诉编译器函数的名称、函数参数的类型和返回值类型。函数声明的目的是为了让编译器在编译过程中能够正确地解析函数的调用。
函数声明的语法格式如下:
返回值类型 函数名(参数类型1 参数名1, 参数类型2 参数名2, ...)
比如:
int sum(int a, int b);
这就是一个函数的声明,可以理解为函数声明就是函数定义去掉了{}内部的内容。
不过函数声明也可以只有类型,没有形参名称,比如这样:
int sum(int, int);
函数声明和定义的关系是:函数声明是对函数定义的提前声明,函数定义是对函数的具体实现。在实际使用中,通常将函数的声明放在头文件中,将函数的定义放在源文件中。这样做的好处是可以将函数的声明和定义进行分离,方便代码的管理和维护。
需要注意的是,在C语言中,函数的声明和定义可以放在任意顺序,只要在函数被调用之前可以访问到函数的声明即可。
extern
在C语言中,extern
关键字用于声明一个全局变量或者函数,但不进行定义。它可以被用于多个源文件中,用于告诉编译器该变量或函数是在其他源文件中定义的。
在C语言中,变量的声明和定义是不同的。声明告知编译器该变量的类型和名称,而定义则为该变量分配存储空间并初始化它。如果在一个源文件中声明一个全局变量,那么在其他源文件中访问该全局变量就需要使用extern
关键字。
这里是一个例子来说明extern
关键字的使用:
假设我们有两个源文件:main.c和helpers.c。我们希望在main.c中使用helpers.c中定义的全局变量count
和函数increment()
。
首先,我们在helpers.c中定义全局变量:
int count = 0; void increment() { count++; }
然后,在main.c中声明这些全局变量和函数,并使用extern
关键字告诉编译器它们是在其他源文件中定义的:
#include <stdio.h> extern int count; // 声明count变量 extern void increment(); //声明increment函数 int main() { increment(); // 调用increment函数 printf("count: %d\n", count); // 访问count变量 return 0; }
注意,在main.c中,我们需要包含helpers.c中定义的函数的头文件,以便在编译过程中编译器可以知道函数的签名。
编译并运行这些代码,输出将会是count: 1
。这是因为我们在increment函数中增加了count变量的值,并在main函数中访问了它。
总结一下,extern
关键字是用于在一个源文件中访问另一个源文件中定义的全局变量和函数。它将变量或函数的声明与定义分离,使得代码更加模块化和可扩展。
static
在C语言中,"static"是一个关键字,用于修饰变量和函数。它的行为和作用会根据修饰的对象不同而有所不同。下面分别介绍"static"关键字在变量和函数中的作用,并给出相关案例。
- "static"修饰变量:
- 在函数内部,"static"修饰的局部变量会被赋予静态的存储持续时间。这意味着变量的生命周期将持续到程序结束,而不是在函数执行完后销毁。此外,静态局部变量的作用域仍然是局部的,只能在定义它的函数内部使用。
- 例子:
#include <stdio.h> void example() { static int count = 0; count++; printf("Count: %d\n", count); } int main() { for (int i = 0; i < 5; i++) { example(); } return 0; }
- 输出结果:
Count: 1 Count: 2 Count: 3 Count: 4 Count: 5
- 在上面的例子中,静态变量"count"被定义为函数"example()"的局部变量。每次调用"example()"函数时,"count"值递增并打印。由于静态变量的存储持续时间为整个程序,因此每次调用"example()"函数时,"count"的值会保留。
- 在全局范围内,"static"修饰的全局变量具有内部链接,即只能在当前源文件中访问,其他源文件中不可见。
- 例子:
// File1.c static int global_var = 10; // File2.c #include <stdio.h> extern int global_var; int main() { printf("Global Variable: %d\n", global_var); return 0; }
- 输出结果:
Global Variable: 10
- 在上面的例子中,我们在File1.c中声明了一个静态全局变量"global_var"。由于它是静态的,所以只能在同一源文件中访问。然后,在File2.c中通过extern关键字声明"global_var",确保在其他源文件中可见,从而在main函数中进行访问。
- "static"修饰函数:
- 在函数声明前,"static"修饰的函数具有内部链接。这意味着该函数只能在当前源文件中访问,其他源文件中不可见。
- 例子:
// File1.c static void static_function() { printf("This is a static function.\n"); } // File2.c extern void static_function(); int main() { static_function(); return 0; }
- 输出结果:
This is a static function.
- 在上面的例子中,我们在File1.c中定义了一个静态函数"static_function()“。由于它是静态的,所以只能在同一源文件中访问。然后,在File2.c中通过extern关键字声明"static_function()”,确保在其他源文件中可见,从而在main函数中进行访问。
总结:
- 在C语言中,"static"关键字的作用取决于其修饰的对象。它可以用于修饰局部变量和全局变量,以及函数声明。
- 对于局部变量,"static"赋予了它们静态存储持续时间和保留其值的能力。
- 对于全局变量,"static"给予了它们内部链接特性,使其只能在同一源文件中访问。
- 对于函数声明,"static"使其具有内部链接特性,使其只能在同一源文件中访问。
嵌套调用
在C语言中,函数的嵌套调用指的是在一个函数中调用另一个函数,而被调用的函数又可以继续调用其他函数。这种嵌套调用的方式可以实现代码的模块化,提高代码的可读性和可维护性。
下面以一个简单的例子来说明函数的嵌套调用:
#include <stdio.h> int func2(int num) { printf("In func2\n"); return num * num; } int func1(int num) { printf("In func1\n"); int result = func2(num); return result + 1; } int main() { int number = 3; int result = func1(number); printf("Result: %d\n", result); return 0; }
在上面的例子中,有三个函数:main
、func1
和func2
。main
函数是程序的入口,func1
和func2
是被调用的函数。
main
函数中定义了一个整型变量number
,并将其赋值为3。然后调用func1
函数,将number
作为参数传递给func1
。在func1
函数中,可以看到它调用了func2
函数,并将num
作为参数传递给func2
。func2
函数执行完毕后,返回结果给func1
,最后func1
将其结果加1并返回给main
函数。
运行上述代码,输出结果如下:
In func1 In func2 Result: 10
从结果可以看出,main
函数调用func1
函数,func1
函数又调用了func2
函数,实现了函数的嵌套调用。通过函数的嵌套调用,可以将代码按照功能划分为若干个小模块,提高代码的可读性和可维护性。