目录

1、函数的概述

1.1 为什么要使用函数 

1.2 什么是函数 

1.3 函数的分类 

2、函数的定义 

3、函数的参数及传递方式 

3.1 函数的参数 

3.2 函数参数的传递方式 

4、函数的调用 

4.1 函数调用方式

4.2 函数的声明 

4.3 函数的嵌套调用

4.4 函数的递归调用 

5、内部函数和外部函数 

5.1 内部函数  

5.2 外部函数 

6、main 函数 


1、函数的概述

1.1 为什么要使用函数 

如果编写的程序越来越长,有成百上千行语句,若只用一个函数 main来实现,那么 main()的代码就会冗长、数据量大,即造成编写、阅读的困难,又对调试和维护带来了诸多不便。那么怎么调试才能最方便、最简洁、最有效? 要解决这些问题,就要使用本次将要介绍的函数来做。结构化程序设计的思想是把一个大问题分解成若干个小问题,每一个小问题就是一个独立的子模块,以实现特定的功能。在 C 程序中,子模块的作用就是由函数完成的。

1.2 什么是函数 

一个 C 源程序可以由一个或多个文件构成(C 文件后缀是.c),一个源程序文件是一个编译单位。一个源文件可以由若干个函数构成,也就是说,函数是 C 程序基本的组成单位。每个程序有且只能有一个主函数(main),其他的函数都是子函数。主函数可以调用其他的子函数,子函数之间可以相互调用任意多次。 

1.3 函数的分类 

1. 从函数定义的角度,可以将函数分为标准函数用户自定义函数

(1)标准函数: 标准函数也称库函数,是由 C 系统提供的,用户无须定义,可以直接使用,只需要在程序前包含有函数的原形声明的头文件便可。比如:前面所用到的 printf()函数和scanf()函数等都属于库函数。每个系统提供的库函数的数量和功能不同,也有一些基本的函数是共同的。

(2)用户自定义函数: 由用户根据自己的需要编写的函数,对于用户自定义函数,不仅要在程序中定义函数本身,而且在主调函数中还必须对该被调函数进行类型说明,才能使用。

2. 从有无返回值的角度,可以将函数分为有返回值函数无返回值函数 

(1)有返回值函数: 该类函数被调用执行完毕后,向调用者返回一个执行结果,称为函数的返回值。由用户自定义的有返回值的函数,必须在函数定义和函数声明中明确返回值的类型。例:

int Add()
{
    int x =0;
    int y =0;
    return (x+y);
}

(2)无返回值函数:无返回值函数不需要向主调函数提供返回值。通常用户定义此类函数时需要指定它的返回值类型为 “ 空 ” (即 void 类型)。该类函数主要用于完成某种特定的处理功能任务,如输入、输入、排序等。例如:

void Input_Data()
{
    ......
}

3.  从函数的形式看,可以分为有参函数无参函数

(1)无参函数:无参函数即在函数定义声明和调用中均不带参数。在调用无参函数时,主调函数并不将数据传递给被调函数。此类函数通常用来完成指定的功能,可以返回或不返回函数值。例:

例1:
int Add()
{
    int x =0;
    int y =0;
    return (x+y);
}
例2:
void Input_Data()
{
    ......
}

(2)有参函数: 有参函数,就是在函数定义和声明时都有参数。在函数调用时也必须给出参数。即当主调函数调用被调用函数时,主调函数必须把值传递给形参,以供被调函数使用。例:

例1:
int Add(int x,int y)
{
    return (x+y);
}
例2:
void Input_Data(struct Node* stu)
{
    ......
}

 注意:程序不仅可以调用系统提供的标准库函数,而且还可以自定义函数。在程序设计语言中引入函数的目的,是使程序更便于维护、逻辑上更加清晰、减少重复编写代码的工作量、提高程序开发的效率。

2、函数的定义 

作为 C 程序的基本组成部分函数来说,是具有相对独立性的程序模块,能够供其他程序调用,并在执行完自己的功能后,返回调用它的程序中。函数的定义实际上就是描述一个函数所完成功能的具体过程。

函数定义的一般形式是:

