函数的相关介绍

在上一篇文章中,我们介绍了数组的基本知识,这篇文章则主要是介绍函数相关的基本知识,听到函数你是否是会想起什么?第一想法是不是数学中的函数?哈哈如果是这样,看来数学给我们留下了深刻的印象啊!!不要担心,今天的函数不是数学中那个函数。今天要讲的函数和C语言挂钩,接下来,让我们掀开它神秘的面纱!!

1.函数的概念

在数学中,y=kx+b是最常见一类函数,1个x对应1个y的值,一一对应。

在C语言中,函数就相当于一个子程序,而这个子程序帮我们负责解决对应的问题,所以函数就是解决相应的问题的一段代码。在我们每一次写的程序,都是由无数个小函数堆凑起来的。面对复杂的代码,我们可以利用函数,将其拆分成无数个小板块,这样敲出来的即通俗简介还便于理解。所以函数对我们解决复杂的问题!

函数分为两大类:

1)库函数

2)自定义函数

2.库函数

2.1 标准库和头文件

C语言规定了各种语法的规则,但是C语言并没有提供库函数。那么库函数是由谁提供的呢?C语言国际标准ANSIC规定了一些常用的函数的标准,被称为标准库,那不同的编译器厂商根据ANSI提供的C语言标准就给出了一系列函数的实现。这些函数就被称为库函数。

我们经常敲得代码,譬如:scanf、printf等等,这类函数就是我们所说的库函数,其功能的实现是由我们的编译器去实现。库函数不仅提高了敲代码的效率,而且保证了我们的代码的质量。

库函数中含有不同的函数,每一个函数的功能都不一样,我们使用每一种函数都必须要包含相应的头文件。如果不包含正确的头文件,编译器会报错!

如果想要拓展知识或者了解相关函数头文件,可以去c plus plus官网。

C 标准库头文件 - cppreference.comicon-default.png?t=N7T8https://zh.cppreference.com/w/c/header

C plus plus官网地址:cplusplus.comicon-default.png?t=N7T8https://cplusplus.com/

2.2 库函数的使用方法

我们既然知道了库函数,那么我们该如何取使用库函数官网呢?举个简单栗子吧~

sqrt------计算平方根

库函数从其基本结构、函数功能、参数、返回类型对函数进行了讲解,后面还有相关例子。不妨实际操作一下sqrt函数

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>//sqrt的头文件
int main()
{
    double num=0;
    scanf("%lf",&num);
    double ret=sqrt(num);
    printf("%lf\n",ret);
    return 0;
}

总结一下库函数的格式:

1).函数原型

2). 函数功能介绍

3). 参数和返回类型说明

4). 代码举例

5).相关知识链接

3.自定义函数

在解决一些实际问题时,利用标准库的函数可能解决不了,这个时候我们就想要自己写一个函数,来解决问题,自己写一个是完全没有问题,但是我们该怎样去写呢?要注意哪些问题呢?

3.1 自定义函数的基本结构

ret_type name(形式参数)
{

}

ret_type-----函数的返回值类型

函数的返回类型可以是int、char、double等等,如果不想要返回值可以用void ;

name-----函数名

创建的函数名可以是多样的,一般我们给函数命名是根据函数功能定义,譬如定义加法,通常命名为Add,诸如此类!

括号括起来的是形式参数;

{}中是函数体,我们解决问题主要代码。

3.2 函数举例

写一个函数,完成加法运算

分析:在创建函数时,我们首先要确定函数的返回类型,给函数命名,函数的参数类型,参数的个数。在创建函数体解决问题!

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int Add(int x, int y)//创建形式参数,接收a和b的值
{
	int z = 0;
	z = x + y;//进行加法运算
	return z;//将计算好的结果返回
}

int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);//输入变量,进行相加
	int ret = Add(a, b);//将a和b的值传递给Add函数
	printf("%d", ret);
	return 0;
}

4.形参和实参

