曾是惊鸿照影来 (第六章函数)

1. 函数的概念

2. 库函数

3. ⾃定义函数

4. 形参和实参

5. return语句

6. 数组做函数参数

7. 嵌套调⽤和链式访问

8. 函数的声明和定义

关于c语言同志们可能会听过一句话,叫做c语言就是由一个一个函数组成的,这句话我感觉是非常对的。我们起始接触c语言写的第一个函数,就使用了main函数。那么接下来我们就来讲解函数,本章是很重要。

一、函数的概念

在数学中函数,就是一个非常重要的知识,y = kx +b,我们可以通过传入一个x值从而得到一个y值

在c语言中我们也引入了函数(有时也被称为子程序),其实C语言的函数就是引入一个完成某个特定任务的一小段代码。我们要实现该代码就要有特定的调用方式。

一般我们会见到两类函数:
1 库函数(编译器自带的)
2 自定义函数
二、库函数

C语⾔标准中规定了C语⾔的各种语法规则,C语⾔并不提供库函数;C语⾔的国际标准ANSIC规定了⼀些常⽤的函数的标准,被称为标准库,那不同的编译器⼚商根据ANSI提供的C语⾔标准就给出了⼀系列函数的实现。这些函数就被称为库函数
printf和scanf 函数都是常见的库函数,在使用的时候需要包含头文件。(库函数的使用为我们节省了时间)

2.2我们来给一个学习库函数的工具

https://legacy.cplusplus.com/

我们举个例子来说明怎么观察和使用库函数

strlen //举例的函数
size_t strlen ( const char * str )
1 .size_t是函数的返回类型。
2.strlen 是函数名
3.const char *str 函数的参数,表示strlen函数传递的是一个地址。

功能:计算的是字符串的长度,但是不包括\0。

我们来举例子

#include<stdio.h>
#include<string.h>
int main()
{
	char a[] = "abcdef";
	printf("%zd", strlen(a));//strlen该代码的使用需要包括头文件<string.h>
	return 0;
}

在这里插入图片描述
2.3 库函数的一般格式

在这里插入图片描述

三、自定义函数
了解库函数,但是更多更重要还是需要关注的是自定义函数(能给我们提高创作性)

我们来介绍一下,自定义函数的语法
在这里插入图片描述
我们可以把自定义函数理解成一个工厂

在这里插入图片描述
那么函数也一样的,我们创建一个函数通过形式参数接收数据(原材料),然后通过函数体实现(加工),得到返回数据(产品)。

  1. type 是用来接收计算后的返回类型,有时候返回类型是void类型(表示没有返回类型)
    2.name表示的函数名:我们创建函数名是用来调用函数的,有了函数名我们就可以方便调用函数的。(最好的方式就是名字次要达意)。
    3.函数的参数:函数的参数就相当于原材料,如果没有参数就是void函数有参数我们就要交代好参数的名字,类型,以及参数个数
    4、{}括起来的部分称为函数体,函数图就是完成计算的过程。

3.2 我们直接上例子
写一个加法函数,完成两个函数的相加

//所以这里返回值要用int类型接收
 int add(int x, int y)//我们传递的a,b,所以形式参数也要声名其类型和名字
{
	int z = 0;
	z = x + y;
	return z;  //这里的返回值是int类型的z 
}
//这里也可以简化为:
 //int add(int x, int y)//我们传递的a,b,所以形式参数也要声名其类型和名字
 //{
	// return x+y;  //这里的返回值是int类型的表达式
 //}

int main()
{
	int a = 0;
	int b = 0;
	printf("请输入两个数值,进行加法运算\n");
	scanf("%d%d", &a, &b);
	int ret = add(a, b);//函数名add表示的是相加,我们通过函数来传递整形 a,b
	printf("%d ", ret);
	return 0;
}

四:实参和形参

在函数的使用的过程中我们把函数的参数分为实参和形参

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

int main()
{
	int a = 0;
	int b = 0;
	printf("请输入两个数值,进行加法运算\n");
	scanf("%d%d", &a, &b);
	int ret = add(a, b);
	printf("%d ", ret);
	return 0;
}

4.1 实参
我们在调用函数的时候已经创建了a,b变量(已经有地址了),我们在调用的时候传递了a,b称为实际参数,简称实参

4.2 形参

在上面的函数中,在add的函数(x,y),称为形式参数简称实参。
为什么叫实际参数呢?事实上如果我们只是定义了add函数而不去调用的话,x,y只是在形式上存在的,但是其并不会创建地址(没有内存),不会真实存在。形式参数只有在调用的时候才会调用函数。

4.3实参和形参的关系
实参和形参是有联系的,但是其实际上两者是有区别的。

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

