第6章 函数与宏定义.ppt_第1页
第6章 函数与宏定义.ppt_第2页
第6章 函数与宏定义.ppt_第3页
第6章 函数与宏定义.ppt_第4页
第6章 函数与宏定义.ppt_第5页
已阅读5页,还剩89页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、,C程序由一个主函数main()和任意个函数组成。由主函数调用其他函数,其他函数也可以互相调用,且同一函数可以被一个或多个函数调用任意次。以此完成下面两个功能:,(1) 使程序具有“ 积木”功能。(模块化),(2) 使程序具有“重构”功能,main() a() b() c() ,a() a1() b() b1() b2() c() c1() ,a1() b1() b2() c1() ,1) 除main外, 其它为标准库函数、用户自定义函数, 标准库函数:由系统提供,用户可调用。 用户自定义函数:由用户按语法规则编写。,C程序由一个main和任意个函数组成。,在C语言中,函数的含义不是数学中的函

2、数关系,而是一个处理。它可以进行数值运算、信息处理、控制决策,即把一段程序的工作放在函数中进行。,2) 除main函数外,其它函数可相互调用,3)函数不可嵌套定义,具有全局性、平行性,4)函数分为有参与无参函数,5)程序从main开始执行,最后又回到main函 数结束。,库函数简介,系统自带的标准库函数根据不同的功能作用放在不同的头文件中。,stdio.h 用于标准输入/输出 math.h 用于数学计算 ctype.h 用于字符处理 string.h 用于字串处理 time.h 用于时间/日期的处理 dir.h 用于控制目录和路径 graphics.h 用于图形操作,dos.h,bios.h,

3、 用于接口处理,库函数的作用,(1) 使程序更具有“ 独立性”和“ 可移植”性。,(2) 使用时只需在程序的开头加上一条语句:,#include ,根据使用的函数来确定,(3) 使程序具有“ 模块化”。,6.1 函数概念,6.1.1 函数定义,存储类型符 类型标识符 函数名(形参列表),形参说明, 说明部分 执行部分 ,存储类型符 类型标识符 函数名(形参说明表), 说明部分 执行部分 ,例 : 求二数之最大值 int max (x, y) int x, y; int z; z=xy? x:y; return (z); ,本函数中出现了return, 语句,其返回值一般与函数类型一致.,int

