版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第6章函数与宏定义第6章函数与宏定义本章主要内容1.函数的概念
①函数的声明和调用②函数的传值方式2.变量的作用域和存储类型3.内部函数与外部函数4.递归函数的设计和调用5.预处理6.综合范例6.1函数的概念模块化程序设计的核心:函数设计。
重要概念:①把解决问题的方案设计成一个个独立的模块;②程序通过调用模块功能来解决问题。③这些模块通过函数来实现,又称为函数模块。
④每一个函数具有独立的功能,程序通过各模块之间的协调工作完成复杂的程序功能。6.1函数的概念
C语言的函数分为两类:①系统定义的标准函数,又称为库函数。函数声明一般是放在系统的include目录下以.h为后缀的头文件中,如在程序中要用到某个库函数,必须在调用该函数之前用#include<头文件名>命令将库函数信息包含到本程序中。常用的库函数请查阅附录C。②自定义函数。按功能要求设计和定义函数,设计规则:功能单一化。
C语言程序设计的核心之一:自定义函数。6.1函数的概念6.1.1函数的定义
自定义函数的形式:[存储类型符][返回值类型符]函数名([形参说明表]){
<函数语句体>
}
1.[存储类型符]指的是函数的作用范围,只有两种形式:static和extern。
static说明的函数称为内部函数,只能作用于其所在的源文件,
extern说明的函数称为外部函数,可被其他源文件中的函数调用。
缺省情况为extern。说明6.1函数的概念[存储类型符][返回值类型符]函数名([形参说明表]){
<函数语句体>
}2.[返回值类型符]指的是函数体语句执行完成后,函数返回值的类型。如int,float,char等。
若函数无返回值,则用空类型void来定义函数的返回值。
默认情况为int型(有些编译器不支持默认情况)。3.函数名由任何合法的标识符构成。
建议将函数名的命名与函数内容有一定关系。说明6.1函数的概念4.[形参说明表]是一系列用逗号分开的形参变量说明。如:intx,inty,intz表示形参变量有3个:x,y,z。类型都是int型的。
注意:不能直接写成:intx,y,z。5.在古典式函数定义中,[形参表]只要形参名,不要类型名。
如:x,y,z表示形参变量有3个。形参的类型通过形参说明语句说明。
如:intx,y,z。
[形参说明表]或[形参表]都可以缺省,缺省时表示函数无参数。[存储类型符][返回值类型符]函数名([形参说明表]){
<函数语句体>
}说明6.1函数的概念6.函数语句体是放在一对花括号{}中,由局部数据类型描述和功能实现两部分组成。
函数返回语句通常用返回语句来结束函数的调用。
两种形式的返回语句:①函数无返回值的情况
return;②函数有返回值的情况
return(表达式的值);[存储类型符][返回值类型符]函数名([形参说明表]){
<函数语句体>
}说明6.1函数的概念例如,编写自定义函数abs_sum(),求两个任意整数的绝对值的和。intabs_sum(intm,intn){
if(m<0)
m=−m;
if(n<0)
n=−n;
return(m+n);}或直接调用系统函数来实现:intabs_sum(intm,intn){
return(abs(m)+abs(n));}注意:函数abs()的声明在头文件math.h。6.1函数的概念6.1.2函数的声明和调用
通常情况下,自定义的函数在使用之前要先进行函数声明,才能在程序中进行函数调用。1.函数声明
函数声明语句的形式:
[存储类型符][返回值类型符]函数名([形参说明表]);如:intabs_sun(intm,intn);2.函数调用
函数定义好后,若不通过函数调用,不会发挥任何作用。
函数调用是通过函数调用语句来实现的。①函数无返回值情况:
函数名([实参表]);6.1函数的概念②函数有返回值的情况:
变量名=函数名([实参表]);注意:变量名的类型必须与函数的返回值类型相同。
函数调用时,会去执行函数中的语句内容,函数执行完毕后,回到函数的调用处,继续执行程序中函数调用后面的语句。例如:
…
intx=5,y=−10;
intz;
…
z=abs_sum(x,y);
/*函数调用*/
…6.1函数的概念6.1.3函数的传值方式函数传值的4个要点:①对于带有参数的函数,调用函数时,将实参表中每一个实参的值对应地传递给每一个形参变量;②形参变量接收到实参传来的值后,会在内存临时开辟新的空间,保存形参变量的值;③函数执行完毕时,释放临时开辟的内存空间;④形参的值在函数中不论是否发生变化,实参变量的值均不会发生变化。
自定义函数在程序中的使用方式有两种形式:①先进行函数声明,再进行函数调用,函数定义放在main函数的后面。②先定义函数,再调用函数(不需要在调用前函数声明)。6.1函数的概念例1:编写程序,通过调用函数intabs_sum(inta,intb),求任意两个整数的绝对值的和。第1种形式:先进行函数声明;
再进行函数调用;
最后给出函数定义。/*example6_1.c*/#include<stdio.h>intabs_sum(intm,intn);/*函数声明*/intmain(){intx,y,z;scanf("%d%d",&x,&y);
z=abs_sum(x,y);/*函数调用*/printf("|%d|+|%d|=%d\n",x,y,z);return0;}intabs_sum(intm,intn)/*函数定义*/{if(m<0)m=-m;if(n<0)n=-n;returnm+n;}6.1函数的概念第2种形式:先定义函数;
再调用函数。
(不需要在调用前函数声明)
思考:
1.怎样求任意3个整数的绝对值之和?
参考答案:example6_2.c
2.怎样求任意2个数的乘积?
参考答案:example6_3.c/*example6_1a.c*/#include<stdio.h>intabs_sum(intm,intn)/*函数定义*/{if(m<0)m=-m;if(n<0)n=-n;returnm+n;}intmain(){intx,y,z;scanf("%d%d",&x,&y);
z=abs_sum(x,y);/*函数调用*/printf("|%d|+|%d|=%d\n",x,y,z);return0;}6.1函数的概念重要概念函数传值调用的性质:实参的值在函数调用前和函数调用后不会发生变化。如有程序:
分析程序的运行结果。
/*传值调用示例:*/#include<stdio.h>voidexchange(inta,intb){ inttemp; printf("3---a=%d,a=%d\n",a,a); temp=a; a=b; b=temp; printf("4---a=%d,a=%d\n",a,a);}intmain(){ intx=10,y=20; printf("1---x=%d,y=%d\n",x,y);
exchange(x,y);
/*函数调用*/ printf("2---x=%d,y=%d\n",x,y);return0;}6.2变量的作用域和存储类型
根据变量的作用域不同,可分为局部变量和全局变量两种。1.变量的作用域变量的作用域:变量起作用的范围。①局部变量:在函数内部或某个控制块的内部定义的变量。
局部变量的作用域:函数内部。
作用:增强了函数模块的独立性。②全局变量:在函数外面定义的变量称为全局变量。
全局变量的作用域:从该变量定义的位置开始,直到源文件结束。
作用:在同一文件中,所有函数都可以引用全局变量。增强了各函数间数据的联系。
局部变量和全局变量的作用域如图所示:6.2变量的作用域和存储类型例2:阅读程序【例6-4】的程序,
了解变量作用域。
请注意区分局部变量和全局变量的作用域。思考和分析程序:example6_4.c6.2变量的作用域和存储类型2.变量的存储类型
变量的存储类型指的是变量的存储属性,它说明变量占用存储空间的区域。
在内存中,供用户使用的存储区由程序区、静态存储区和动态存储区3部分组成。
变量的存储类型:auto、register、static和extern4种。①auto型变量存储在内存的动态存储区;②register型变量保存在寄存器中;③static型和extern变量存储在静态存储器。6.2变量的作用域和存储类型
局部变量的存储类型默认值为auto型。
全局变量的存储类型默认值为extern型。注意:①一般情况下,常用auto型register型定义局部变量。
②static型既可作为局部变量,又可作为全局变量。作为局部变量时,局部变量的值将被保留,若定义时没有赋初值,则系统会自动为其赋0值;作为全局变量时,其有效范围为它所在的源文件,其他源文件不能使用。6.2变量的作用域和存储类型例3:设计一个函数:longfac(intn),用来计算正整数的阶乘,编写程序进行测试。分析:由于计算机对变量的字节长度分配有限,整型变量的最大值是一定的,因此,目前计算整数的阶乘只能针对较小的整数。假定要计算1~5的阶乘。
算法的核心思想:对于任意正整数n,如果知道(n−1)!,则n!=n×(n−1)!。可在函数中定义一个static型变量,用来保存每一次阶乘的计算结果。6.2变量的作用域和存储类型程序如下:/*example6_5.c*/#include<stdio.h>longfac(intn)/*计算n!的函数*/{staticintf=1;f=f*n;returnf;}intmain(){inti;for(i=1;i<=5;i++)
printf("%d!=%ld\n",i,fac(i));return0;}6.2变量的作用域和存储类型
思考:1.这个程序能计算任意正整数的阶乘值吗?为什么?2.如果不用循环,能否直接求出某个整数的阶乘?6.3内部函数与外部函数
自定义的函数有两种:内部函数和外部函数。1.内部函数若函数的存储类型为static型,则称其为内部函数(内部函数又称为静态函数),它表示在由多个源文件组成的同一个程序中,该函数只能在其所在的文件中使用,在其他文件中不可使用。
内部函数的声明形式:
static<返回值类型><函数名>(<参数>);例如:
staticintSelffile();2.外部函数若函数的存储类型定义为extern型,则称其为外部函数,它表示该函数能被其他源文件调用。
函数的默认存储类型为extern型。6.3内部函数与外部函数例4:外部函数的应用示例。下面的程序由3个文件组成:file1.c、file2.c、example6_6.c。
在file1.c、file2.c中分别定义了两个外部函数;在example6_6.c中可以分别调用这两个函数。1.file1.c/*file1.c外部函数定义*/externintadd(intm,intn){return(m+n);}2.file2.c/*file2.c外部函数定义*/externintmod(inta,intb){return(a%b);}6.3内部函数与外部函数3.example6_6.c/*example6_6.c调用外部函数*/#include<stdio.h>externintmod(inta,intb); /*外部函数声明*/externintadd(intm,intn); /*外部函数声明*/intmain(){intx,y,result1,result2,result;printf("Pleaseenterxandy:\n");scanf("%d%d",&x,&y);result1=add(x,y); /*调用file1中的外部函数*/printf("x+y=%d\n",result1);<见后页>6.3内部函数与外部函数<接前页>if(result1>0)
result2=mod(x,y); /*调用file2中的外部函数*/result=result1-result2;printf("mod(x,y)=%d\n",result2);printf("(x+y)-mod(x,y)=%d\n",result);return0;}
思考和分析程序的运行结果。6.3内部函数与外部函数
关于程序的几点说明(1)在程序file1.c、file2.c中的函数定义可以不需要extern加以说明,默认为外部函数。(2)在example6_6.c中对外部函数的声明也可以不用extern加以说明,默认为外部函数。(3)由多个源文件组成一个程序时,main()函数只能出现在一个源文件中。(4)由多个源文件组成一个程序时,有3种连接方式:①将各源文件分别编译成目标文件,得到多个目标文件(.obj后缀),然后用连接命令把多个.obj文件连接起来。
Turboc的连接接命令为tlink
例如:tlinkexample6_6.obj+file1.obj+file2.obj
结果:生成一个example6_6.exe文件。6.3内部函数与外部函数②建立项目文件(.prj后缀或.dsw后缀),具体操作可参阅各种C语言集成开发环境说明。③使用文件包含命令。请参阅本章6.6节。(5)如果将file1.c或file2.c中的extern改成static,则主程序在编译时无法通过。(6)在程序file1.c或file2.c中,也可以互相调用其外部函数。6.4递归函数的设计和调用
函数的嵌套调用:函数中的语句可以是对另一个函数的调用。
函数嵌套调用的过程如图所示:
嵌套调用属于一种线性调用关系,函数执行完成后,返回到原调用点,继续执行函数调用下面的语句。6.4递归函数的设计和调用
函数的递归调用两种递归形式:直接递归调用和间接递归调用。①直接递归调用
直接递归:
函数直接调用自身函数。②间接递归调用
间接递归:
函数互相调用对方函数。
递归形式:6.4递归函数的设计和调用
可能出现的陷阱
递归调用陷入无限递归状态。
递归的限制并不是所有的问题都可以设计成递归函数。为了避免错误的发生,对于递归函数的设计,有严格的数学模型。
递归函数的条件递归函数模型在数学上必须具备以下两个条件。①问题的后一部分与原始问题类似。②问题的后一部分是原始问题的简化。
难点递归函数设计的难点是建立问题的数学模型,有了正确的递归数学模型,很容易写出递归函数。6.4递归函数的设计和调用例5:编写程序,要求从键盘输入一个正整数n,计算n!。分析:n!的数学表达式为:显然,它满足数学上对递归函数的两个条件:①(n−1)!与n!是类似的;②(n−1)!是n!的简化。可用递归函数longfac(intn)实现求n!。6.4递归函数的设计和调用
函数longfac(intn)的算法流程图及函数实现如图所示:
思考和分析程序:example6_7.c6.4递归函数的设计和调用例6:Fibonacci数列的组成规律为:0,1,1,2,3,5,8,13,21,…。
编写程序,求Fibonacci数列第i项的值(0≤i≤40)。分析:数列的组成规律为:第1项为0,第2项为1,从第3项开始,数列每1项的值为前两项的和。Fibonacci数列用数字模型表达为:fibonacci(1)=0(i=1)fibonacci(2)=1(i=2)fibonacci(i)=fibonacci(i−1)+fibonacci(i−2)(i=3,4,5,…)从第3项开始,该Fibonacci数列的数学表达式满足递归函数的两个必要条件,设计递归函数longfibonacci(intn)来求得数列中第n项的值。6.4递归函数的设计和调用
函数longfibonacci(intn)的算法流程图:
思考程序存在的缺陷:
提示:①只能求出指定项的值;②对输入的值没有限制;……函数程序:longfibonacci(intn){if(n==1||n==2)returnn-1;elsereturnfibonacci(n-1)+fibonacci(n-2);}
程序的参考答案:example6_8.c6.5预
处
理
预处理的作用:向编译系统发布信息或命令,告诉编译系统在对源程序进行编译之前应做些什么事。
所有编译预处理都是以“#”号开头,单占源程序中的一行,放在源程序的首部。
编译预处理不是C语句,行未不必加分号。
C语言提供的预处理指令主要有3种:
宏定义、文件包含和条件编译。6.5预
处
理6.5.1宏定义
两种宏定义:不带参数的宏和带参数的宏。
宏定义的作用:简化程序的书写。1.不带参数的宏
定义形式:
#define宏名
字符串说明:①define是关键字,表示宏定义。②宏名用标识符表示,为区别于变量,宏名一般采用大写字母。如:#definePI3.14159③宏的作用:将程序中的宏名用字符串替换。④宏名的有效范围是从定义命令之后,直到源程序文件结束,或遇到宏定义终止命令#undef为止。例7:阅读程序example6_9.c,了解不带参数的宏的作用。6.5预
处
理2.带参数的宏
定义形式:
#define宏名(参数表)字符串说明:①字符串应包含有参数表中的参数。②宏替换时,将字符串中的参数用实参表中的实参替换。例8:阅读程序example6_10.c,了解带参数的宏定义的作用。分析程序运行结果。
特别提示:
有参数的宏定义与函数是完全不同的两个概念。6.5预
处
理6.5.2文件包含
将另一个文件的全部内容包含到程序中,编译时,用包含文件的内容取代该预处理命令。
文件包含命令的一般形式:
#include<包含文件名>或:#include"包含文件名"注意:①include是命令关键字,一个include命令只能包含一个文件。②<>表示被包含文件在标准目录(include)中。③""表示被包含文件在指定的目录中,若只有文件名不带路径,则在当前目录中,若找不到,再到标准目录中寻找。④包含文件名可以是.c源文件或.h头文件,如:
#include<stdio.h>
#include"myhead.h"
#include"D:\\myexam\\myfile.c”6.5预
处
理
文件包含的作用:①将多个源文件拼接在一起。如:有文件file2.c,其内容都是自定义的函数。另有文件file1.c,该文件有main函数。如果在file1.c程序中要调用file2.c中的函数,可采用文件包含的形式:#include"file2.c"②在对file1.c进行编译时,系统会用file2.c的内容替换掉文件包含命令#include"file2.c",然后再对其进行编译。③要注意区分外部函数与文件包含的区别。它们都是可以在某个程序中用到另一个文件中的函数,但使用的方法有所不同。6.5预
处
理6.5.3条件编译及其他条件编译:对程序源代码有选择地进行编译。
ANSI标准定义的C语言预处理命令:
#error
#if
#else
#elif
#endif
#ifdef
#ifndef
#line
#pragma条件编译命令阅读教材6.6综合范例例7:编写函数longexp(inta,intb),计算a的b次幂(ab)。要求a与b的值均为大于零的正整数(a>0,b>0)。
分析:
要计算ab,实际上是计算b个a相乘;
例如:23=2×2×2;54=5×5×5×5;
很显然,满足递归算法;
实际算法设计可以是非递归算法也可以是递归算法。
思考和分析[例6-13]的程序:
example6_13.c--------非递归算法example6_13a.c--------递归算法6.6综合范例例8:编写程序,从键盘输入一个正整数number,通过函数intreverseDigits(intnumber)将number的数字反向返回。为简单起见,number的取值范围为1~9999。例如:整数:4629,函数的返回值:9264;
整数:3027,函数的返回值:7203。程序以输入−1作为结束。分析:问题的核心是将数字number反向后,返回一个新的数reverse。算法关键:分离number的每1位数字;使number的最低位成为reverse的最高位。6.6综合范例函数intreverseDigits(intnumber)的算法流程如图所示:
程序参考答案:
example6_14.c是否还有其他算法?思考6.6综合范例例9:编写程序,求方程f(x)=ax2+bx+c在某区间的定积分:。为了程序的通用性,要求从键盘输入方程f(x)的系数a、b和c的值以及积分区间的上下限upper、lower的值。分析:积分的结果为图(a)所示阴影部分的平
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年包头轻工职业技术学院单招职业适应性测试题库附参考答案详解(培优)
- 2026年南阳职业学院单招职业技能测试题库及完整答案详解一套
- 2026年包头铁道职业技术学院单招职业倾向性测试题库及答案详解一套
- 2026年兰州航空职业技术学院单招职业技能测试题库含答案详解(培优)
- 2026年内蒙古伊克昭盟单招职业倾向性测试题库带答案详解(典型题)
- 2026年南阳科技职业学院单招职业技能考试题库含答案详解(培优b卷)
- 2026年六盘水幼儿师范高等专科学校单招职业倾向性考试题库含答案详解(综合卷)
- 2026年兰州外语职业学院单招职业技能考试题库带答案详解(黄金题型)
- 2026年北海康养职业学院单招职业倾向性测试题库附参考答案详解(突破训练)
- 2026年内蒙古民族幼儿师范高等专科学校单招职业技能测试题库及答案详解参考
- 2026年中国银发经济深度报告:8万亿市场下的细分赛道机会
- 藤县介绍教学课件
- 2026年部编版新教材语文小学三年级下册教学计划(含进度表)
- 2026年贵州毕节织金县事业单位招聘工作人员拟聘用易考易错模拟试题(共500题)试卷后附参考答案
- 大疆社招在线测评题库
- 安责险业务发展培训课件
- (正式版)DB51∕T 3320-2025 《地震灾害损失精细化预评估规范》
- 期末冲刺备考总动员校长在教师会议上讲话:五字诀精实盯严稳
- 秋季学期末会议分管德育工作副校长讲话:勇担职责强德育凝心聚力助发展
- 2025年数字化货运物流平台运营项目可行性研究报告
- 2026年中国头皮健康行业发展展望及投资策略报告
评论
0/150
提交评论