C语言函数

1.函数概念

数学中我们其实就⻅过函数的概念,⽐如:⼀次函数 y=kx+b ,k和b都是常数,给⼀个任意的x,就得到⼀个y值。
其实在C语⾔也引⼊函数(function)的概念,有些翻译为:⼦程序,⼦程序这种翻译更加准确⼀些。**C语⾔中的函数就是⼀个完成某项特定的任务的⼀⼩段代码。**这段代码是有特殊的写法和调⽤⽅法的。C语⾔的程序其实是由⽆数个⼩的函数组合⽽成的,也可以说:⼀个⼤的计算任务可以分解成若⼲个较⼩的函数(对应较⼩的任务)完成。同时⼀个函数如果能完成某项特定任务的话,这个函数也是可以复⽤的,提升了开发软件的效率。
在C语⾔中我们⼀般会⻅到两类函数:
• 库函数
• ⾃定义函数

2.库函数

2.1标准库和头文件

C语⾔标准中规定了C语⾔的各种语法规则,C语⾔并不提供库函数;C语⾔的国际标准ANSI C规定了⼀些常⽤的函数的标准,被称为标准库,那不同的编译器⼚商根据ANSI提供的C语⾔标准就给出了⼀系列函数的实现。这些函数就被称为库函数。
我们前⾯内容中学到的 printf 、 scanf 都是库函数,库函数的也是函数,不过这些函数已经是现成的,我们只要学会就能直接使⽤了。有了库函数,⼀些常⻅的功能就不需要程序员⾃⼰实现了,⼀定程度提升了效率;同时库函数的质量和执⾏效率上都更有保证。
各种编译器的标准库中提供了⼀系列的库函数,这些库函数根据功能的划分,都在不同的头⽂件中进⾏了声明。
库函数相关头⽂件:[link](https://zh.cppreference.com/w/c/header
有数学相关的,有字符串相关的,有⽇期相关的等,每⼀个头⽂件中都包含了,相关的函数和类型等
信息,库函数的学习不⽤着急⼀次性全部学会,慢慢学习,各个击破就行

3.自定义函数

相对于库函数,自定义函数更为重要

3.1语法形式

ret_type fun_name(形式参数)
{
}
  • ret_type是函数返回的类型
  • fun_name是函数的名字,最好取得有意义,方便阅读
  • ()内是形式参数
  • {}是函数的主体

C语言函数_第1张图片
函数就是返回计算结果

3.2实例

完成加法函数

int add(int x, int y)
{
	int z = x + y;
	return z;//或直接return x+y
}
int main()
{
	int i = 0;
	int j = 0;
	scanf("%d %d", &i, &j);
	int q = add(i, j);
	printf("%d", q);

	return 0;
}

C语言函数_第2张图片
函数的参数部分需要交代清楚:参数个数,参数类型,参数名称

4.形参和实参

通俗得说形式参数就是上述x、y,实参就是上述输入的2、3
C语言函数_第3张图片
形参是实参的一份临时拷贝

5.return语句

函数中return语句的注意事项

  • return语句后可以是一个数值,也可以是一个表达式,如果是表达式,先执行表达式
  • return后什么也没有适合void的类型
  • return返回的值和函数返回类型不一样,则强制类型转换
  • return语句执行后,函数返回,后面代码不再执行
  • 如果函数存在if等分支语句,则每种情况都需要return

6.数组做函数参数

在使用函数解决问题时,难免要用数组作为参数在函数内部对数组进行操作
例如:写一个函数将一个整型数组的内容全部置为-1,在写一个函数打印这个数组

void set_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		arr[i] = -1;
	}
	return;
}

void print_arr(int arr[], int sz)
{
	int j = 0;
	for (j = 0; j < sz; j++)
	{
		printf("%d", arr[j]);
	}
	return;
}

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	set_arr(arr, sz);
	print_arr(arr,sz);
	return 0;
}在这里插入代码片

C语言函数_第4张图片

7.嵌套调用和链式访问

7.1嵌套调用

嵌套调用就是函数间相互调用
例如:假设我们计算某年某月有多少天,可以设计两个函数

  • is_leap_year():判断是否是闰年?
  • get_days_of_month():调用is_leap_year判断是否是闰年后,再根据月计算天数
int is_leap_year(int year,int mon)
{
	if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
		return 1;
	else
		return 0;
}

void get_days_of_month(int year,int mon)
{
	int arr[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	int i = arr[mon];
	if (is_leap_year(year,mon) && mon == 2)
		i++;
	printf("这月有%d天\n", i);
	return;

}

int main()
{
	int year = 0;
	int mon = 0;
	printf("请输入年份和月份:");
	while (scanf("%d %d", &year, &mon))
	{
		is_leap_year(year,mon);
		get_days_of_month(year,mon);

	}
	return 0;
}

main函数调用printf、scanf等函数
get_days_of_month调用了is_leap_year函数

7.2链式访问

指一个函数的返回值作为另一个函数的参数
例如:

int main()
{
	printf("%d", printf("%d", printf("%d", 43)));
	return 0;
}

C语言函数_第5张图片
由于printf函数打印返回的是打印字符的个数,所以出现4321

8.函数的声明和定义

8.1单个文件

在一个文件中,在函数调用前要声明函数,或者将函数定义放在调用前面
因为函数的定义也是一种声明

8.2多个文件

一般将函数的声明放在头文件中,将函数的实现放在源文件中
add.h

int add(int x,int y);

add.c

int add(int x,int y)
{
    return x+y;
}

C语言函数_第6张图片

8.3static和extern

static和extern都是C语言中的关键字
static是静态的意思,extern是用来声明外部符号的
先提提作用域和生命周期
作用域(scope)是程序设计概念,通常来说,⼀段程序代码中所⽤到的名字并不总是有效(可⽤)的,⽽限定这个名字的可⽤性的代码范围就是这个名字的作⽤域。

  1. 局部变量的作⽤域是变量所在的局部范围。
  2. 全局变量的作⽤域是整个⼯程。
    生命周期指的是变量的创建(申请内存)到变量的销毁(收回内存)之间的⼀个时间段。
  3. 局部变量的⽣命周期是:进⼊作⽤域⽣命周期开始,出作⽤域⽣命周期结束。
  4. 全局变量的⽣命周期是:整个程序的⽣命周期。
8.3.1static修饰局部变量

C语言函数_第7张图片
C语言函数_第8张图片
显然第一张图test函数的局部变量i是每次进入函数开始创建(生命周期开始)并赋值为1,出函数时变量销毁(生命周期结束)
而第二张图i的值具有累加效果,是由于static修饰局部变量改变生命周期,即改变变量的储存类型,本来一个变量是储存在栈区,后改为静态区,静态区的变量和全局变量一样,作用域是整个程序
使用建议:未来一个变量出了函数后,我们还想保留值,等下次进入函数时继续使用,static可胜任

8.3.2static修饰全局变量

C语言函数_第9张图片
代码1正常,代码2编译错误
extern是用来声明外部符号的,如果一个全局符号在A文件中被定义,想在B文件中使用,就可用extern声明
由于全局变量自带外部链接属性,但如果全局变量被static修饰,则被改为内部链接属性,其他源文件即使声明也无法使用

8.3.3static修饰函数

static修饰函数和修饰全局变量一样,函数在整个工程都可以使用,但被static修饰则只能在本文件中使用

你可能感兴趣的:(C语言知识点,c语言)