4、 max (int x,int y) int z; z=xy? x:y; return (z); ,6.1.2 函数声明和调用,调用函数时应对被调用函数进行声明(说明),使系统知道本函数将要用到某个函数及它的类型,以便主调函数值作处理。(函数名相当于一变量,但应有所区别),函数声明的一般形式: 存储类型符返回值类型符 函数名( 形参说明表); 如: int min(int x,int y);,一、 函数的声明,例: 求二实数之和。 main ( ) float add( ); float a, b, c; scanf(%f, %f, ,函数声明,以下几种情况可省略对被调函数的声明:,(1) 当

5、返回值为整型、字符型。,(2) 在调用之前定义函数。,(3) 在整个文件的开头定义函数。,(1)无返回值的情况 函数名(实参表列); (2)有返回值的情况 变量名函数名(实参表列);,二、 调用方式,例:,int max (int x, int y) int z; z=xy? x:y; return (z); ,main( ),int a, b, c;,scanf(%d, %d, ,c=max(a, b);,printf(max=%dn, c); ,上例函数调用中参数传递过程为:,c=max(a, b); 在main主函数中,max(x, y),int x, y;, int z;,z=xy?

6、x:y;,return(z);,a,b,c,x,y,z,10,15,15,10,15,15,例 阅读下列程序,写出运行结果,swap(x, y),int x, y;,int t;,t=x; x=y; y=t;,printf(x=%d, y=%dn, x, y);,main( ), int a, b;,scanf(%d, %d, ,if (ab) swap(a, b);,printf(a=%d, b=%dn, a, b); ,a,b,x,y,t,15,19,15,19,15,19,15,函数被调用时,临时分配单元给形参,调用完毕, 这些单元被释放。,实参: 出现在调用函数中的参数。,形参: 出现

7、被调用函数中的参数。,6.1.3 函数传值方式,一、 函数参数,实参可为表达式, 其传递为值传递。,实参可为常 量、变量或表达式,实参与形参类型必须一致。,在被定义的函数中,必须指定形参的类型,可在形参说明部分说明,也可在形参表列中说明。如,如:int max(int x, int y), ,注:,二、函数返回值语句,或 return 表达式;,通过函数调用使用函数能得到一个确定的值,这就是函数的返回值。,说明: (1) 函数的返回值由return获得;一个函数中可以有一个以上的return语句,执行到哪一个return语句,则哪一个语句起作用。,return (表达式);,1、函数无返回值的

8、情况,2、函数有返回值的情况,return;,如:#include stdio.h,main( ), int n=4, x=2, s;,s=fun(x, n);,printf(s=%dn, s); ,fun(int x, int n), if (n=1) return(x);,else return(xfun(x, n1); ,不加类型说明的函数,一律按int型处理。,(2) 函数返回值的类型,就是定义函数时指定函数值的类型。,如:int max(x, y),char letter(c1, c2),double f(a, b),函数值为整型,函数值为字符型,函数值为双精度型,(3) 当函数值的

9、类型与return语句中表达式类型不一致时,以函数类型为准。对数值型数据,可以自动进行类型转换。即函数类型决定返回值的类型。(这种方法初学者最好不用),(4) 若被调函数中没有return语句,将不带加一个确定的、用户希望的函数值。但实际上,并非不带回值,而只是不带回有用的值,带回的是一个不确定的值。,main ( ),a=printstar( );,b=print_message( );,c=printstar( );,printstar( ),printf(*n); ,print_message( ),printf( How do you do!n);,int a, b, c;,print

10、f(a=%d, b=%d, c=%dn, a, b, c); ,如:,则运行结果为:,* * * * * * * * * * * * How do you do!* * * * * * * * * * * *,a=16, b=15, c=16,(5) 关于main函数的参数,一般情况下,main函数均无参数,但这并不表示main 函数必须为无参函数,在必要时,main函数也可以带有参数。,(6) 实参传递给形参时,实参的计算有的系统自左至右,有的自右至左。,例main() int i=2,p; p=f(i,+i); printf(%d,p); int f(int a,int b) int c;

11、 if(ab) c=1; else if(a=b) c=0; else c=1; return(c); ,我们现在通用的Turbo C是按自右至左的顺序求实参表的值,所以上机运运行结果为0。,(1) 若实参表的求值顺序为自左至右,则函数调用相当于f(2,3);,这时,运行结果为 :1,(2) 若实参表的求值顺序为自右至左,则函数调用相当于f(3,3);,这时,运行结果为 :0,说明:,三、 函数调用,函数不能嵌套定义,但可嵌套调用,主函数调用其他函数,其他函数也可以互相调用。,例 编写求组合数的函数。 组合数的计算公式为:,/*求阶和乘*/ unsigned long fac(unsigned

12、 int k) unsigned long f=1; unsigned int i; for (i=1; i=k; i+) f=f*i; return f; ,/*求组合数*/ unsigned long comb(unsigned int n, unsigned int m) unsigned long c; c=fac(m)/(fac(n)*fac(m-n);,#include main() unsigned int n,m; unsigned long c; scanf(“%u,%u”, ,从程序中可以看到:,(2)二个函数的定义均在主函数之前,所以在主函数中不必再对它们进行声明(说明)

13、;,(1)在定义函数时,函数fac、comb是互相独立的;,(3)程序从主函数main()开始执行,首先执行的scanf( ),输入数,然后调用comb(n,m),调用comb的过程中,要调用fac( )。fac( )的调用是嵌套在函数comb( )的调用中进行的 ,其嵌套调用过程如下:,6.2 变量作用域及存储类型,1、局部变量,凡在函数(含main 函数)内部定义的变量称为局部变量。,局部性: 局部变量仅在函数内部有效,它包括:,一、变量作用域,根据变量的有效作用范围,变量可分为局部变量和全局变量。,2. 形式参数;,3. 在复合语句中可定义仅复合语句中有效的临时变量。,不同的函数可具有同

14、名的变量, 它们占不同的内存单元, 互不影响。,1. 函数中定义的变量;,2、全局变量,一个源文件中,在所有函数之外定义的变量为全局变量。,有效性: 自定义位置开始至文件结尾全部有效。,char f2(x,y); int c, y; int i, j; main ( ) ,例: int p=1, q=5; float f1(a) int a; int b, c; char c1, c2;,p,q的作用范围,c1, c2的作用范围,1. 全局变量所作用到的函数,相当于这些函数的公共变量。于是,当一个函数对其值进行改变后,另一个函数使用该变量的值亦相应改变。好处: 函数之间值传递。,2. 不要随意

15、使用全局变量。一是始终占据内存单元;二是由于函数依赖于外部定义的变量,减少了通用性。,3. 不在全局变量作用域内的函数。若要使用全局(外)变量,需在函数体内加上extern保留字于以说明。,4. 全局和局部变量同名时,局部变量有效。,例:分析m作为全局变量和局部变量在程序中各个部分时的值。,其运行结果为:,m2=13 /*全局变量的m的值*/,m1=3 /*局部变量m的值*/,3 /*函数调用的结果*/,float f1 (x) int x; extern int a, b; int a0; b= 1 main ( ) ,a, b作用域,例:,二、 变量的存储类型,1、变量的存储类别,C语言特

16、有的方式。表达了一个变量存在的时间。,静态存储变量: 存放于静态存储区,在程序整个运行过程中,始终占据固定的内存单元。,动态存储变量: 存放于动态存储区,根据程序的运行状态(如:函数调用)而临时分配的单元,且单元并不固定。,以上为两大类,又分为四种具体形式,2. 静态(static)变量,3. 寄存器型变量,4. 外部(extern)变量,前面学习的局部、全局变量均以上述方式中的一种形式存储。,1. 自动型变量(auto),局部变量既可以静态方式 , 又可以动态方式存储。 动态方式: auto int a, b;,2、局部变量存储形式,则: a, b为自动型,存入动态区。在该函数被调用时才分配

17、单元,函数调用结束时释放。,auto一般省略。以前用到的变量均为auto型, (除加static说明的之外)。,(1) 若定义时赋初值,则程序运行中仅在第一次调用时赋初值,第二次调用不再赋初值,而是使用上一次调用的值。,则: a, b存入静态区。 函数中的a, b始终占据固定存储单元。,静态方式: static int a, b;,int fac (n) int n; static int f=1; f=fn; return(f); main ( ) int i; for (i=1; i=5; i+) printf (%d!=%dn, i, fac(i); ,例:求n!,运行结果为: 1!1

18、2!2 3!6 4!24 5!120,每一次调用fac(i),打印一个i!,同时保留这个i!的值以便下次再乘(i+1)。,(2) 若不赋初值,则系统置初值0,而动态变量不赋初值则值不确定。,当动态局部变量在一个函数中反复被用达到数百次以上时,为了提高效率,可将其存入寄存器中(有限个),不存入内存的动态区中。,说明方式 register int i, j=1;,不可多, 一般13个,必要时使用。,2、全局变量存储形式,在函数外部中定义,它们一定存放在静态存贮区中。,全局变量即可被本文件中各函数用,亦可被其它源文件中的函数引用。,(1) 只被本文件中的函数引用,全局变量本身一定是存放在静态区的。但

19、若加上 static. 即:,static int a, b; float f1(x) int ,则表明a,b只被本文件中各函数引用,即使与其它文件中的全局变量同名,也互不影响。,(2) 可被其它文件中的函数引用,int a; main( ) ,extern int a; fac(x) int x z=a ,文件f1.c,文件f2.c用到f1.c 中的a,f2.c中的extern在函数外说明,在函数内说明已叙述过。,存储类别,总结,函数内,函数外,作用域,存在性,作用域,存在性,auto ,register ,static 局部 ,static 外部 , ,不加 static 全局(外部),(

20、只限本文件),6.3 内部函数与外部函数,函数本身在一个文件中为全局的。即一个文件中定义的函数可被该文件的所有其它函数引用。,一、内部函数只能在本文件中调用,但函数能否被其它文件中的函数所引用呢?,为此分为: 内部函数、外部函数,static 类型标识符 函数名(形参表),例: static int max (a, b) int a, b; ,则该函数max只能被本文件中的其它函数引用,而不能被其它文件中的函数引用。即如果在不同的文件中有同名的内部函数,将互不干扰。,既可被本文件中的函数调用,也可被其它文件中的函数调用。,extern 类型标识符 函数名(形参表),一般系统在调用外部函数的函数

21、中用extern说明外部函数。,二、外部函数,extern 可省略,例: p110。,/*file1.c*/ #include int mod(int a, int b); extern int add(int m, int n); main() int x,y,result; scanf(“%d%d”, ,/*file2.c*/ extern int add(int m, int n) return (m+n); ,首先要生成两个源文件(file1.c 和file2.c)。再构造project文件,在编辑状态下,编辑一个后缀为prj的文件(文件名可由用户选择,如:ff.prj)此文件内容如下

22、: file1 file2,后缀.c可有可无,file1,file2顺序无关,如是果file1.c和file2.c不在一个目录中时,可在project文件ff.prj中给出各自的路径。 选择project功能项,在project name输入ff.prj。 然后按F9,产生相应的执行文件ff.exe 。,递归调用:,6.4 递归函数设计和调用,在函数内调用函数本身,称为函数的递归调用。函数直接调用本身,称为直接递归。函数调用其他函数 ,其他函数又调用了本函数,称为间接递归。,直接调用 int f(x) int x; int y, z; z=f (y); ,间接调用 int f1 (x) int

23、 x; int y, z; z=f2 (y); ,int f2 (t) int t; int a, b; a=f1 (y); ,以上仅给出了递归的概念.,显然: 上述例子会无限递归 (无限执行)。所以, 在递归调用时都有条件限制。,即: 条件成立,调用递归,否则结束。,一个最常用的例子: 求n!,1. 从数学上定义,2. 程序 float fac(n) int n; float f; if (n0) printf(input error!n); else if (n= = 0 n= =1) f =1; else f =nfac(n 1); return (f); ,main ( ) int n

24、; float y; printf(input a integer! ); scanf (%d, ,3. 执行过程: 设输入n5,main ( ) f=5fac(4); ,fac(4) f=4fac(3); return f; ,fac(3) f=3fac(2); return f; ,fac=4!,n=4,n=3,fac=3!,fac(2) f=2fac(1); return f; ,fac(1) f=f (1); return f; ,n=1,fac=1,n=2,fac=2!,可简化表示为,当变成机器代码时,将其拉成直线(线性程序代码)。,6.5 编译预处理,目的: 为了简化程序的编写,提

25、高程序的可移植性、可读性、模块化。 C语言设计了若干命令编译预处理命令,并可出现于程序当中。,处理流程: 第一次编译扫描时,将预编译命令处理完,然后再进行正式编译,生成目标代码。,1. 宏定义命令;,有三种类型的预处理命令:,为了与C语句区别,这些命令均以“ #”开头。,2. 文件包含命令;,3. 条件编译命令。,6.5.1 宏定义,简化程序的书写,提高可读性、可移植性。,一、不带参数的宏,# define 标识符 字符串表达式,宏定义分为: 无参数和有参数二种形式。,例: #define PAI 3.14159 #define Array_size 500 int mArray_size;,

26、实际处理时: 用“ 字符串表达式”替换程序中的标识符(宏名),从开始定义的位置至文件结尾,但允许提前终止。用命令(#undef标识符),#define M 10.5 main ( ) # undef M ,1. 宏定义的作用域:,# include ;,# define PAI 3.14159,main( ), float r, s;,r=2.0;,s=PAIrr;,printf(s=%5.3fn, s);,#undef PAI,r=3.0;,s=PAIrr;,printf(s=%5.3fn, s);,运行结果: s=12.566,s=28.274,如果在程序中提前终止宏定义,则编译时会提示:

27、,undefined symbol PAI in function main,#define PI 3.14159 #define R 3.0 #define L 2R PI #define S PI R R main prinf(L=%fn S=%fn , L, S); ,2. 可以嵌套定义, 即可引用已定义的宏名。,注意: 双引号“ ”中的L不被替换。,结论: (1)凡程序中常用到的字符序列,如:常数, 公式,均可用宏定义。,(2) 经常会改变的数据可用宏定义。,结果: L=18.84954 /*L=23.0 3.14159*/ S=28.27131 /*S=3.141593.0 3.0*

28、/,二、带参数的宏,形式: #define 宏名(参数表) 字符串 #define S(r ) 3.14159 r r S(3.0) /*相当于: 3.14159 3.0 3.0*/ S(4.0) /*相当于: 3.14159 4.0 4.0*/ ,合适于字符串表达式序列中有变化的字符,将这部分字符可定义为参数。,例1: r23.1415r r 为字符序列, 但r是可变的。,main( ) int x, y, b, z; x=3; y=4; b=x+y; z=f(x+y); 编译后: z=f(x+y) 变成: x+y*b 结果为: z=31 (这个结果有可能与初始设想不一致),初始想法有可能是

29、:,若有:f(x+y) 则应有:(x+y)b,若有:f(x+y+z) 则应有:(x+y+z)b,例2: #define f(a) a*b,1. 宏展开实质为严格的字符替换,把参数看一种 “ 字符串”,所以定义宏时要考虑实参(替换)的各种可能, 防止出现二义性。 如程序例1中:若有#define s( r) 3.14159rr 则S(a+b)的结果为: 3.24159a+ba+b 为避免出现错误的结果,可将宏定义修改为: #define S( r) 3.1415926( r)( r),宏定义时必须注意以下几个方面:,2. 宏定义与函数是完全不同的概念,(1)目的 :宏调用是为了减少书写量和提高运

30、行速度;函数调用为了实现模程序设计。 (2)代码:宏调用展开后的代码是嵌入源程序中,代码是增加的 ;函数调用没用展开的问题,函数体的代码都不增加。 (3)参数:宏定义不需说其类型 ;函数定义必须说明其类型 。 (4)工作 时间:宏调用在编译预处理时完成宏展开工作 ;而函数调用在程序 序运行过程中执行。 (5)存储空间:宏调用不存在内存分配问题;函数可能需分配临时空间以存放函数调用之结果。,例: printf中经常有 n , %d 或程序中经常有printf( please input a number )等。则可用宏定义代替,如:,3. 灵活运用宏定义, 可使程序书写简化.,例:,# incl

31、ude,# define EN printf(please input number:n),#define NL printf( n) #define PRINT(F, V) printf (“F=%dt,V),#define INPUT_ PROMPT printf(),6.5.2 文件包含,C语言允许在一个文件预编译时,将另一个文件原封不动地包含进来。,(2) 当一组符号常量的定义为公用时,可作为一个独立的文件被其它文件包含。,目的: (1) 当函数不在一个文件中,既可用link连接编译后的obj文件,也可用“ 文件包含”方式。,格式: # include 文件名,# include f2

32、.c main ( ) ,例:,f1.c,f2.c,预编译后:,: main ( ) ,f1.c,为f2.c中的内容,再进行正式编译,1. 一个include只能写一个文件,包含多个文件需多个include,(1) 只在f1.c中用#include. #include f2.c #include f3.c,(2) 分别在f1.c, f2.c中用#include 在f1.c中: #include f2.c 在f2.c中: #include f3.c,2. 当f1.c包含f2.c,而f2.c又包含f3.c时,有两种包含形式:,3. 一般用.h扩展名命名被包含文件。 h为head之意以与一般.c文件

33、区别。如系统stdio.h.系统.h文件在子目录INCLUDE下。,4. #include后的文件名既可用 , 也可用,两者区别: “ ”首先在当前目录中找,然后再去标准目录中找。 只在标准目录(include目录)中找。,5. 被包含文件和包含文件最后形成一个文件。因此,全局变量作用域为整个文件。不必用extern说明。,6.5.3 条件编译,有时, C程序的一部分语句是否起作用要根据一定的条件,即由条件而决定是否起作用。,例如: (1) 调试程序时,需加多条printf,但调试完则要一一删除。,(2) 有的语句也视条件而决定其具体形式。,形式1 # ifdef 标识符 程序段1 # else 程序段2 # endif,功能 当标识符在此之前被定义过 (即用#define定义过),则编译程序段1,否则编译程序段2。,其中: 程序段即可为语句组,也可为命令行。,条件编译命令的三种形式:,例: 为了调试程序,加上以下条件 编译 #ifdef DEBUG printf(x=%d, y=

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论