目录
一、函数是什么
C语⾔中的函数就是⼀个完成某项特定的任务的⼀⼩段代码。
在整个应用程序中,是由许许多多个函数来构成的。
而且函数可以重复使用,大大提高了开发效率
在C语言中,函数是执行特定任务的基本单位,是C程序的基本构建块。
二、库函数
库函数是C语言中本身就有的函数,我们可以直接调用,而不用再重新写一遍。
在调用库函数时,我们需要包含库函数对应的头文件,这样编译器才能识别并调用这些函数。
我们常用的函数有printf,使用这个函数时需要进行库函数的调用就需要包含这个头文件#include <stdio.h>。
在C语言中也有许多这样的函数,比如使用sqrt来计算根值,那么我们就需要使用#include <math.h>这个头文件,进行调用库函数。
库函数有许多,不用一下子都记住。
库函数网站:C 标准库头文件 - cppreference.com
在我们想调用某个函数的时候,可以去这个网站里进行参考,来寻找合适的库函数。
例:
#include <math.h>
#include <stdio.h>
int main()
{
double a = 16;
double b = sqrt(a);
printf("%lf", b);
return 0;
}
输出:
三、自定义函数
函数的语法类型
ret_type fun_name(形式类型)
{
}
ret_type是返回类型
fun_name是创建名
{ }大括号里放入函数体
例子:
// 定义add函数,用于接收两个整数参数x和y,并返回它们的和
int add(int x, int y) {
int z = 0; // 定义局部变量z,用于存储x和y的和
z = x + y; // 计算x和y的和,并将结果存储在z中
return z; // 返回z的值,即x和y的和
}
// main函数,程序的入口点
int main() {
int a = 0; // 定义整数变量a,用于存储第一个输入的整数
int b = 0; // 定义整数变量b,用于存储第二个输入的整数
scanf("%d%d", &a, &b); // 从用户那里读取两个整数,并存储在a和b中
int r = add(a, b); // 调用add函数,将a和b作为参数传递,并将返回值存储在r中
printf("%d", r); // 打印变量r的值,即a和b的和
return 0; // 表示程序执行成功
}
在这部分没有理解,那么就继续往下看,下面会把你的疑问给讲解出来。
四、形参实参
int add(int x, int y)//x和y是形参
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d%d", &a, &b);
int r = add(a, b); //a和b是实参
printf("%d", r);
return 0;
}
- 形参和实参的概念:形参是在函数定义时使用的变量,用于接收传递给函数的值。实参是在函数调用时传递给函数的值。形参是函数定义的一部分,而实参是函数调用的一部分。
- 形参和实参的关系:形参是函数定义时用来接收实参的变量,实参是函数调用时传递给函数的值。在函数调用时,实参的值会被复制给形参,因此在函数内部对形参的任何修改都不会影响实参。
- 地址问题:在C语言中,当传递变量给函数时,实际上传递的是变量的值的拷贝,而不是变量的地址。
- 参数的数量:在函数定义和调用时,参数的数量必须一致。如果函数定义了两个参数,那么在调用时也必须传递两个参数。
五、return语句
1、return后面可以是一个数值,也可以是一个表达式,如果return
后面是一个表达式,那么这个表达式会先被执行,然后其结果被返回。
2、在返回值为void的时候,可以直接写return其他都不写,可以直接不写return。但是,写return;
是一个好的编程习惯,因为它清楚地表明了函数的结束。
3、如果return
语句返回的类型与函数声明的返回类型不一致,C语言不会自动进行类型转换。例如,如果一个函数声明为返回int
类型,但是return
语句返回了一个float
类型的值,这将导致编译错误。
4、一旦执行了return
语句,函数会立即结束,即使函数体中还有其他代码也不会被执行。
5、如果函数的返回类型不是void
,
函数有if分支,需要保证每个分支都有一个return返回,否则会出现编译器报错。
第五点代码如下:
int func(int a) {
if (a > 0) {
return a;
} else if (a < 0) {
return -a;
} else {
return 0; // 处理a等于0的情况
}
}
六、数组做函数参数
可以把一个数组作为函数的参数。
例题:把数组传参到函数中,如何打印出来。
void add(int arr[], int sz)
{
int i = 0;
for ( i = 0; i < sz ; i++)
{
printf("%d ", arr[i]);
}
}
int main()
{
int arr[] = { 1,2,3,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
add(arr,sz);
return 0;
}
结果:
我们把难度加大,在创建一个函数,把arr数组中的数全部变成-1然后打印出来。
// 定义一个函数 set_arr,用于将数组 arr 的每个元素设置为 -1
void set_arr(int arr[], int sz)
{
int i; // 定义循环变量 i
for (i = 0; i < sz; i++) // 循环从 0 到 sz-1
{
arr[i] = -1; // 将数组 arr 的第 i 个元素设置为 -1
}
}
// 定义一个函数 add,用于打印数组 arr 的每个元素
void add(int arr[], int sz)
{
int i = 0; // 定义并初始化循环变量 i
for (i = 0; i < sz; i++) // 循环从 0 到 sz-1
{
printf("%d ", arr[i]); // 打印数组 arr 的第 i 个元素,后面跟一个空格
}
}
// main 函数,程序的入口点
int main()
{
int arr[] = { 1,2,3,5,6,7,8,9,10 }; // 定义并初始化一个整数数组 arr
int sz = sizeof(arr) / sizeof(arr[0]); // 计算数组 arr 的大小(元素个数)
set_arr(arr, sz); // 调用 set_arr 函数,将数组 arr 的每个元素设置为 -1
add(arr, sz); // 调用 add 函数,打印数组 arr 的每个元素
return 0; // 程序结束,返回 0
}
七、链式访问
链式访问的意思是,在函数中,在一个函数中嵌套另一个函数,在被嵌套的函数中,再嵌套另一个函数内,可以一直下去。
例题:假设我们计算某年某⽉有多少天?如果要函数实现,可以设计2个函数:
• is_leap_year():根据年份确定是否是闰年
• get_days_of_month():调⽤is_leap_year确定是否是闰年后,再根据⽉计算这个⽉的天数
// 函数get_days_of_month:获取给定年份和月份的天数
// 输入参数:int y - 年份,int m - 月份
// 返回值:int - 该月的天数
int get_days_of_month(int y, int m)
{
// 定义一个数组,存储每个月的天数,0索引不使用
int days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int day = days[m]; // 获取当前月份的天数
// 如果是闰年且当前月份是2月,则天数加1
if (is_leap_year(y) && m == 2)
day += 1;
return day;
}
// 主函数main
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; // 程序结束
}
输出:
get_days_of_month(y, m);在这个函数中,我们看见里面又嵌套一个函数。
八、函数的声明与定义
一、单个文件
我们在上面这么例子中,已经看过了单个文件的函数声明与定义。接下来我们再讲一下。
函数声明的作用:函数声明告诉编译器函数的名称、返回类型和参数类型,这样编译器在遇到函数调用时就能正确地处理。
在图中看见,add函数在主函数的下面,程序的执行是自上而下进行的,从而在运行中会无法找到add这个函数,在这个简单函数中,是可以运行成功的,但这是错误的用法,我们需要声明一个函数,要让机器知道我们有这个函数,在运行程序。
在函数有了声明以后,那么就可以正常运行了。
二、多个文件
在企业开发中,是有许许多多的文件,他们都是存放在其他文件里面。
⼀般情况下,函数的声明、类型的声明放在头⽂件(.h)中,函数的实现是放在源⽂件(.c)⽂件中。
test.c文件中
#include <stdio.h>
#include “add.h”
int main()
{
int a = 0;
int b = 0;
scanf("%d%d", &a, &b);
int sz = add(a, b);
printf("%d", sz);
return 0;
}
int add(int a, int b)
{
return a + b;
}
add.c文件中
int add(int a, int b)
{
return a + b;
}
add.h文件中
int add(int a, int b);
九、static和extern关键字
static关键字
static
关键字用于声明静态局部变量,它使得局部变量的生命周期延长到程序的整个执行过程,但它的作用域仍然限制在声明它的函数内部。改变后的全局变量只可以在当前我.c文件使用,无法跨文件链接使用。
extern关键字
extern关键字可以链接其他.c文件所也的函数,但是无法链接到含有static函数.和的文件。
例题:
test.c
extern int add(int a, int b);
int main()
{
int a = 0;
int b = 0;
scanf("%d%d", &a, &b);
int sz = add(a, b);
printf("%d", sz);
return 0;
}
add.c
int add(int a, int b)
{
return a + b;
}
输出:
十、作用域与声明周期
作用域:变量限定在一个范围内有效的地方
1、局部变量:作⽤域是变量所在的局部范围。
2、全局变量:全局变量可以链接其他.c文件,全局变量的作⽤域是整个⼯程(项⽬)
声明周期:是创建变量到销毁变量的一个时间段。
1、局部生命周期:进入作用域开始到出作用域是局部声明周期。
2、全局生命周期:整个程序的生命周期。
全局变量的作用域是整个工程(项目),如果全局变量声明时加上static
关键字,则它的链接属性变为内部链接,只能在声明它的文件内部使用。