今天我们要讲C语言非常重要的一个知识模块——函数。
这里的函数不同于我们在数学中学习的函数概念,在计算机科学中,函数被定义为子程序,是一个大型程序中的某部分代码,由一个或多个语句块组成,它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。
目录
1.函数的定义
1.1 C语言中函数的分类
这一部分在上一篇文章有浅浅的提过啦,现在我们详细了解一下。
在C语言中,函数分为库函数和自定义函数。
1.2 库函数
先来讲讲库函数,像printf、scanf、strcpy等函数,就是C语言为了方便程序员进行开发而在C语言的基础库里提供的一系列库函数。
常见的库函数有:IO函数、字符串操作函数、内存操作函数、时间/日期函数、数学函数以及其他库函数。
这里给大家提供几个查阅和学习库函数的网站以及软件:
MSDN(Microsoft Developer Network)
www.cplusplus.com
http://en.cppreference.com(英文版)
http://zh.cppreference.com(中文版)
库函数的学习是漫长的,随着你编程能力的提升,需要实现的需求更多,你会慢慢了解并学习到更多的库函数。
1.3 自定义函数
显然,库函数能实现的功能是有限的,因此在C语言中,还有更重要的一类函数,那就是自定义函数。顾名思义,自定义函数就是由我们用户根据自己的实际需求来设计的函数,自定义函数和库函数一样,拥有函数名、返回值类型和函数参数。
我们来看看函数的组成:
void fun_name(para)
{
statement;
}
//void 函数的返回类型,这个是空类型
//fun_name 函数名
//para 函数参数
//statement 语句块
我们举个例子,写一个函数,能够找出两个整数中的最大值,代码如下:
#include<stdio.h>
int get_max(int x,int y)
{
if (x > y)
return x;
else
return y;
}
int main()
{
int a = 5;
int b = 6;
int max = get_max(a, b);
printf("%d", max);
return 0;
}
我们可以来看一下运行结果:
再举一个例子,写一个可以交换两个整型变量的函数,代码实现如下:
(代码内的注释在看完后面的ADD后再回来看)
//写法1
void Swap1(int x,int y)//创建类型为void、函数名为Swap1的函数,形参分别是x、y,类型都为整型
{
int tmp = 0;//创建第三个变量,作为中介来交换x和y的内容;
tmp = x;
x = y;
y = tmp;
}
//在Swap1中,实际上只是完成了对x和y内容的交换,并不影响外部的a和b
//写法2
void Swap2(int* px, int* py)//创建类型为void、函数名为Swap2的函数,形参分别是*px、*py,类型都为指针
{
//这里px、py分别存放着函数外部a和b的地址,因此可以对外部a和b的内容直接进行交换
int tmp = 0;
tmp = *px;
*px = *py;
*py = tmp;
}
int main()
{
int a = 5;
int b = 6;
Swap1(a, b);
printf("函数1:a = %d,b = %d\n", a, b);
Swap2(&a, &b);
printf("函数2:a = %d,b = %d\n", a, b);
return 0;
}
运行结果:
我们可以看到,函数1并不能完成我们交换两个整型变量的需求,而函数2却能够实现我们的需求,这是为什么呢?
后面我们讲到函数调用的时候会给大家详细讲讲。
1.4 函数的参数
在C语言中,函数的参数分为实际参数(实参)与形式参数(形参)
实际参数是我们真实传给函数的参数,参数可以是常量、变量、表达式和函数等,在函数调用时,这些实际参数必须拥有确定的值,以便传送给形参。
形式参数是函数名后面括号内的变量,形参只有在函数被调用时才实例化(分配内存单元),在函数调用结束后就会自动销毁,因此形式参数只在函数内有效。
上面的Swap1函数和Swap2函数中,x、y、*px、*py都是函数的形式参数。
1.5 函数的调用
函数的调用分为传值调用和传址调用
传值调用:因为函数的形参和实参是分别占据不同内存单元的,因此对形参的修改不会影响到实参。(Swap1无法实现需求的原因)
传址调用:这种调用方式是将外部实参的内存地址传递给函数的形参,这种传参方式能够使形参和实参建立起真正的联系,即能够通过在函数内部修改形参达到修改实参的目的。(Swap2的传参方式)
这里要给大家补充一点点关于内存地址与指针的知识,方便大家更好理解传址调用。
2.知识补充
2.1 内存与地址
在计算机中,数据的存储是需要内存的,内存是以字节为单位的连续编址空间,每个字节单元对应着一个编号,这个编号就是地址。比如一个int类型需要的存储空间是4个字节,一个char类型需要的存储空间是1个字节。通过地址可以直接访问到存放在该地址的数据,就像我们可以通过房号直接定位到某个房间一样。
2.2 初识指针
在C语言中,指针就是内存地址,指针变量是用来存放地址的变量,在Swap2中,px 和py就是两个指针变量,定义一个指针的格式如下:
//定义一个整型指针变量p
int a = 0;
int* p = &a;
大家如果不能理解也不用太担心,指针是C语言里掌握起来较为困难的一部分,后面会再给大家详细讲一讲的。
3.函数的嵌套调用和链式访问
3.1 函数的嵌套调用
顾名思义,就是在一个函数中调用另一个函数,我们可以举个例子来看一下:
#include<stdio.h>
void fun_1()//自定义第一个函数,打印一行hello,world
{
printf("hello,world!\n");
}
void fun_2()//自定义第二个函数
{
int i = 0;
for (i = 0; i < 3; i++)
{
fun_1();//调用fun_1,即嵌套调用
}
}
int main()
{
fun_2();
return 0;
}
在fun_2中,fun_1总共被调用了三次,那么程序的运行结果应该是打印三行hello,world!
我们一起来看一下运行结果:
注意:函数可以嵌套调用,但不能嵌套定义。
3.2 函数的链式访问
函数的链式访问,即把一个函数的返回值作为另一个函数的参数
我们举个例子:
#include<stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
// 1 2 3
//printf函数的返回值是打印在屏幕上的字符个数
return 0;
}
大家认为这段代码的运行结果会是什么呢?
我们一起来分析一下:首先,第三个printf会在屏幕上打印43,又因为43是两个字符,所以紧接着第二个printf就会将第三个printf的返回值(即2),打印在屏幕上,又因为2是一个字符,所以第一个printf就将第二个printf的返回值(即1)打印在屏幕上,那么程序的运行结果应该是在屏幕上打印4321,我们来运行一下看看结果:
4.函数的声明与定义
4.1 函数的声明
函数声明可以理解为告诉编译器有一个函数叫什么、返回类型是什么,但是具体存不存在,并不是由函数声明决定的;
函数的声明要出现在函数的使用之前;
函数的声明一般放在头文件中。
4.2 函数的定义
函数的定义就是函数的具体实现;
即完成整个函数的具体功能实现。
4.3 代码分文件书写
一般在做比较大的工程时,往往会将代码分文件书写,具体如下:
test.h的内容
放置需要的头文件、函数的声明
#include<stdio.h>
//#include<string.h>
//#include<stdlib.h>
int Add(int x,int y);
test.c的内容
放置函数的实现
#include "test.h"
//函数Add的实现
int Add(int x,int y)
{
return x+y;
}
目前我们写的都是一些比较简单、代码量少的程序,因此一个.c文件就足够我们使用了,随着我们学习的深入,以后会需要实现更多的需求,到时候就会用到分文件的书写形式了。
今天讲的都是一些函数的基础知识,后续会专门找几道题来巩固一下这部分知识,然后再给大家介绍一个有点难理解但很好用的方法,即函数递归,函数迭代应该也会一并讲,接下来学习的内容会越来越难哦,uu们一定要把之前的知识给学好,打好基础。
今天的内容就到这里啦
如果你觉得这篇文章对你有帮助的话,麻烦动动小手点个赞哦~
你们的喜欢就是我创作的动力oWo