函数类型 函数名([类型说明 变量名[,类型说明 变量名......]])
{
    函数体
}

( [ ] )方括号括起来的部分是可选的,用可根据自身需求选择。

关于函数说明: 

(1) 函数名必须符合标识符的命名规则(即只能由字母、数字和下划线组成,开头只能为字母或下划线),且同一个程序中函数不能重名,函数名用来唯一标识一个函数。函数名最好能够见名知意,一见到它就能了解其基本功能。如函数名为 Input_Data,一眼便知是输入数据的函数。

(2) 函数类型规定了函数返回值的类型。如:函数 Add 是 int 型的,函数的返回值也是 int 型的,函数的返回值就是 retum 语句后面所带的值,变量x、y的类型是 int 型。也就是说函数值的类型和函数的类型应该是一致的,它可以是C语言中任何一种合法的类型。

注意:如果函数不需要返回值(即无返回值函数),则必须用关键字 void 加以说明。默认的返回值类型是int型。 如:

double max(int a,int b)/*函数返回值类型为double型*/

void max(int a,int b)/*函数无返回值*/

max(int a,int b)/*函数返回值类型不写,表示默认为int型*/

(3) 函数名后面圆括号括起来的部分称为形式参数列表(即形参列表),如果有多个形式参数,应该分别给出各形式参数的类型,并用逗号隔开,该类函数称为有参数函数。例如:

 int Add(int x,int y,float f)/*有参函数,有3个形参,中间用逗号隔开,每个参数分别说明类型*/

 如果是形参列表为空,则称为无参函数,定义形式为:

类型说明 函数名()
{
    函数体
}

 注 意:函数名后面的括号的形参列表可以为空(即可以没有参数),但圆括号一定要有。有参函数与无参函数的唯一区别就是括号里面有没有形参,其他都是一样的。

(4)函数体是由一对花括号“ { } ”括起来的语句序列,用于描述函数所要进行的操作。函数体又包含了说明部分和执行部分。其中,说明部分是对函数体内部所用到的各种变量类型进行定义和声明,对被调用的函数进行声明;执行部分是实现函数功能的语句序列。

 注 意:函数体一定要用大括号括起来,像主函数的函数体也是用大括号括起来的。

(5) 还有一类比较特殊的函数是空函数,即函数体内没有语句。调用空函数时,空函数表示什么都不做。如:

void empty()
{
    
}

使用空函数的目的仅仅是为了“占位置 ”。因为在程序设计中,往往会根据需要确定若干个模块分,别由一个函数来实现,而在设计阶段,只设计最基本的模块,其他一些功能要在以后需要时再补上。那么在编写程序的开始阶段,就可以在将来准备扩充功能的地方写上一个空函数占一个位置,以后用一个编好的函数代替它。利用空函数占位,对于较大程序的编写、调试及功能扩充非常有用。

(6) C 程序中所有的子函数都是平行的,不属于任何其他函数,它们之间可以相互调用。但是函数的定义不能包含在另一个函数的定义内,即函数不能嵌套定义。下面这种函数定义的形式是不正确的:

int function_one(int x,int y)/*第一个函数的定义*/
{
    ......
    int function_two(int a,int b)/*第二个函数的定义*/
    {
        ......
    }
    ......
}

(7)在函数定义中,可以包含对其他函数的调用,后者又可以调用另外的函数,甚至自己调用自己,即递归调用

 注意:C程序的函数一类是标准函数(库函数),一类是用户自定义的函数,一般来讲用户定义的函数多,标准函数再多也就几十个,而用户则可根据需要定义很多函数。

3、函数的参数及传递方式 

3.1 函数的参数 

函数的参数有两类:形式参数(简称形参)和实际参数(简称实参)。函数定义时的参数称为形参,形参在函数未被调用时是没有确定值的,只是形式上的参数。函数调用时使用的参数称为实参。

函数的参数说明: 

①定义函数时,必须说明形参的类型。

 ②函数被调用前,形参不占用内存的存储单元;调用以后,形参才被分配内存单元;函数调用结束后,形参所占用的内存也将被回收,被释放。