在函数使用时,我们将变量分为两种,一种是实参,一种时形参!

4.1 实参

实参就是真实传递给函数的变量,在上述代码中,Add(a,b)中a和b就是实参。

4.2 形参

形参就是形式参数,我们用来接收实参而创建的变量。上述代码中Add(int x,int y)x和y就是形参。

4.3 形参和实参的区别

1)形参接受的是实参传过来的内容,只有在函数调用的时,传递过程才会发生

2)只有调用函数,形参才会被创建,才会向内存申请空间;如果不调用函数,形参就不会被创建也不会向内存申请空间。

3)形参和实参都是独立的,拥有各自单独的内存空间

我们可以通过代码观察上述代码a,b,x,y的地址,证明他们的空间是独立的

(观察代码利用监视窗口-----快捷键Ctrl+F10,进入函数内部按F11,后面会讲解VS使用技巧)

4.4 形参和实参的关系

我们可以通俗的认为形参是实参的一份临时拷贝

5.return语句

我们每一次写的代码中都出现了return,那么今天我们说说return使用的注意事项

1)return后面可以返回值,也可以返回表达式,如果是表达式,那么返回表达式计算后的值。

2)return后面可以什么都没有,但是这种只适用于返回类型是void的情况下。

3)return的返回值如果和返回类型不符,会被强制转化为返回类型的值,就比如return 3.14,但是返回类型是int,那么3.14就会被强制转换为3。

4)return语句执行后,后面的代码将彻底不会被执行。因此在编写代码时,要注意return的位置,一般都放最后,如果有特殊要求,就按照要求来。

6.数组做函数参数

在使用函数时,我们难免会遇到数组作为参数传递给函数,在函数内部进行修改。

例如:写⼀个函数将⼀个整型数组的内容,全部置为-1,再写⼀个函数打印数组的内容。

我们要重置数组的内容,就要将数组作为参数传递过去,将数组传递后,我要把每一个元素都重置,那我不知道你数组中到底有几个元素啊?那怎么办?--这是我们就需要将数组的元素传递过去。同时呢因为我只是重置,那就没有返回值,那么返回类型就是void。用代码敲一敲试试呢?

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
//写⼀个函数将⼀个整型数组的内容,全部置为-1,再写⼀个函数打印数组的内容。
void set_arr(int arr[], int sz)//创建同类型的变量来接受数组和元素个数
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		arr[i] = -1;//遍历数组,每一个都重置为-1
	}
}
void print_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	int arr[10] = { 0 };
	set_arr(arr, 10);
	print_arr(arr, 10);//传递数组和元素个数

	return 0;
}

6.1 数组传参的重点

1)函数的形参个数要与实参个数相匹配;

2)一维数组传参时,用形参接收时,数组的大小可以忽略;

      二维数组传参时,行可以省略但列不可以省略;

3)数组传参时不会创建一个新的数组;

4)形参和实参所操作的内容是同一个数组。

7.嵌套调用和链式访问

7.1 嵌套调用

在之前,我们讲过if语句的嵌套和循环语句的嵌套,那么函数也有是嵌套的。

例如:我想知道每一个月的天数,让我们用函数求解。

分析:1)我们知道只有2月份的天数是会发生变化的,所以我们得用一个函数去判断是否是闰年。

           2)每个月的天数我都要进行统计,那么又可以创建一个函数统计天数。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int is_leap_year(int arr[], int year)
{
	if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)//判断是否是闰年
		return 1;//是闰年就返回1
	else
		return 0;
}

int is_day_month(int arr[], int month)
{
	int day = arr[month];//判断每个月天数
	if (is_leap_year && month == 2)//判断是否是闰年2月份
		day++;
	return day;

}