int main()
{
	int a = 0;
	int b = 0;
	printf("请输入两个数值,进行加法运算\n");
	scanf("%d%d", &a, &b);
	int ret = add(a, b);
	printf("%d ", ret);
	return 0;
}

在这里插入图片描述
这里我们发现,x和y确实接收了a,b的值,但是它们的地址是不一样的,所以我们可以理解为形参是实参的一份临时拷贝

5 .return 语句
在这里插入图片描述

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

int main()
{
	int a = 0;
	int b = 0;
	printf("请输入两个数值,进行加法运算\n");
	scanf("%d%d", &a, &b);
	int ret = add(a, b);
	printf("%d ", ret);
	return 0;
}
//222222222222
void print(void)
{
	printf("小黑子");
	return;
}
int main()
{

	print();
	return 0;
}

//33333333333
int output(void)
{ 
	double a = 3.14;
	return a;  //double类型
 }

int main()
{
	int ret = output();
	printf("%d ", ret);
	return 0;
}
//444444444444
void output(void)
{ 
	printf("小黑子");
	return;  //提前返回
	printf("真爱粉");
 }

int main()
{
	output();
}

//555555555555
//返回类型为int
int output(void)
{
	int a = 0;
	scanf("%d", &a);
	if (a >=0)
	{
		printf("a大于等于0\n");
		return a;

	}
	else
	{
		printf("a小于0\n");
		return a;
	}
}

int main()
{
	int ret =output();
	printf("%d", ret);
}

六、数组做函数参数

在我们使用函数的时候难免会遇见数组做函数的参数,那么当数组做函数的时候我们如何传参的呢?