③ 实参可以是常量、变量、其他构造数据类型或表达式。调用时可写成:

Add(1,2);/*实参是常量*/
Add(x+y,x-y);/*实参是表达式*/

如果实参是表达式,先计算表达式的值,再将实参的值传递给形参。但要求它有确切的值因为在调用时是要将实参的值传递给形参的。

④ 实参的个数、出现的顺序和实参的类型,应该与函数定义中形参表的设计一一对应。如:定义函数时有两个整形的形参,调用它时,实参也要与它对应,两个整型的,而且多个实参之间要用逗号隔开。如果不一致,则会发生 “ 类型不匹配 ” 的错误。

注意:①形参只能是简单变量或数组,不能是常量或表达式。②对于特殊的字符型和整形可以通过的,是可以互相匹配的,必要的时候还需要进行类型的转换。 

3.2 函数参数的传递方式 

 实参和形参之间有什么关系?

前面已经讲过,形参只是一个形式,在调用之前并不分配内存。函数调用时,系统为形参分配内存单元,然后将主调函数中的实参传递给被调函数的形参。被调函数执行完毕,通过 return 语句返回结果,系统将形参的内存单元释放。由此可见,实参和形参的功能主要是作数据传递的。

C 语言规定,实参对形参的数据传递是 “ 值传递 ”,即单向传递,只是把实参的值传递给形参,而不能把形参的值再传回给实参。在内存当中,实参与形参是不同的单元,不管名字是否相同,因此函数中对形参值的任何改变都不会影响到实参的值。在函数调用过程中,形参只是对实参的一个拷贝,形参的值发生改变,并不会影响到实参的变化。 

4、函数的调用 

C程序总是从主函数 main() 开始执行,以 main() 函数体结束为止在函数体的执行过程中是通过不断地对函数的调用来执行的,调用者称为主调函数,被调用者称为被调函数。被调函数执行结束,从被调函数结束的位置再返回主调函数当中,继续执行主调函数后面的语句。如下图所示,调用一个函数的简单例子:

4.1 函数调用方式

函数调用的一般形式有两种: 

1. 函数语句 

当 C 语言中的函数只进行了某些操作而不返回结果时,使用这种形式,该形式作为一条独立的语句,如:

函数名(实参列表);/*调用有参函数,实参列表中有多个参数,中间用退号隔开*/
或者
函数名();/*调用无参函数*/

 2. 函数表达式

当所调用的函数有返回值时,函数的调用可以作为表达式中的运算分量参与一定的运算。 如:

value = Add(x,y);/*将Add()函数的返回值赋值给变量value*/

注意:一般void类型的函数使用函数语句的形式,因为void类型没有返回值;对于其他类型的函数,在调用时一般采用函数表达式的形式。

4.2 函数的声明 

为什么要进行函数声明? 

在学习变量时,要求遵循 “ 先定义后使用 ” 的原则,同样,在调用函数时也要遵循这个原则。也就是说,被调函数必须存在,而且在调用这个函数的地方,前面一定给出了这个函数定义,这样才能成功调用。如果被调函数的定义出现在主调函数之后,这时应给出函数的原形说明,以满足 “ 先定义后使用”的原则 ” 。 

函数声明的目的是使编译系统在编译阶段对函数的调用进行合法性检查,判断形参与实参的类型及个数是否匹配。 函数声明采用函数原型的方法。函数原型就是已经定义函数的首部。

有参函数的声明形式为: 

函数类型 函数名(形参列表);

 无参函数的声明形式为:

函数类型 函数名();

 注 意:函数声明包含函数的首部和一个分号“ ; ”,函数体不用写。有参函数声明时的形参列表只需要把一个个参数类型给出就可以了,如:int Add(int,int);函数声明可以放在所有函数的前面,也可以放在主调函数内调用被调函数之前。

函数声明三种情况下可以省略: 

(1) 被调函数定义在主调函数之前。

(2)被调函数的返回值是整型或字符型(整型是系统默认的类型)。

(3)在所有的函数的定义之前,已在函数外部进行了函数声明。