int main()
{
	int arr[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int year = 0;
	int month = 0;
	scanf("%d %d", &year, &month);
	is_leap_year(arr,year);
	int day=is_day_month(arr,month);
	printf("%d", day);
	return 0;
}

这样就通过两个函数解决的每月天数的问题。

 7.2 链式访问

什么是链式访问?链式访问就是将一个函数的返回值作为另一个函数的参数,重复几次像链条串珠子一样串起来就是链式访问。

了解之后,我们来看一道有意思的题目

printf("%d",printf("%d",printf("%d",138)));

看到这道题目,我们自己先想想或者猜一猜这个结果是什么?在屏幕上会打印什么?

结合printf返回值我们再猜一猜?

好,现在我们来分析一下。

程序显示的结果也是一样的。这就是链式访问,你理解了吗?

8.函数的声明和定义

8.1 单文件

什么是函数声明?函数声明通俗来说就是我告诉编译系统有这么一个函数,你别给我报警告。

在上述代码的中,int Add(int x,int y)就是函数声明,有同学就问我能不能把它放在后面?当然是可以的。

有同学尝试了之后,就来问小编了?你说可以放后面,为什么要报错?----小编说的不对?

不不不。小编话还没有说完,我们在把函数放在后面时,因为编译器是一行一行的往下解读的,当我解读到Add函数,我往前面找,欸没找到,编译器就报警告了。

这是因为我们没有对函数进行声明。当我们在前面进行声明时,我们看看编译器会不会报错?

这个时候编译器就没有再报类似的警告了。

总结:函数一定要先声明后使用

那么函数如何正确声明呢?

1)要有函数名;

2)要告知函数的形参个数;

3)形参类型(名字可以忽略);

4)函数的返回类型。

是不是很简单呢?动动小手操作一下吧~

8.2 多文件

在复杂的代码中,大佬们通常会把代码分装在几个文件中。

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

就拿加法运算函数来说,我们把它分装在不同的文件中。

9.static和extern

static和extern是C语言中的关键字。

static的意思是静态的,可以用来修饰:

1)局部变量

2)全局变量

3)函数

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

在讲解static和extern之前,我们先来了解两个知识:作用域和生命周期

什么是作用域?

对于局部变量来说:创建这个变量开始,这个变量作用的范围,就是局部变量。

对于全局变量:作用域则是整个工程。举个简单例子

在这里a是局部变量,它的作用范围就if语句的范围内;b是全局变量,作用范围则是整个工程。

那什么是生命周期?

生命周期就是一个变量从被创建,再到最后被销毁,作用的时间段就是生命周期。

对于局部变量:变量创建周期开始,出了作用域变量被销毁,生命周期结束。

对于全局变量:生命周期则是整个工程。

9.1 static修饰局部变量

 对比这两张图发现,除了多了static之外,其余都是一样的。那为什么他们的结果却不相同呢?

我们可以通过监视窗口来看!

你会发现当我们第二次进入循环时,i=6;这是为什么呢?这是因为static修饰局部变量i时,改变了i的属性,也就是改变了i的生命周期。

结论:static修饰局部变量时,改变了其生命周期,生命周期改变的本质时变量的存储类型被改变。局部变量时存放在栈区的,但是由于static的修饰,局部变量存放在了静态区。而在静态区,它的生命周期适合全局变量一样的。

9.2 static修饰全局变量

 extern是用来声明外部符号。比如add.c文件中的变量我想在test.c中使用,就需要用extern声明。

结论:static修饰全局变量时,extern再想使用时,编译器就会发出警告,这是因为static改变了全局变量的外部链接属性。全局变量是具有外部连接属性的,当我用static修饰时,外部链接属性被改为了内部链接属性,也就是说,x变量只能在本源文件(add.c)中使用,外部文件(test.c)无法使用。

9.3 static修饰函数

static修饰函数和static修饰全局变量他们的问题是一样的。

使用建议:一个函数只想在所在的源文件内部使用,不想被其他源文件使用,就可以使用修饰。

那么本期函数到这里就结束了,下一期我们来介绍一个热门的小游戏~敬请期待!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值