我们来想一下数组中主要元素有两个(数组名,数组元素的个数

void print(int arr[], int x) //那么用形参来接收的时候需要用一个数组来接收实参的数组,另一个整形来接收元素个数
{
	for (int i = 0; i < x; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int ret = sizeof(arr) / sizeof(arr[0]); //得到的是元素个数
	print(arr, ret);//实参传递的一个是数组,一个是元素个数
	return 0;
}

这里我们需要注意以下几个重要的知识点

在这里插入图片描述

七、函数的嵌套调用和链式访问

我们知道循环可以嵌套的使用,那么函数可以嵌套使用吗?
当然也是可以的

举个例子:我们的手机是由许多部分组成而成的精密文件,那么函数之间相互的使用和调用才可以写出大型的程序

练习:我们计算某年某月有多少天


int is_year(int year)
{
	if ((year % 4 == 0) && (year % 100 != 0) || (year % 100 == 0))

	{
		printf("%d是闰年\n", year);
		return 1;  //为闰年
	}
	else
		return 0;  //不为闰年且返回值均为整数
		
}
int  is_day(int y ,int m )
{
	int arr[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	int day = arr[m];
		if (is_year(y)  && m == 2)  //函数的嵌套调用(这里用嵌套调用来判断是否为闰年)
		{
			day = day + 1;
			return day;
	   }
		else
		{
			return day;
		}
 }


int main()
{
	int y = 0; //输入的是年
	int m = 0;  //输入的是月
	scanf("%d%d", &y,&m);
	int days = is_day (y,m);
	printf("%d天", days);
	return 0;
}

函数是可以嵌套使用的,但是不能嵌套定义。

7.2 链式访问

所谓链式访问就是将⼀个函数的返回值作为另外⼀个函数的参数,像链条⼀样将函数串起来就是函数的链式访问。

int main()
{
	printf("%zd", strlen("abcdefg"));
	return 0;
}

这里我们先把strlen函数计算出字符串的长度,然后又把该函数得到的返回值作为printf函数的的参数进行了打印

这里我们来举个有趣的例子

int main()
{

	printf("%d", printf("%d", printf("%d", printf("%d", 54321))));
	//printf函数返回值就是打印字符的个数;
	return 0;
}

在这里插入图片描述
在这里插入图片描述

八、函数的声明和定义

1.特殊的声明

int add(int x, int y)  //函数定义
{
	return  x + y;   //当函数定义写在函数调用前就是特殊的声明
}


int main()
{
	int num1 = 0;
	int num2 = 0;
	scanf("%d%d", &num1, &num2);
   int ret =add(num1, num2); //函数调用
   printf("%d", ret);
	return 0;
}

2.正常的声明

int main()
{
	int num1 = 0;
	int num2 = 0;
	scanf("%d%d", &num1, &num2);
   int ret =add(num1, num2); //函数调用
   printf("%d", ret);
	return 0;
}


int add(int x, int y)  //函数定义
{
	return  x + y;   //当函数定义写在函数调用后没有声明就不可以正常使用
}

在这里插入图片描述

正常的改动应该是这样的

int add(int x, int y); //这里我们需要声明才可以正常的使用
int main()
{
	int num1 = 0;
	int num2 = 0;
	scanf("%d%d", &num1, &num2);
	int ret = add(num1, num2); //函数调用
	printf("%d", ret);
	return 0;
}


int add(int x, int y)  //函数定义
{
	return  x + y;   
}

进行改动就不会报错的,可以正常使用
在这里插入图片描述
8.2 多个文件

我们在写大型代码的时候,往往不会只创建一个文件,而是会创建出许多文件(根据文件所实现的内容不同)

一般情况下我们会把函数的声明,类型的声明放在头文件中(.h)函数的实现是放在源文件(.c)文件中。

在这里插入图片描述
这里我们分别给不同的文件中放入实现该函数的不同代码,但是为什么为报错呢?

原因是我们没有调用自己所创建的函数的头文件
如果我们加上就不会出现该问题

add.h文件

int add(int x, int y);//函数声明

add.c 文件

int add(int x ,int y)  //函数定义
{
	return x+y ;
}

text.c 文件

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include "add.h"
int main()
{
	int num1 = 0;
	int num2 = 0;
	scanf("%d%d", &num1, &num2);
	int ret= add(num1, num2);
	printf("%d", ret);
	return 0;
}

这样分别把代码的定义,调用,声明分别放在不同的文件中,写代码就更加方便。

8.3 static和extern
接下来我们来学习这两个关键字
static是静态的意思,它的作用可以用来
1.修饰局部变量
2.修饰全局变量
3.修饰函数

extern 是用来声明外部符号的。

在我们了解这两个关键字之前,我们先需要掌握作用域声明周期这两个概念。

作用域:指的是代码的可使用的范围
1.全局变量的作用域是整个项目(工程)
2.局部变量的作用域是变量所在的范围

生命周期:指的是代码进行创建(申请内存)销毁的过程(释放内存)
1.局部变量的生命周期,从进入作用域开始,出作用域结束
2.全局变量的生命周期,整个程序的生命周期

我们来分别举例子说明 static 和 extern的作用

1.修饰局部变量

void text2(void)
{
	 int a = 0;  //  每次进入 a 都会被重新赋值为0 ;
	a++;
	printf("%d ", a);
}

//1.static 修饰局部变量
int main()
{
	for (int i = 0; i < 5; i++)
	{
		text2(); //

	}

	return 0;
}

打印出的结果:
在这里插入图片描述

void text1(void)
{
	static int a = 0;  //static修饰局部变量a改变了其内存周期,使其在出函数的时候不会销毁,而是储存其执行的值
	a++;//不会再重新创建变量,直接累积上次的数值进行运算
	printf("%d ", a);
}

//1.static 修饰局部变量
int main()
{
	for (int i = 0; i < 5; i++)
	{
		text1(); //
	
	}

	return 0;
}

在这里插入图片描述
结论:经过static修饰的局部变量改变了其生命周期,本质是改变了变量的储存地区,由原来的栈区改变为静态区(静态区的生命周期只有程序结束变量才销毁,内存才回收),但是作用域不变。

建议:在以后我们使用变量后如果还想保留其值,就可以用static来修饰

2.static修饰全局变量
add.c文件的代码

int num1 = 10;

text .c函数的代码

//static修饰全局变量
//extern函数就是声明外部符号的
extern int num1;//我们要使用add.c文件中的数据,就要声明一下
int main()
{
	printf("%d", num1);
	return 0;
}

这里就可以正常打印
在这里插入图片描述
add.c文件的代码

 static int num1 = 10;

add.c文件的代码

//static修饰全局变量
//extern函数就是声明外部符号的
extern int num1;//我们要使用add.c文件中的数据,就要声明一下
int main()
{
	printf("%d", num1);
	return 0;
}

在这里插入图片描述
这里我们发现了经过static修饰过后的全局变量在add.c文件中无法使用了。

结论:经过static修饰的全局变量只能在本源文件内使用,及时其它源文件进行声明之后,也不可以使用了(也就是成为了内部的链接属性)

建议:如果一个全局变量只想在本源文件内部使用就可以用static进行限制

3.static 修饰函数

add.c文件的函数

#include<stdio.h>
void text(void)
{
	printf("abcdef");
}

text.c文件函数

//static修饰函数
extern void text(void);
int main()
{
	text();
	return 0;
}

可以正常使用
在这里插入图片描述

add.c文件的函数

#include<stdio.h>
static void text(void)
{
	printf("abcdef");
}

text.c文件函数

//static修饰函数
extern void text(void);
int main()
{
	text();
	return 0;
}

在这里插入图片描述
结论:这里我们发现static修饰函数和修饰全局变量的作用是一样的。
**使用建议:**⼀个函数只想在所在的源⽂件内部使⽤,不想被其他源⽂件使⽤,就可以使⽤ static 修饰。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值