C语言的函数讲解

目录

一、函数是什么

二、库函数

三、自定义函数

四、形参实参

五、return语句

六、数组做函数参数

七、链式访问

八、函数的声明与定义

一、单个文件

二、多个文件

九、static和extern关键字

static关键字

extern关键字

十、作用域与声明周期


一、函数是什么

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;
}
  1. 形参和实参的概念形参是在函数定义时使用的变量,用于接收传递给函数的值。实参是在函数调用时传递给函数的值。形参是函数定义的一部分,而实参是函数调用的一部分。
  2. 形参和实参的关系形参是函数定义时用来接收实参的变量,实参是函数调用时传递给函数的值。在函数调用时,实参的值会被复制给形参,因此在函数内部对形参的任何修改都不会影响实参。
  3. 地址问题:在C语言中,当传递变量给函数时,实际上传递的是变量的值的拷贝,而不是变量的地址。
  4. 参数的数量:在函数定义和调用时,参数的数量必须一致。如果函数定义了两个参数,那么在调用时也必须传递两个参数。

五、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关键字,则它的链接属性变为内部链接,只能在声明它的文件内部使用。

  • 41
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值