注意:如果被调函数是C语言提供的库函数,虽然库函数的调用不需要作函教声明,但必须把该库函数的头文件用#include 命令包含在源程序的最前面。例如,getchar()、putchar()、gets()、puts()等,这样的函数定义是放在stdio.h头文件中的只要在程序的最前面加上#include<stdio.h>就可以了。同样,如果使用数学库中的函数,则应该用#include<math.h>。

4.3 函数的嵌套调用

在 C 语言中,函数之间的关系是平行的,是独立的,也就是在函数定义时不能嵌套定义,即一个函数的定义的函数体内不能包含另外一个函数的完整定义。但是 C 语言允许进行套调用,也就是说,在调用一个函数的过程中可以调用另外一个函数。 

4.4 函数的递归调用 

如果在调用一个函数的过程中,又直接或者间接地调用了该函数本身,这种形式称为函数的递归调用,这个函数就称为递归函数。递归函数分为直接递归和间接递归两种。

①直接递归就是函数在处理过程中又直接调用了自己。 

②函数p调用函数q,而函数q反过来又调用函数p,称为间接递归。

 注意:这两种递归都无法终止自身的调用。因此在递归调用中,应该含有某种条件控制递归调用结束,使递归调用是有限的,有终止的。比如可以用 if 语句来控制只有在某一条件成立时才继续执行递归调用,否则不再继续。

5、内部函数和外部函数 

5.1 内部函数  

一个源文件中定义的函数只能被本文件中的函数调用,而不能被同一源程序其他文件中的函数调用,这种函数称为内部函数。 定义内部函数的一般形式是:

static 类型说明符 函数名([形参表])

其中,“ [ ] ”中的部分是可选项,即该函数可以是有参函数,也可以是无参函数。如果为无参函数,形参表为空,但括号必须要有。如:

static int Add(int x,int y)/*内部函数前面加 static 关键字*/
{
    ......
}

 说明:Add()函数只能被本文件中的函数调用,在其他文件中不能调用此函数。

内部函数也称为静态函数。但此处静态 static 的含义并不是指存储方式,而是指对函数的调用范围只局限于本文件,因此在不同的源文件中定义同名的内部函数不会引起混滑。通常把只由同一个文件使用的函数和外部变量放在一个文件中,前面加上 static 使之局部化,其他文件不能引用。

5.2 外部函数 

外部函数在整个源程序中都有效,只要定义函数时,在前面加上extern 关键字即可。其定义
的一般形式为: 

extern 类型说明 函数名(形参表)

 如:

extern int Add(int x,int y)
{
    ......
}

注意:因为函数与函数之间都是并列的,函数不能嵌套定义,所以函数在本质上都具有外部性质。因此在定义函数时省去了 extern 说明符时,则隐含为外部函数。可以说,前面例子中使用的函数都是外部函数。

如果定义为外部函数,不仅可被定义它的源文件调用,而且可以被其他的文件中的函数调用,即其作用范围不只局限于本源文件,而是整个程序的所有文件。在一个源文件的函数中调用其他源文件中定义的外部函数时,通常使用 extern 说明被调函数为外部函数。 

6、main 函数 

从开始学C语言,我们就一直使用 main 函数,都知道一个C 程序必须有,并且只能有一个主函数,C程序的执行总是从 main 函数开始的。 

main函数在使用过程中应该注意以下几点:

(1)main 函数可以调用其他函数,包括本程序中定义的函数和标准库中的函数,但其他函数不能反过来调用 main 函数。main 函数也不能调用自己。 

(2) 用到的 main 函数都没有在函数头中提供参数。其实,main 函数可以带有两个参数,其一般形式是:

int main(int argc,char *argv[])
{
    函数体
}

 其中:形参 argc 表示传给程序的参数个数,其值至少是1;而argv 则是指向字符串的指针数组。由于main函数不能被其它函数调用, 因此不可能在程序内部取得实际值。那么,在何处把实参值赋予main函数的形参呢?实际上,main函数的参数值是从操作系统命令行上获得的。当我们要运行一个可执行文件时,在命令行键入文件名,再输入实际参数即可把这些实参传送到main的形参中去。不过在平时编写程序的时候,基本上不会用到的,这里作了解一下。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