




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第六章第六章 函数函数1.为什么要使用函数?函数如何定义和调用?为什么要使用函数?函数如何定义和调用?2.函数参数如何传递?如何获得函数的返回值?函数参数如何传递?如何获得函数的返回值?3.如何使用局部变量与全局变量?变量有几种存如何使用局部变量与全局变量?变量有几种存储类型?储类型? 4.C提供的预处理功能主要有哪几种?如何定义提供的预处理功能主要有哪几种?如何定义带参数的宏?带参数的宏? 本章内容6.1 问题的提出与程序示例问题的提出与程序示例6.2 函数的定义与调用函数的定义与调用6.3 函数的参数传递与返回值函数的参数传递与返回值6.4 变量的作用域和存储类型变量的作用域和存储类型6.
2、5 编译预处理编译预处理6.6 函数的应用举例函数的应用举例 * 6.7 函数的嵌套与递归调用函数的嵌套与递归调用6.1 问题的提出与程序示例为什么要使用函数?为什么要使用函数?大型程序多人合作大型程序多人合作模块化,功能清晰,便于编写、阅模块化,功能清晰,便于编写、阅读和调试读和调试减少重复编写的工作量,如减少重复编写的工作量,如#include long factorial(int x) /*定义函数定义函数factorial计算计算n的的阶乘阶乘*/ int i; long result=1; for(i=2;i=x;i+)result*=i; return result; /*将将re
3、sult作为作为factorial的函的函数值返回到主调函数中数值返回到主调函数中*/!() !nmmCnmnvoid main() int m, n; long c; printf(Please input m,n:); scanf(%d,%d, &m,&n); c=factorial(m)/(factorial(n)*factorial(m-n);/*调调用函数用函数factorial并作为运算对象并作为运算对象*/printf(“c=%ldn”,c); 例例2:double add(double a,double b) double s; s=a+b; return s;
4、 void main() x=add(3.0,4.0); for(y=0,i=1;i=5;i+) y=add(y,i);函数参数函数参数实际值代入实际值代入这是函这是函数返回数返回值值例例3:计算计算1!+2!+3!+n! 用函数完成用函数完成 #include “stdio.h” fun( int n) int s=0,t=1; int i; for(i=1;i=n;i+) t=t*i; s=s+t; return (s); void main( ) int n; int s=0; scanf(“%d”,&n); s=fun(n); printf(“s=%dn”,s); 6.2 函数
5、的定义与调用6.2.1函数的分类函数的分类函数定义的角度函数定义的角度(1)库函数)库函数 库函数是由库函数是由C系统提供的,可以直接使用它系统提供的,可以直接使用它们。如:们。如:scanf,printf,getchar,putchar等。等。(2)用户自定义函数)用户自定义函数 用户自定义函数就是由用户自己定义和设计用户自定义函数就是由用户自己定义和设计的函数。的函数。主调函数和被调函数之间数据传递的角度主调函数和被调函数之间数据传递的角度(1)无参函数)无参函数(2)有参函数)有参函数6.2 函数的定义与调用6.2.2函数的定义函数的定义 C语言中的语言中的函数和变量一样函数和变量一样,
6、具有一定的,具有一定的存储类型和存储类型和数据类型数据类型。一般情况下,函数也应先定义,后调用一般情况下,函数也应先定义,后调用 1.无参函数的定义形式无参函数的定义形式 类型说明符类型说明符 函数名(函数名( ) 声明部分声明部分 语句部分语句部分 例如,定义一个打印一行例如,定义一个打印一行语句的函数。语句的函数。void printfline( ) printf(This is a C program!n);6.2 函数的定义与调用2. 有参函数的定义形式有参函数的定义形式类型说明符类型说明符 函数名(形式参数表列)函数名(形式参数表列) 声明部分声明部分语句部分语句部分 例如:定义一个
7、例如:定义一个average函数,用来函数,用来计算两个整数的平均数。计算两个整数的平均数。float average(int x, int y) float result; /* 定义变量定义变量result*/result=(x+y)/2.0;return result; /*函数返回值函数返回值*/6.2 函数的定义与调用 有参函数注意事项有参函数注意事项: (1)为每一个参数指定数据类型,)为每一个参数指定数据类型, 如如 float average(int x, int y)。任何不指定参数。任何不指定参数类型的写法都是错误的。如:类型的写法都是错误的。如:float average
8、(int x, y)编译时会出错,参数编译时会出错,参数 y 未指定数据类型。未指定数据类型。 (2)不允许在函数内部再次定义与参数同名的)不允许在函数内部再次定义与参数同名的变量。但不同函数内部允许定义同名的变量。变量。但不同函数内部允许定义同名的变量。6.2 函数的定义与调用6.2.3函数的调用函数的调用1.函数调用的形式函数调用的形式函数调用的形式为:函数调用的形式为:函数名(实参表列);函数名(实参表列);或或函数名();函数名();【例6-2】编写程序计算两个整数的平均数。要求调用printfline和average函数。#include void printfline( ) /*函
9、数定义函数定义*/printf(This is a C program!n); float average(int x,int y) /*函数定义函数定义*/float result;result=(x + y)/2.0; return result;void main( )int a,b;float ave;printfline( ); /*调用调用printfline函数输出一行语句函数输出一行语句*/printf(Please input a,b:);scanf(“%d,%d”, &a,&b); ave=average(a,b); /*调用调用average函数求两个整数
10、函数求两个整数a和和b的平均数的平均数*/ printf(Average of %d and %d is %f.n, a, b, ave); 按函数调用在程序中出现的位置来分,按函数调用在程序中出现的位置来分,函数调用函数调用大致可大致可分为以下几种形式:分为以下几种形式: (1)函数语句函数语句:把函数作为语句来使用把函数作为语句来使用,一般不需要返回一般不需要返回值值,只是通过函数调用完成某些操作。只是通过函数调用完成某些操作。 如如printfline( ); (2)函数表达式函数表达式:函数作为表达式中的一项出现在表:函数作为表达式中的一项出现在表达式中,以函数返回值参与表达式的运算,
11、如达式中,以函数返回值参与表达式的运算,如 ave=average(a,b); (3)函数实参函数实参:函数作为另一个函数调用的实际参数:函数作为另一个函数调用的实际参数出现。这种形式是把该函数的返回值作为实参进行传送,出现。这种形式是把该函数的返回值作为实参进行传送,因此要求该函数必须是有返回值的因此要求该函数必须是有返回值的 6.2 函数的定义与调用6.2函数的定义与调用2.被调函数的声明被调函数的声明 函数声明的一般形式为:函数声明的一般形式为: 函数类型函数类型 函数名函数名(参数类型参数类型1, 参数类型参数类型2,); 或或 函数类型函数类型 函数名函数名(参数类型参数类型1 参数
12、名参数名1, 参数类型参数类型2 参数名参数名2,); 自定义函数自定义函数一般应一般应定义在前,调用在后定义在前,调用在后。如果用户在编制。如果用户在编制程序时都按这个规律来进行,则不需要进行函数的声明。程序时都按这个规律来进行,则不需要进行函数的声明。但由于但由于C语言中的函数定义是各自独立的。函数与函数之间语言中的函数定义是各自独立的。函数与函数之间的排放位置并没有先后顺序关系,只有调用与被调用的关的排放位置并没有先后顺序关系,只有调用与被调用的关系。当出现调用在前,定义在后的情况,系。当出现调用在前,定义在后的情况,即即被调函数放置被调函数放置在主调函数之后,则应在主调函数的适当位置,
13、在主调函数之后,则应在主调函数的适当位置,加上函数加上函数原型声明原型声明 6.3 函数的参数传递和返回值6.3.1 函数的参数传递函数的参数传递参数传递方式分为:参数传递方式分为:(1)值传递方式(单向传递)值传递方式(单向传递) 所谓所谓“值传递方式值传递方式”是指将实参的具体数值单向传是指将实参的具体数值单向传递给形参的一种方式。递给形参的一种方式。 注意:值单向传递是指只能由实参传递给形参,而注意:值单向传递是指只能由实参传递给形参,而不能由形参传递给实参。如下例中,只能由实参不能由形参传递给实参。如下例中,只能由实参a,b依次对应传递给形参依次对应传递给形参x,y,反之则不行。,反之
14、则不行。6.3 函数的参数传递和返回值例:例:#includevoid main()void swap(int x,int y); /* 函数原型说明声明函数原型说明声明*/ int a,b;a=2,b=6;printf(调用前:调用前:a=%d,b=%dn,a,b);swap(a,b); /*函数调用函数调用*/printf(调用后:调用后:a=%d,b=%dn,a,b);函数在后函数在后,调用调用在前在前,所以要在所以要在调用前加说明调用前加说明6.3 函数的参数传递和返回值void swap(int x,int y) /*函数定义函数定义*/ int temp;printf(交换前:交换
15、前:x=%d,y=%dn,x,y);temp=x;x=y;y=temp;printf(交换后:交换后:x=%d,y=%dn,x,y);6.3 函数的参数传递和返回值上例运行结果:上例运行结果:最后交换最后交换成功了吗成功了吗?为什么为什么?在语言中,数据只能从实参单向传递给形参,在语言中,数据只能从实参单向传递给形参,称为称为“按值按值” 传递,即当简单变量作为实参传递,即当简单变量作为实参时,用户时,用户不能不能在在函数函数中中改变改变对应实参的值对应实参的值6.3 函数的参数传递和返回值(2)地址传递方式)地址传递方式 所谓所谓“地址传递方式地址传递方式”是指将实参的地址传递给形参是指将实
16、参的地址传递给形参的一种方式。在函数调用时,通过对形参所指地址单的一种方式。在函数调用时,通过对形参所指地址单元中的内容进行操作,从而引起与形参指向同一地址元中的内容进行操作,从而引起与形参指向同一地址单元的实参的内容发生变化。但这种变化并不是由形单元的实参的内容发生变化。但这种变化并不是由形参传回实参引起的。参传回实参引起的。 采用这种方式时,实参可以是变量的地址、也可以是采用这种方式时,实参可以是变量的地址、也可以是后续章节要介绍的数组名、相关指针变量,形参通常后续章节要介绍的数组名、相关指针变量,形参通常是数组或指针变量。是数组或指针变量。6.3 函数的参数传递和返回值6.3.2 函数的
17、返回值函数的返回值在在C语言中,可以在被调函数内采用语言中,可以在被调函数内采用return语句获得函语句获得函数的返回值。一个函数利用数的返回值。一个函数利用return语句只能返回一个值。语句只能返回一个值。return语句的形式为:(以下两种写法均可)语句的形式为:(以下两种写法均可)return(表达式);(表达式);或或return 表达式;表达式;需注意的是:函数名前面的函数类型说明符是用来指定需注意的是:函数名前面的函数类型说明符是用来指定本函数返回值的数据类型。在进行函数定义时,应使本函数返回值的数据类型。在进行函数定义时,应使return语句中的表达式的类型与函数类型说明一致
18、。当语句中的表达式的类型与函数类型说明一致。当return语句中的表达式的类型与函数类型说明不一致时,语句中的表达式的类型与函数类型说明不一致时,则以函数类型说明为准,系统自动将则以函数类型说明为准,系统自动将return语句中的表达语句中的表达式的值转换为函数类型说明所指定的类型。式的值转换为函数类型说明所指定的类型。 #include void main() int average(int x,int y); /* 函数声明函数声明*/ int a,b; float ave; printf(Please input a,b:); scanf(“%d,%d”, &a,&b);
19、 ave=average(a,b); /*调用调用average函数求两函数求两个整数个整数a和和b的平均数的平均数*/ printf(Average of %d and %d is %f.n, a, b, ave); int average(int x,int y) /*将将average函数的类型改为函数的类型改为int型型*/float result;result=(x + y)/2.0; return result;本例中,因为函数本例中,因为函数average的返回类型为的返回类型为int型,所以要将型,所以要将return的实型值的实型值3.5转化为转化为int型。固结果为型。固结
20、果为3.000000当函数没有返回值时,为了使程序有良好的可读性并减当函数没有返回值时,为了使程序有良好的可读性并减少出错,在函数定义时,函数类型说明符应为少出错,在函数定义时,函数类型说明符应为void(“空类型空类型”)。)。注意:注意:(1)调用函数时,函数名不能有错。)调用函数时,函数名不能有错。(2)实参和形参要一一对应,个数一致,类实参和形参要一一对应,个数一致,类型匹配。型匹配。(3)函数必须先定义,后调用函数必须先定义,后调用,除非函数返,除非函数返回值类型为回值类型为int或或char。(4)函数可以自己调用自己,即递归调用。)函数可以自己调用自己,即递归调用。例:例:dou
21、ble add(double,double);函数说明的形式函数说明的形式类型名类型名 函数名(参数类型函数名(参数类型1,参数类型,参数类型2,) 对于用户定义的函数要对于用户定义的函数要“先定义,后使用先定义,后使用”的的规则。凡是未在调用前定义的函数,默认函数规则。凡是未在调用前定义的函数,默认函数的返回值为的返回值为int类型。类型。对于返回值为其它类型的函数,对于返回值为其它类型的函数,若把函数的定若把函数的定义放在调用之后,义放在调用之后,应该在调用之间对应该在调用之间对函数进行函数进行说明说明(称为函数原型说明)(称为函数原型说明)可放在所有函数可放在所有函数外部外部,在此后的所
22、有位置,在此后的所有位置上都可以上都可以调用调用该函数。该函数。也可放在某函数内的说明部分,则只有在也可放在某函数内的说明部分,则只有在某函数内能识别该函数。某函数内能识别该函数。71513114#include#includevoid main( ) float e,pi; double funpi(double t); scanf(“%lf”,&e); pi=funpi(e); printf(“pi=%fn”,pi);补充补充: 编写函数编写函数isprime(int a) 用来判断自变量用来判断自变量a是否为素是否为素数,若是素数,函数返回数,若是素数,函数返回1,否则返回,否则
23、返回0#include “math.h”int isprime (int ); void main()int x;printf(“Enter a integer number: ”);scanf(“%d”,&x);if(isprime(x) printf(“%d is primen”,x);else printf(“%d is not primen”,x); 编写函数编写函数isprime(int a) 用来判断自变量用来判断自变量a是否为是否为素数,若是素数,函数返回素数,若是素数,函数返回1,否则返回,否则返回0int isprime( int a) int i; for(i=2;
24、 i=(int)sqrt(double)a); i+) if(a%i=0) return 0; return 1;补充例子补充例子:编写函数编写函数myupper(ch),把把ch中的小写字母转换中的小写字母转换成大写字母作为函数值返回成大写字母作为函数值返回,其它字符不变其它字符不变,用字符用字符#结束结束输入。输入。#include char myupper(char);void main( ) char c; while(c=getchar()!=#) c=myupper(c); putchar(c);char myupper(char ch) if(ch=a & ch=z) c
25、h=ch-32; return ch;程序举例程序举例:补充补充: 编写函数编写函数sum( int n ) 求求 。 和值作为和值作为函数返回。函数返回。f(x)=x2+1#includeint sum (int);函数要先定义它的原型,后调用函数要先定义它的原型,后调用int f(int);void main( )int n,s;printf(“Enter a integer number: ”);scanf(“%d”,&n);s=sum(n);printf(“n=%d sum=%dn”,n,s); nxxf0)(int sum( int n) int x,s=0; for(x=0
26、; x=n; x+) s+=f(x); return s; int f(int x)return x*x+1; 补充补充: 用梯形法求函数用梯形法求函数f(x)=sin(x)在在0,1.5区间的区间的定积分,定积分,n选选100。近似公式。近似公式近似公式近似公式s=h*(f(a)+f(b)/2+ )#include “math.h”double integ(double a,double b) (函数先定义后函数先定义后调用调用)double s,x,h; int n=100,i; h=fabs(b-a)/n; s=(sin(a)+sin(b)/2.0; 11)*(nihiaffor(i=1
27、; i=n-1;i+) x=a+i*h; s=s+sin(x); s=s*h; return s;void main() double s; s=integ(0.0,1.5); printf(“s=%fn”,s);6.4 变量的作用域与存储类型6.4.1 变量的作用域变量的作用域: 1局部变量局部变量(1)在函数体内定义的变量,在本函数范围内有效,)在函数体内定义的变量,在本函数范围内有效,作用域局限于函数体内。作用域局限于函数体内。(2)在复合语句内定义的变量,在本复合语句范围)在复合语句内定义的变量,在本复合语句范围内有效,作用域局限于复合语句内。内有效,作用域局限于复合语句内。(3)有参
28、函数的形式参数也是局部变量,只在其所)有参函数的形式参数也是局部变量,只在其所在的函数范围内有效。在的函数范围内有效。 注注:(:(1)主函数中定义的变量只能在主函数中使用,不)主函数中定义的变量只能在主函数中使用,不能在其它函数中使用。同时,主函数中也不能使用其它函能在其它函数中使用。同时,主函数中也不能使用其它函数中定义的变量。(数中定义的变量。(2)不同函数中和不同的复合语句中不同函数中和不同的复合语句中可以使用(即定义)相同的变量名。可以使用(即定义)相同的变量名。因为它们的作用域不因为它们的作用域不同,程序运行时在内存中占据不同的存储单元,各自代表同,程序运行时在内存中占据不同的存储
29、单元,各自代表不同的对象,所以它们之间互不干预。不同的对象,所以它们之间互不干预。6.4 变量的作用域与存储类型 2全局变量全局变量全局变量也称为外全局变量也称为外部变量,它是在任部变量,它是在任何函数之外定义的何函数之外定义的变量。变量。它的有效范它的有效范围为从定义变量的围为从定义变量的位置开始到本源文位置开始到本源文件结束。件结束。例如:例如: int x,y,z;/* x,y,z均是全局变量均是全局变量 */ float f1(float a,float b) char ch1,ch2; /* ch1,ch2均是均是全局变量全局变量 */ int f2( int m) double t
30、,p;/* t,p均是全局变量均是全局变量 */ void main( ) 6.4 变量的作用域与存储类型全局变量和局部变量同名的应用例子。全局变量和局部变量同名的应用例子。 #include int a=3,b=5; /* a,b为为 变量变量*/ int max (int a, int b) /*a,b为为 变量变量 */ int c; c=ab?a:b; return (c); void main ( ) int a=8; /*a为为 变量变量 */ printf (max=%dn, max (a,b); 运行结果:运行结果:如果同一个源文件中,如果同一个源文件中,全局变量与局部变量全局
31、变量与局部变量同名,当局部变量有同名,当局部变量有效时,效时,同名的全局变同名的全局变量不起作用。量不起作用。 6.4 变量的作用域与存储类型6.4.2 变量的存储类型变量的存储类型动态存储方式和静态存储方式。动态存储方式和静态存储方式。根据这两类存储方式,变量又可具体分为四种:根据这两类存储方式,变量又可具体分为四种:自动变量自动变量(auto)、寄存器变量、寄存器变量(register)、静态变、静态变量量(static)、 外部变量外部变量(extern)。在在C语言中,每个变量和函数有两个属性:数据语言中,每个变量和函数有两个属性:数据类型和存储类型。定义变量的完整形式为:类型和存储类
32、型。定义变量的完整形式为:存储类型存储类型 数据类型数据类型 变量名变量名1,变量名,变量名2,;其中,其中,存储类型可以是存储类型可以是auto、register、extern或或static。6.4 变量的作用域与存储类型1auto变量变量auto说明符常常省略说明符常常省略,系统默认为没有存储类型说,系统默认为没有存储类型说明符的变量为自动存储类型的变量,前面各章中所明符的变量为自动存储类型的变量,前面各章中所有程序中所用到的各个变量,都未说明存储方式,有程序中所用到的各个变量,都未说明存储方式,都是自动变量。都是自动变量。在函数体内定义变量时,下面两种写法是等价的。在函数体内定义变量时
33、,下面两种写法是等价的。int x,y,z;auto int x,y,z;6.4 变量的作用域与存储类型2register变量变量一般情况下,变量的值都是存储在内存中的。对于一般情况下,变量的值都是存储在内存中的。对于一些频繁使用的变量,可以放在一些频繁使用的变量,可以放在CPU内部的寄存器内部的寄存器中,以提高访问这些变量的速度。这种变量就称为中,以提高访问这些变量的速度。这种变量就称为寄存器变量。寄存器变量也属于局部变量范畴。寄存器变量。寄存器变量也属于局部变量范畴。(1)只有局部变量和形参可以作为寄存器变量,)只有局部变量和形参可以作为寄存器变量,其他变量不可以(全局变量、局部静变量不可
34、以)。其他变量不可以(全局变量、局部静变量不可以)。(2)由于计算机的寄存器数目有限,不宜定义太)由于计算机的寄存器数目有限,不宜定义太多寄存器变量。不同的系统允许使用的寄存器个数多寄存器变量。不同的系统允许使用的寄存器个数是不同的。是不同的。 6.4 变量的作用域与存储类型3static变量变量静态变量在程序开始运行时就分配了固定的内存静态变量在程序开始运行时就分配了固定的内存空间,在程序运行过程中不释放,直到程序运行空间,在程序运行过程中不释放,直到程序运行结束才释放它所占的存储空间,因此静态变量的结束才释放它所占的存储空间,因此静态变量的生存期是整个程序。由于变量分为局部变量和全生存期是
35、整个程序。由于变量分为局部变量和全局变量,所以静态变量也分为静态局部变量和静局变量,所以静态变量也分为静态局部变量和静态全局变量。态全局变量。(1)静态局部变量)静态局部变量静态局部变量仍然是定义在函数内部的局部变量,静态局部变量仍然是定义在函数内部的局部变量,它的作用域仍然局限于函数内部。它的作用域仍然局限于函数内部。例如:例如:static int m=10,n;6.4 变量的作用域与存储类型【例例6-7】编写程序计算编写程序计算1!5!的值。!的值。 #include int fac(int n) /*函数定义函数定义*/ static int f=1; /* 静态局部变量静态局部变量
36、*/ f=f*n; return(f); void main() int i; for (i=1;i=5;i+) printf(%d! =%dn,i,fac(i); 在函数在函数fac内部定义内部定义了静态局部变量了静态局部变量 f,每次调用都保留了上每次调用都保留了上一次计算的结果一次计算的结果例如:例如:static int m=10,n;说明:说明:(1) 静态局部变量的存储空间是在程序编译时由系统分静态局部变量的存储空间是在程序编译时由系统分配的,且在程序运行的整个期间都固定不变。配的,且在程序运行的整个期间都固定不变。(2)静态局部变量编译时赋初值,每次调用时不再赋初静态局部变量编译
37、时赋初值,每次调用时不再赋初值,只保留调用结束时变量的值。值,只保留调用结束时变量的值。(3)对静态局部变量,如果定义时不赋初值的话,编译)对静态局部变量,如果定义时不赋初值的话,编译时系统会自动赋值为时系统会自动赋值为0或空字符(对字符变量)。或空字符(对字符变量)。如上例在程序编译时给变量如上例在程序编译时给变量m赋予赋予10,对未给定,对未给定初值的变量初值的变量n,C 编译系统自动给它赋编译系统自动给它赋0值。值。(2)静态全局变量)静态全局变量在在C语言中,全局变量的存储都采用静态存储方式。语言中,全局变量的存储都采用静态存储方式。6.4 变量的作用域与存储类型4extern变量变量
38、 全局变量(即外部变量)的作用域是从定义的位置起到本全局变量(即外部变量)的作用域是从定义的位置起到本文件的末尾止。采用文件的末尾止。采用extern来声明全局变量可以扩大全局来声明全局变量可以扩大全局变量的作用域。变量的作用域。 (1) 在多个文件中声明全局变量在多个文件中声明全局变量 一个一个C程序可以由多个文件组成。如果一个文件中要引用程序可以由多个文件组成。如果一个文件中要引用另外一个文件中所定义的全局变量,需要在此文件中,用另外一个文件中所定义的全局变量,需要在此文件中,用extern进行声明。进行声明。 (2) 在一个文件中声明全局变量在一个文件中声明全局变量 同一个文件中,在全局
39、变量定义点之前的函数想引用该全同一个文件中,在全局变量定义点之前的函数想引用该全局变量,应该在引用之前用局变量,应该在引用之前用extern对该变量作对该变量作“全局变量全局变量声明声明”。全局变量未被初始化,系统自动赋初值。全局变量未被初始化,系统自动赋初值0。6.4 变量的作用域与存储类型 【例例6-8】同一个文件中声明全局变量的应用例子。同一个文件中声明全局变量的应用例子。 #include void main() 结果示例:结果示例: int min(int a,int b); extern x,y;/* 声明全局变量声明全局变量x,y */ printf(%dn,min(x,y);
40、int x=22,y=11;/* 定义全局变量定义全局变量x,y */ int min(int a,int b) int c; c=ab?a:b; return(c); 6.5 编译预处理 编译预处理是指,在对源程序进行编译之前,先编译预处理是指,在对源程序进行编译之前,先对源程序中的编译预处理命令进行处理,然后再对源程序中的编译预处理命令进行处理,然后再将处理的结果,和源程序一起进行编译,以得到将处理的结果,和源程序一起进行编译,以得到目标代码。目标代码。 提供的预处理功能主要有宏定义、文件包含和提供的预处理功能主要有宏定义、文件包含和条件编译。这些功能分别用宏定义命令、文件包条件编译。这些
41、功能分别用宏定义命令、文件包含命令、条件编译命令来实现。预处理命令是以含命令、条件编译命令来实现。预处理命令是以号开头的代码行。号开头的代码行。6.5 编译预处理 6.5.1 宏定义宏定义 宏定义是用指定标识符(宏名)来代表一个字符串。宏定义是用指定标识符(宏名)来代表一个字符串。1不带参数的宏定义不带参数的宏定义 其定义的一般形式:其定义的一般形式: define 宏名宏名 字符串字符串 字符串可以是常数、表达式、格式串等。字符串可以是常数、表达式、格式串等。宏定义可出现在宏定义可出现在程序中的任何位置。程序中的任何位置。 可以用可以用#undef命令终止宏名的作用域,其一般形式为命令终止宏
42、名的作用域,其一般形式为 undef 标识符标识符 一、无参数的宏定义一、无参数的宏定义 格式:格式:#define 宏名宏名 替换文本替换文本 or #define 宏名宏名 (说明宏名已被说明宏名已被”定义定义“)如如 :#define SIZE 100 替换文本中可以包含已定义过的宏名替换文本中可以包含已定义过的宏名如:如: #define PI 3.14#define ADDPI (PI+1)#define TWO_ADDPI (2*ADDPI) 若程序中有表达式若程序中有表达式:x=TWO_ADDPI/2,替换后替换后,表达式将表达式将成为成为: x=(2*(3.14+1)/2若写成
43、若写成 #define PI 3.14 #define ADDPI PI+1 (无括号无括号) #define TWO_ADDPI 2*ADDPI (无括号无括号)则以上表达式成为则以上表达式成为:x=2*3.14+1/2 二、有参数的宏定义二、有参数的宏定义 格式:格式: #define 宏名(参数表)宏名(参数表) 替换文本替换文本如:如: #define MU(X,Y) (X)*(Y) a=MU(5,2);/*引用带参的宏名引用带参的宏名*/b=6/MU(a+3,a);注意:同一宏名也不能重复定义注意:同一宏名也不能重复定义圆括号不能少圆括号不能少6.5 编译预处理对于带参数的宏定义有以
44、下问题需要说明:对于带参数的宏定义有以下问题需要说明:(1)定义带参数的宏时,宏名与左圆括号之间)定义带参数的宏时,宏名与左圆括号之间不能留有空格。否则将空格以后的所有字符均作不能留有空格。否则将空格以后的所有字符均作为替换字符串,用来替换不带参数的宏名。为替换字符串,用来替换不带参数的宏名。(2)带参数的宏展开时,用实参字符串替换形)带参数的宏展开时,用实参字符串替换形参字符串,而不做任何语法检查。参字符串,而不做任何语法检查。读者可能已经读者可能已经体会到,带参数的宏在程序中使用时,它的使用体会到,带参数的宏在程序中使用时,它的使用形式和特性和函数很相似。但是在本质上它们是形式和特性和函数
45、很相似。但是在本质上它们是完全不同的。完全不同的。6.5 编译预处理 带参数的宏和函数的区别有以下几点:带参数的宏和函数的区别有以下几点: (1)函数调用时,先求出实参表达式的值,然后将值代函数调用时,先求出实参表达式的值,然后将值代入形参;而宏调用时只是用实参简单地替换形参。入形参;而宏调用时只是用实参简单地替换形参。 (2)函数调用是在程序运行时进行的,为形参分配临时函数调用是在程序运行时进行的,为形参分配临时的内存单元,占用运行时间;而宏调用则是在编译时由编的内存单元,占用运行时间;而宏调用则是在编译时由编译预处理程序完成的,仅仅是一种简单的替换,并不分配译预处理程序完成的,仅仅是一种简
46、单的替换,并不分配内存单元,不进行值的传递处理,也没有内存单元,不进行值的传递处理,也没有“返回值返回值”的概的概念,因此宏调用不占用运行时间。念,因此宏调用不占用运行时间。 (3)在函数定义时,必须指定每一个形式参数的类型,在函数定义时,必须指定每一个形式参数的类型,而宏定义中只给出形式参数,宏不存在类型问题,宏名无而宏定义中只给出形式参数,宏不存在类型问题,宏名无类型,它的参数也没有类型,只是一个符号代表。类型,它的参数也没有类型,只是一个符号代表。 (4)调用函数只可以得到一个返回值,而用宏可以设法调用函数只可以得到一个返回值,而用宏可以设法得到几个结果。得到几个结果。 6.5 编译预处
47、理6.5.2 文件包含文件包含一般形式为:一般形式为:include “文件名文件名” 或或 include 它的功能是把指定的文件插入该命令行位置取代它的功能是把指定的文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序文该命令行,从而把指定的文件和当前的源程序文件连成一个源文件。件连成一个源文件。 6.5 编译预处理 6.5.3 条件编译条件编译 为了提高程序的可移植性,增加程序的灵活性,有时只希为了提高程序的可移植性,增加程序的灵活性,有时只希望对其中某一部分在满足一定条件时才进行编译,即对部望对其中某一部分在满足一定条件时才进行编译,即对部分内容指定编译条件,这就是条件编译
48、。分内容指定编译条件,这就是条件编译。 条件编译命令有以下几种形式:条件编译命令有以下几种形式: (1)ifdef 标识符标识符 程序段程序段1 else 程序段程序段2 endif 它的功能是它的功能是:当所指定的标识符被当所指定的标识符被#define命令命令定义过,程定义过,程序在编译时只编译程序段序在编译时只编译程序段1;否则,只编译程序段;否则,只编译程序段2,或不,或不编译。编译。6.5 编译预处理 (2)ifndef 标识符标识符 程序段程序段1 else 程序段程序段2 endif 它的功能是当所指定的标识符没有被它的功能是当所指定的标识符没有被 #define命令定义过,命令
49、定义过,则在程序编译时只编译程序段则在程序编译时只编译程序段1;否则编译程序段;否则编译程序段2,或不,或不编译。这种形式与第一种形式的功能正好相反。编译。这种形式与第一种形式的功能正好相反。 (3)if 表达式表达式 程序段程序段1 else 程序段程序段2 endif 它的功能是当指定的表达式的值为真(非零)时,程序在它的功能是当指定的表达式的值为真(非零)时,程序在编译时只编译程序段编译时只编译程序段1;否则编译程序段;否则编译程序段2,或不编译。,或不编译。 6.6函数应用举例 【例例6-14】由键盘任意输入两个整数,求这两个整数的最由键盘任意输入两个整数,求这两个整数的最大公约数和最
50、小公倍数。大公约数和最小公倍数。 分析:设两个整数用变量分析:设两个整数用变量m、n表示,最大公约数用表示,最大公约数用“辗辗转相除法转相除法”求解,其思路是:首先用两个数中的大数求解,其思路是:首先用两个数中的大数m除除以小数以小数n,得到一个余数,得到一个余数r,若,若r的值为的值为0,则此时的除数,则此时的除数n就是最大公约数;若就是最大公约数;若r的值不为的值不为0,则把除数作为被除数,则把除数作为被除数,余数作为除数,再相除,直到余数为余数作为除数,再相除,直到余数为0为止。从数学知识为止。从数学知识可知道,任何两个整数的最小公倍数等于这两个数之积再可知道,任何两个整数的最小公倍数等
51、于这两个数之积再除以这两个数的最大公约数。即最小公倍数除以这两个数的最大公约数。即最小公倍数=m*n/最大公最大公约数。定义一个函数约数。定义一个函数maxdivisor来实现最大公约数。来实现最大公约数。6.6函数应用举例 #include main( ) int maxdivisor(int m,int n); /* 函数的声明函数的声明*/ int a,b,divisor,multiple; printf(input two integer numbers:); scanf(%d%d,&a,&b);divisor= maxdivisor(a,b); /* 调用函数,求最大公约数调用函数,求最大公约数*/ multiple=a*b/divisor; /*求最小公倍数求最小公倍数 */ printf(The largest common divisor:%dn,divisor);printf(The small common multiple:%dn,multiple); 6.6函数应用举例int maxdivisor(int m,int n)/* 定义求出最大公约定义求出最大公
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 中国环保清洗剂项目创业计划书
- 模型中的科学与科学中的模型
- 2025年中国镁合金触变产品项目创业计划书
- 中国专项化学用品项目投资计划书
- 阳泉市人民医院产科住院医师规范化培训考核
- 七台河市人民医院造口肉芽肿处理考核
- 石家庄市人民医院隐形矫治技术专项技能考核
- 大同市人民医院老年医学论文写作考核
- 中国氮化硅基陶瓷项目创业计划书
- 鸡西市中医院脊柱术后康复指导考核
- 生产线平衡法应用培训
- 无人飞行器无人机在边境巡逻与安全保障考核试卷
- 山塘水库维修申请报告
- T/CHES 63-2022活塞式调流调压阀技术导则
- 国家a级食堂标准
- 2025-2030中国果胶行业市场发展现状及发展趋势与投资风险研究报告
- 烟雾病和烟雾综合征诊断与治疗中国专家共识(2024版)解读
- 《黄帝内经养生智慧》课件
- 战略物资储备安全-洞察阐释
- 自治区幼儿园保育教育质量自评 指导手册 (试行)
- 公交公司信访工作方案
评论
0/150
提交评论