第七章函数
【本章要求】
1、 C语言程序中函数的基本概念 2、 函数定义和声明的方法
3、 函数调用方法,尤其是类型匹配问题
4、 值传递方式下,函数形式参数和实际参数的变化 5、 函数返回值类型的一致性问题 6、 函数嵌套调用和递归调用的过程
7、 局部变量、全局变量、静态变量的作用域和生存期
7.1 模块化程序设计和C语言程序组成
7. 1. 1模块化程序设计
将重复使用的程序,设计成能够完成一定功能的可供英他程序使用(调用)的相对独立的功 能模块。它独立存在,但可以被多次调用,调用的程序称为主程序。
高级语言程序设计中的基本方法,即自顶向下、逐步细化和模块化。使用独立模块化的优点 有:
1、 消除重复的程序行; 2、 使程序容易阅读; 3、 使程序开发过程简化; 4、 可以在其他程序中重用; 5、 使C语言得到扩充。
独立模块由顺序、选择、循环这3种基本结构所组成。
由于模块是通过执行一组语句来完成一个特定的操作过程,因此模块又称为“过程”,执行 一个过程就是调用一个子程序或函数模块。
结构化程序设计的基本思想是“自顶向下、逐步求精”,即将一个教大的程序按其功能分成 若干个模块,每个模块具有单一的功能。
7. 1.2 C语言程序的组成
一个较大的程序一般应分为若干个程序模块,每一个模块用來实现一个特定的功能。所有的 高级语言屮都有子程序这个概念,用子程序实现模块的功能。在c语言屮,子程序的作用是rh函 数完成的。一个c程序可由一个主函数和若干个函数构成。由主函数调用其他函数,其他函数也 可以互相调用。同一个函数可以被一个或多个函数调用任意多次。
【说明】
1、 一个C程序有且只有一个主函数main。
2、 C程序的执行从main函数开始,调用其他函数后流程回到main函数,在nudn函数中结 束
整个程序的运行。
3、 一个C程序由一个或多个源程序文件组成。这样可以分别编写、分别编译,提高调度效
率。一个源文件可以为多个C程序公用。
4、 一个源程序文件由一个或多个函数组成。一个源程序文件是一个编译单位,而不是以函 数
为单位进行编译。
5、 所有函数都是平行的,即在定义函数时是互相独立的,一个函数并不从属于另一函数, 即函数不能嵌套定义。
6、 一个函数可以调用其他函数或者本身,即函数间可以嵌套调用,但任何函数均不能调用 main函数。
7.1. 3函数分类
(一) 从用户使用的角度看,函数有两种:
1、 标准函数,即库函数。这是由系统提供的,用户不必自己定义这些函数,可以直接使用 它
们。
2、 用户自己定义的函数。用以解决用户的专门需要。
(二) 从C语言函数兼有其他语言中的函数和过程两种功能,函数有两类:
1、 有返回值函数。有返回值函数被调用执行完成后将向调用者返回一个执行结果,这一结 果称为函数返回值。
2、 无返回值函数。无返回值函数用于完成某项特定的处理任务,执行完成后不向调用者返 回函数值。由于函数无须返回值,用户在定义此类函数时可指定它的返回为“空类型”,说明符 为 “void ”。
(三) 从函数的形式看,函数分为两类:
1、 无参函数。在调用无参函数时,主调函数并不将数据传送给被调用函数,一般用来执行 指定的一组操作。无参函数可以带回或不带回函数值,但一般以不带回函数值的居多。
2、 有参函数。在调用函数时,主调函数和被调用函数之间有数据传递。也就是说,主调函 数可将数据传给被调用函数使用,被调用函数中的数据也可以带冋来供主调函数使用。
7.2库函数及预处理命令
7. 2.1 C语言常用库函数
一、 数学函数
数学函数用于数学计算:
#include \
二、 字符函数和字符串函数
字符函数和字符串函数用于将字符按其ASCII码进行分类, 调用字符函数:
#include ''ctype.h\
调用字符串函数时,要求在源文件屮包含:
#include \
三、 输入输出函数
输入输出两数用于完成输入输出功能:
#include “ stdio.h^
7. 2. 2预处理命令
C提供预处理功能主要有以下3种: 1、 宏定义 2、 文件包含 3、 条件编译 一、宏定义
(一)不带参数的宏定义
用一个指定的标识符(即名字)来代表一个字符串,它的一般形式为:
#deline标识符字符串
例如:#define PI 3.1415926
用一个简单的标识符代替一个长的字符串,这个标识符称为“宏名”,在预编译吋将宏名替 换成字符串的过程称为“宏展开”。#define是宏定义命令。
【例7?1】使用不带参数的宏定义
# include
void main ( ) { float 1, s, r, v;
printf (\H); scanf (H%r\\ &!*); 1 = 2.0*PI*r; s = PI*r*r;
v = 4.0/3 *PI*r*r*r;
printf (Hl = .4f\\ns = .4f\\nv = .4f\\nM,l ,s, v); } 【说明】
1、 宏名一般习惯用大写字母表示,以便与变量名相区别,但并非规定,也可用小写字母。 2、 使用宏名代替一个字符串,可以减少程序屮重复书写某些字符串的工作量。
3、 宏定义是用宏名代替一个字符串,也就是作简单的置换,不作正确性检查。如果把#define PI 3.1415926中的数字1写成小写字母1,预处理时照样代入,不管是否符合用户原意,也不管含 义
是否有意义。
4、 宏定义不是C语句,不必在行末加分号。
5、 #define命令出现在程序中函数的外面,宏名的有效范围为定义命令之后到本源文件结 束。 6、 可以用#undcf命令终止宏定义的作用域。
7、 在进行宏定义时,可以引用已定义的宏名,可以层层置换。
【例7?2】在宏定义中引用已定义的宏名
#include
#define R 3.0
#define PI 3.1415926 #define L 2*PI*R # define S
void main()
printf (HL = %f\\n S=%f\\n— S ); }
PI*R*R
8、 对程序中用双撇号括起来的字符串内的字符,即使与宏名相同,也不进行置换。
9、 宏定义是专门用于预处理命令的一个专用名词,它与定义变量的含义不同,只作字符替
换,不分配内存空间。
(二)带参数的宏定义
带参数的宏定义不是进行简单的字符串替换,还要进行参数替换。其定义的形式为:
#define宏名(参数表)字符串 【例7.3】使用带参数的宏
#include
float a, area; a 二 3.6;
area = S(a);
printf (Hr = %f \\n area = %f \\n'\\ a, area);
}
【说明】
1、 对带参数的宏的展开只是将语句中的宏名后面括号内的实参字符串代替#define命令行中 的形参。如果有#define S(r)) PI*r*r这样的宏定义,则以下语句:
area = S(a+b);
它等价于:
area = PI*a + b * a + b;
请注意在a + b外血没有括号,而原意是希望得到
area = PI* (a + b) * ( a + b);
为了得到这个结果,应当在定义时,在字符串中的形式参数外面加一个括号。即
#define S(r) PI* (r) * (r)
2、 在宏定义时,在宏名与带参数的括号之间不应加空格;否则将空格以后的字符都作为替 代
字符串的一部分。例如,如果有
#define S (r) PI*r*r
被认为S是符号常量(不带参数的宏名),它代表字符串“(r) PI*r*r\如果在程序中有语 句
area = S(a);
则被展开为
area = (r) PI*r* (a);
3、 带参数的宏定义与函数是不同的,主要有:
(1) 函数使用时,先求岀实参表达式的值,然后代入形参。而使用带参数的宏知识进行简 单的字符替换。
(2) 函数调用是在程序运行吋处理的,为形参分配临吋的内存单元。而宏展开则是在编译 前
进行的,在展开时并不分配内存单元,不进行值的传递处理,也没有“返回值”的概念。
(3) 对函数屮的实参和形参都要定义类型,二者的类型要求一致,如不一致,应进行类型 转
换。而宏不存在类型问题,宏名无类型,它的参数也无类型,只是一个符号代表,展开时代入 指定字符串即可。宏定义吋,字符串可以是任何类型的数据。
(4) 调用函数只可得到一个返回值,而用宏可以设法得到几个结果。
(5) 使用宏次数多时,宏展开后源程序变长,因为每展开一次都使程序增长,而函数调用
不会使源程序变长。
(6) 宏替换不占运行时间不占运行时间,只占编译时间。而函数调用则占运行时间(分配
单元、保留现场、值传递、返回)。
二、“文件包含”命令include的使用
文件包含的一般形式如下:
# include \文件名‘‘
或:^include 〈文件名〉
该控制行一般放在源文件的起始部分,它告诉编译程序把由〈文件名〉标识的文件的整个内容 來替换该控制行。该文件提供了C语言标准输入输出中的有关常量、变量和函数原型的信息,它 们一般被称为头文件(headfile),用扩展名.h表示。
【说明】
1、 一个include命令只能指定一个被包含文件,如果要包含n个文件,要用n个文件,要 用n
个include命令。
2、 如果文件1包含文件2,而文件2屮要用到文件3的内容,则可在文件1屮用两个include
命令分别包含文件2和文件3,而且文件3应出现在文件2之前,即在filel.c中定义.
3、 在一个被包含文件屮又可以包含另一个被包含文件,即文件包含是可以嵌套的。
4、 在^include命令中,文件名可以用双引号或尖括号括起来。用双引号或尖括号宋括起 文件是有区别的:若用双引号,则系统先在引用被包含文件的C源程序所在的文件目录中寻找, 若找不到,
再按系统指定的标准方式寻找其他目录;而用尖括号 则仅查找按系统标准方式指 定的目录。
5、 被包含文件与其所在的文件,在预编译后已成为同一个文件。
7.3函数的定义和调用
7. 3.1函数的定义
一、 无参函数的定义形式
类型标识符 函数名()
{声明部分
语句}
二、 有参函数定义的一般形式
类型标识符 函数名(形式参数表列) {声明部分
语句}
例如:int max (int a, int b)
{ int c;
z= a>b?a: b; return (c); }