收藏 分销(赏)

C语言经典课件5-9章.doc

上传人:xrp****65 文档编号:7057555 上传时间:2024-12-25 格式:DOC 页数:118 大小:844KB 下载积分:10 金币
下载 相关 举报
C语言经典课件5-9章.doc_第1页
第1页 / 共118页
C语言经典课件5-9章.doc_第2页
第2页 / 共118页


点击查看更多>>
资源描述
第五章 函数 第五章 函数 解决大的复杂问题的方法之一是化整为零,分而治之,在程序设计中也采用这样的方法,将大任务分解成若干人的智力能及的小模块,降低程序的复杂性,增加程序的可靠性和可重用性。在C程序中,模块以函数的形式来体现,所以说C程序是由函数构成的语言,是函数式的语言。一个C程序由一个main函数和若干其它函数组成,这些函数可以写在一个文件中,也可以写在若干文件中,无论main函数的位置在何处,程序总是从main函数开始执行,也从main函数结束。图5-1是C程序的组织示意图。 文件1 文件2 文件n 函数1一1 函数2一1 函数n一1 程 序 图5-1 C程序的组织 5.1问题的提出 5.1.1问题 如何使用函数编程计算3!+5!+8! 5.1.2问题分析 题目要求计算三个阶乘的值,相加后,输出结果,核心在于计算阶乘。如果在一个main()函数里完成的程序如下: 例5_1 main() { long int j,t=1,s=0; for(j=1;j<=3;j++) t=t*j;/*计算3阶乘*/ s+=t; for(t=1,j=1;j<=5;j++) t=t*j;/* 计算5阶乘*/ s+=t; for(t=1,j=1;j<=8;j++) t=t*j;/* 计算8阶乘*/ s+=t; printf(“3!+5!+8!=%ld\n”,s); } 仅仅因为循环次数不同,程序中写了三遍计算阶乘的程序段(三个for语句),程序显得重复累赘,如果把计算阶乘的程序段独立写成一个模块(函数),需要时,调用它,程序的可重用性、可靠性、可维护性都可得到提高,怎样写一个函数呢?,怎样调用这个函数呢?这就是我们学习函数这章的任务。 5.1.3 程序 1.把求阶乘的程序段独立写成一个函数,调用这个函数时,输入n,函数就输出n!,如图5-2。 n! n 函数 图5-2 求n!函数 例5_2 long jch(int n) { long t=1; int j; for(j=1;j<=n;j++) t*=j; return t;} 2. main函数中三次调用以上函数,并将结果相加后输出。 main() {long s=0; s=jch(3)+jch(5)+jch(8); printf(“3!+5!+8!=%ld\n”,s);} 将以上两个函数输入计算机,运行后输出和例5_1同样的结果: 3!+5!+8!=40446 ,但程序的模块化程度提高,整个程序由两个函数组成,可重用性提高,jch函数可被多次使用,main函数的任务减轻,它主要完成调用其他函数的任务。函数的应用主要涉及三方面的内容:定义、调用、声明。 5.2 函数的定义 函数的定义由函数头和函数体两部分组成,形式如下: 函数头 { 函数体 } 5.2.1函数头 C程序通过函数头来调用函数,因此函数头是函数的用户界面,其形式如下: 类型名 函数名(形参表列) 类型名是函数返回值的类型,如例5_2中的long jch(int n)表示该函数将返回一个长整型数据,无返回值的函数是void型。 函数名是函数的标识,必须是合法的C标识符。 括号中的形参表列是0个或多个以逗号分隔的形式参数,它定义了调用该函数时必须送给该函数的数据类型及数据个数,之所以称为形式参数,是因为函数不被调用时,系统不给这些参数分配存储空间,只在调用发生时,才分配空间,调用结束后,系统又收回空间。这种参数就像剧本中的角色,只有在被演员扮演时才能有演出效果。 5.2.2函数体 函数体是用一对花括号括起来的语句系列(语句块),它描述了函数实现某一功能的执行过程,函数最后要执行返回,返回的作用是: u 将程序的执行从当前函数返回其上级(调用它的函数)。 u 释放该函数的参数及变量所占用的内存空间。 u 向函数返回一个值(如果函数的类型不是void时)。 当函数要返回一个值时,必须通过return语句返回,其形式如下: return (表达式); 表达式的类型应该和函数定义时函数名前的类型一致,有冲突时,服从于函数名前的类型,return后的括号可有可无。 例5_3 int absint(int x)/* 返回一个整数的绝对值*/ { if (x>=0) return x; else return -x; } C函数只能通过一个return语句返回一个值,所有return语句的返回值类型应与函数名前的类型一致。这个函数虽然有两个return语句,但执行时只可能执行到一个return语句。 函数只完成某一功能,而不返回任何值时,可将函数定义成void类型,在这种函数中return语句可有可无,函数执行完后,有无return语句,都执行返回功能。 例:5_4 void pok() {printf(“0k”); return;/* 可以无return*/ } 在一个程序中不能出现同名的多个函数定义,且函数体内不能再定义函数,即不能嵌套定义,如: int ff1() /* ff1函数定义 */ { float ff2() /* ff2函数定义出现在ff1函数定义中,错*/ { } } 函数的定义就是给出函数的名字,函数的返回值类型,函数的形参名字与类型,函数的实现语句(函数体)。 5.3函数原型、函数声明与函数调用 5.3.1函数原型 调用函数时,系统需要知道下列信息: u 函数类型 u 函数名 u 函数的参数(个数、类型及顺序) 知道这些信息就可以调用该函数,而不必知道函数的具体实现(函数体),这些信息描述了函数的模型,是函数的用户界面,我们称为函数原型。如: int absint(int); void pok(); 5.3.2函数的声明 在函数调用前,C系统需要知道函数的原型,才能保证函数的正确调用,因此当函数调用在前,定义在后时(当函数为int型时,也可不声明),必须用函数原型声明,以便C系统获取相关信息。声明时参数名可以缺省,但类型名不可以缺省,如: int ff1(int age,char sex); 也可以写为:int ff1(int ,char); 声明中的参数名相当于注释,以便系统检查调用时实参的位置,所以可以缺省。 5.3.3函数的调用 函数的定义相当于写剧本,函数的调用相当于演戏,函数的定义只是说明了函数的存在,函数必须通过调用才能被执行,函数调用的功能是: u 实参数向形参数传递数据。(由演员担当角色) u 为参数和函数体内的变量分配内存空间。 u 中断当前函数的执行,把执行流程转向被调用函数的入口,执行被调用函数。 函数调用通过调用表达式进行,其形式为: 函数名(实参表列) 当函数无返回值时,调用表达式后加分号,作为调用语句使用,如:pok();当函数有返回值时,调用表达式作为其他表达式的一部分,如:c=max(a,b); 函数调用结束后,返回到调用时的中断处,但形参的值不传递给实参,参数的传递是单向值传递,见下例。 例:5_5调用mult10时: 实参变量n的值 新分配的形参单元 n 例5_5:将一实数乘以10后显示出来 5.76 main() {float n=5.76,result; float mult10(float); /* 函数的声明*/ result=mult10(n); /* 函数的调用*/ printf(“result=%f\n ”,result); printf(“num=%f\n ”,n); } float mult10(float n) /* 函数的定义*/ { n*=10; return(n); } 程序运行结果: 程序运行结果: result=57.600002 图5-3 单向值传递 num=5.760000 变量作函数的形式参数时,实参可以是表达式(包括已有确定值的变量,常量),当执行到调用表达式时,计算表达式的值,将实参的值传递给形参。函数返回时,形参的值不传给实参,这被称作单向值传递。形参和实参是不同的变量,即便它们有相同的名字,也互不相干,如本例 形实参都是n,但形参n的值不影响实参n。 说明: 程序从main函数开始执行,执行到函数调用mult10(n)时,将实参n变量中的值传递给形参的n变量,执行mult10函数中的语句,形参n变量乘10后,返回到main函数,并将mult10函数的结果57.600002(形参n变量的值)赋给result变量,形参n变量和实参n变量是不同的内存单元,mult10函数调用结束后,形参n变量的值不传给实参n变量,因此实参n变量的值不会改变。调用的时候,实参变量中的值传递给形参变量,调用结束后,形参变量的值不传给实参变量,这就是单向值传递,见图5-3。 例5_6: 求s=s1+s2+s3+s4的值。其中:s1=1+1/2+1/3+…+1/50;s2=1+1/2+1/3+…+1/100; s3=1+1/2+1/3+…+1/150;s4=1+1/2+1/3+…+1/200 float fc(int n) /*定义函数求1+1/2+1/3+…+1/n的值*/ {float s; int i; s=0; for(i=1; i<=n; i++) s+=1/(float)i; return(s);} main() /*主函数*/ {float sum; sum=fc(50)+fc(100)+fc(150)+fc(200);/* 四次调用fc()函数,使该函数的形参n分别 为n=50;n=100;n=150;n=200;*/ printf(“sum=%f ”,sum); } 程序的运行结果: sum=21.155796 5.4数组名作函数的参数 5.4.1问题的提出 在程序设计中,我们经常需要对一组数据排序,如对若干同学的成绩按由高到低的顺序排序,对若干选手的成绩排序,对工厂每月的产品产量排序等,所以有必要写一个能对若干数据排序的函数,需要时调用它即可完成排序任务,这个函数应该怎样定义呢? 5.4.2问题分析 对大量同类型的数据排序,这些数据应怎样存放是第一个要考虑的问题,根据我们已有的知识,这些数据只能存放在一维数组中,因此送给排序函数进行排序的对象是个一维数组,这是排序函数的第一个参数,其次由于每次需要排序的数目不同,因此还必须有第二个参数,告知该函数需要排序的数据个数,这个参数肯定是个整型变量,这样我们就有下面该函数的轮廓: void sort(一维数组,int n) { 用某种算法对该数组排序 } 现在的问题是在C语言中,用一维数组作参数时,应怎样表达?根据前面的知识,实参和形参的类型应该一致,因此,我们的第一个参数,只能是float,int,char这些基本类型中的一个数组,C语言允许一维数组作形参时,可不定义大小,因此如果对整型数据排序,这个函数头如下: void sort(int s[],int n) { 用某种算法对该数组排序 } 在函数体中我们只要用某种算法排序s数组即可。 5.4.3程序 例5_7 用选择法排序的该函数如下: void sort(int s[],int n) { int j,t,k; for(j=0;j<n-1;j++) for( k=j+1;k<n;k++) if(s[j]<s[k]) {t=s[j];s[j]=s[k];s[k]=t;} } main() { int j,a[]={60,70,55,89,90,100,67,88,76,95}; sort(a,10); for(j=0;j<10;j++) printf(“%d,”,a[j]); } 程序运行结果: 100,95,90,89,88,76,70,67,60,55, 函数必须通过调用才能被执行,因此,必须在main()函数中定义实参数组,并调用该函数,C语言规定,形参是数组时,实参应是同类型的数组名,如本例中的a,数组名是数组的首地址,将实参数组的首地址传给形参数组,这样实参数组和形参数组共用同一段内存单元,对形参数组的操作就是对实参数组的操作,因此形参数组排序完成,实参数组也完成了排序,如图5-3所示: a[0] a[9] 60 70 55 89 90 100 67 88 76 95 s[9] s[0] 图5-3 现在您可以写一个函数求一维数组的平均值,并调用它吗?请完成。 例5_8 编写求字符串长度的函数。 strlen(char s[]) {int i; for(i=0;s[i]!=’\0’;i++); return(i); } 请编写main函数,调用该函数,测试某字符串的长度。 5.5函数的嵌套调用和递归调用 C语言不可以嵌套定义,但可以嵌套调用,在调用一个函数时,这个函数又调用其他函数就称函数嵌套调用。嵌套调用也遵循前面介绍的调用规则,即从哪里调用该函数,该函数执行完成后也返回到哪里。 例5_9 int f1(int a,int b)/*定义f1函数*/ {int c; a+=a;b+=b; c=f11(a,b); /*调用f11函数*/ return c*c; } int f11(int a,int b) /*定义f11函数*/ { int c; c=a*b%3; return c; } main() { int x=11,y=19; printf(“%d\n”,f1(x,y)); /*调用f1函数*/ } 程序的执行结果是:4 程序的执行过程如下图: main() { … f1(x,y) } { a=22;b=38; c=f11(a,b); return c*c { c=22*38%3 return c } ① main() f1(int a,int b) f11(int a,iny b) ③ ④ 2 ② 42 图5-4 在调用函数的时候,这个函数又调用自己就叫递归调用,递归调用是嵌套调用的特例,是程序设计中常用的方法之一。 例5_10 求n!的方法可以用递归的方法表达,即5!=4!*5,而4!=3!*4,3!=2!*3,2!=1!*2,1!=1,递归公式为: 1 (n=0,1)/* 递归的终点 */ n!= n(n-1)! (n>1) /* 递归调用 */ 根据公式我们就可以写出求n!的函数。 float ff(int n) { float f; if(n<0) printf(“n<0 data errar !”); else if(n==0||n==1) f=1; /*递归的终点*/ else f=ff(n-1)*n; /*递归调用*/ return f; } main() { int n; float y; printf(“input a integer number:”); scanf(“%d”,&n); y=ff(n); printf(“%d!=%f\n”,n,y); } 递归调用一定要有结束调用的条件,本例中当n=0或1时,f=1,结束递归调用,当n>1时,发生递归调用,即在ff函数中又调用ff函数(ff(n-1)),下面以5!为例,分析该函数的执行过程: 调用 main() { …ff(5); f=ff(4)*5; f=ff(3)*4; f=ff(2)*3; f=ff(1)*2; 120 24 6 2 返回 } 图5-5 5.6库函数 C语言定义了丰富的库函数,方便我们程序设计,我们只需注意以下几点,便可调用库函数。 u 函数的功能 u 函数的原型 u 库函数所需的头文件 我们已经知道,调用函数前,需要定义函数并声明函数,C系统为方便我们使用库函数,已经完成了这两项工作。首先函数按功能分为不同的库,如:数学函数、输入输出函数、字符串处理函数等,每个库都有一个头文件,给出了各库中各个函数的原型声明等有关信息。使用库函数之前,只需在程序中使用#include命令将其相应的头文件包含进来,程序就有了该函数的原型说明。然后在程序执行时,C系统会根据函数的原型说明,到相应的库文件中找到相应的库函数定义,并执行它。比如与数学函数对应的头文件是math.h, 与输入输出对应的头文件是stdio.h,与字符串处理函数对应的头文件是string.h等。 例5_11 #include “math.h”/*嵌入数学库函数头文件*/ main() { float f; printf(“input a real number:”); scanf(“%f”,&f); printf(“The sqnare root is of %f is :% f”,f,sqrt(f)); } /* sqrt是数学函数库math.h头文件中的一个求开方的函数*/ 库函数是一些被验证的、高效率的函数,进行程序设计时,应优先选用库函数。常用的库函数及标准头文件见附录。 注意,系统头文件在include子目录中,系统函数定义在lib子目录中,连接时,系统会根据原型到相应库文件中连接相应的库函数。要正确地进行文件包含和连接应设置正确的环境。如: c:\tc\include c:\tc\lib 这样才能在编译和连接时找到相应的文件。 5.7变量的作用域 5.7.1问题的提出 写一个函数,送入圆的半径r后,得到圆的面积及圆的周长。 5.7.2问题分析 我们已经知道,通过函数调用,最多可以用return语句返回一个值给调用函数,如果想返回多个值,怎么办呢?这道题涉及到变量的作用域。 5.7.3程序 例5_12 现在我们编写利用全局变量来完成写一个函数,送入圆的半径r后,得到圆的面积及圆的周长的程序。 float cl;/*定义全局变量c1*/ float carea(float r) {float ar; ar=3.14*r*r; cl=2*3.14*r;/*使用全局变量c1*/ return ar; } main() {float r,area; printf(“\n r=?”); scanf(“%f”,&r); area=carea(r); printf(“r=%5.2f,carae=%5.2f,cl=%5.2f\n”,r,area,cl); /*使用全局变量c1*/ } 程序运行结果: r=?3 /* 输入半径3 */ r= 3.00,carae=28.26,c1=18.84 该程序中使用了在函数体外定义的全局变量c1, 在carea函数中把圆的周长存入c1中,在main函数中把c1的值输出,两个函数都在用c1变量,这是前面的程序中没有的情况,什么是全局变量和局部变量?它们有何特点? 5.7.4局部变量和全局变量 变量是程序设计中的重要元素,我们已经知道了它的一些属性:类型决定了它所获得的存储空间的大小;存入变量的值可以被多次使用,写入(赋值)将覆盖变量中原有的值;它所占居的存储空间的编号是变量的地址,此外,它还有存储属性,存储属性包括作用域与生存期两个方面。 变量的作用域就是变量在源程序代码中的可用范围,作用域根据变量的定义位置区分为局部变量和全局变量两种。在函数体内或语句块中定义的变量称局部变量,它们的作用范围只在所定义的函数体内或语句块中,在函数体外定义的变量,称全局变量,它的作用域从定义点起,直到本源文件结束,如果用extern关键词加以引用说明,它的作用范围可以扩大到在一个程序的所有文件中都可使用。如:图5-6。 int p=1,q=5;/*定义外部变量 */ 外部变量p,q作用域 a,b,c变量作用域 float f1(int a) /*定义f1函数 */ {int b,c; … } char c1,c2; /*定义外部变量 */ x,b,j变量作用域 外部变量c1,c2作用域 char f2(int x) /*定义f2函数 */ {int b,j; … } m,n变量作用域 main()/*主函数 */ {int m,n; … } 图5-6 说明: (1) 在函数体内定义的变量,只在本函数有效,因此在不同函数中可以使用相同名字的变量,如本例中f1、f2函数都有变量b,但两个函数中的b变量是不同的内存空间,它们互不相干。 (2) main函数中定义的变量(m,n),也只在main函数中有效,其他函数不得使用。 (3) 形参也是局部变量。 (4) 函数体外定义的变量都是全局变量(p,q,c1,c2),但根据定义点,作用范围不同。(c1,c2的作用范围小) (5) 外部变量可以增加函数间的联系,但破坏了函数的封装性,增加了程序的难读性(程序中的函数都可以改变全局变量的值),建议少用全局变量。 5.8变量的存储类型 5.8.1问题 通过5次函数调用打印1到5的阶乘值。 5.8.2问题分析 该题有多种算法,其中效率较高的一种是:先求出1!=1,在1!的基础上再乘2,就得2!(2!=1!*2), 在2!的基础上再乘3,就得3!(3!=2!*3),…,这样我们要写的函数必须能保存上一次的阶乘值,在(n-1)!的基础上再乘n就完成了n!,问题是局部变量可以保存函数的结果,以备下一次调用函数时使用吗?换句话说在函数调用完成后它还存在吗?如果它不存在,显然它不能保存值。所以定义变量时,不仅要考虑它的作用域还要考虑它的生存期。 5.8.3 程序 例5_13 通过5次函数调用打印1到5的阶乘值 int fact(int x) { static int t=1; t*=x; return t; } main(){ int i; for(i=1;i<=5;i++) printf(“%d!=%d\n”,i,fact(i));} 程序运行结果为: 1!=1 2!=2 3!=6 4!=24 5!=120 思考:请将fact函数中的static int t;语句改为int t;,运行程序,结果是什么? main函数中不用循环语句,直接用printf(“%d!=%d\n”5,fact(5));语句,能输出5!的结果吗?为什么? 要完全看懂例5-13,必须知道变量的存储类型与变量的生存期。C语言把用户执行程序所占用的内存空间分为三部分:程序代码区、静态变量存储区和动态变量存储区。静态存储区中的变量在编译时创建,在程序结束时才被撤消。全局变量和静态变量放在该区,也就是说,在整个程序的执行期间它们始终存在。而存储在动态存储区中的变量,在程序的执行过程中根据需要创建,在运行完所在域后即被撤消,它们是动态存在的。局部变量和形参就分配在动态存储区。如图5-7。 程序代码区 静态存储区 动态存储取 存放可执行程序的机器指令 存放外部变量、静态局部变量 存放自动变量,形参等 全局变量或静态变量 局部变量和形参 图5-7 C语言定义一个变量的完整形式如下: 存储类型说明符 数据类型说明符 变量名 其中,存储类型说明符有:auto(自动)、extern(外部)、static(静态)、register(寄存器)四种。 5.8.4局部变量的存储类型 局部变量有三种存储类型:auto、 static、 register。 1.自动变量 局部变量定义时不指明存储类型或用auto说明时,都是自动变量。自动变量分配在动态存储区。程序执行到该局部变量所在域时,C系统为该局部变量分配存储空间,执行完该域后,释放其所在的空间。再次调用函数时,系统将为自动变量重新分配空间。如果自动变量定义时有初值,每调用一次函数,都要赋初值。没有赋初值的自动变量,将得到分配给它的内存单元原有的值,是一个不确定的值。 2.静态局部变量 使用static定义的局部变量,称静态局部变量,它的作用域与自动变量一样,但它被分配在静态存储区中,因此,当函数调用完成后,它依然存在,再次调用函数时,C系统不再重新为静态局部变量分配存储空间,赋给静态局部变量的初值是在编译时完成的,程序执行期间不再赋初值,因此局部静态变量可以保存上一次函数结束时的值,以备下一次函数调用时使用。对定义时未赋初值的静态局部变量,C系统在编译时自动赋初值一次,数值型赋0,字符型赋空格。 3.寄存器变量 使用register定义的局部变量,称寄存器变量,顾名思意,寄存器变量占用CPU的通用寄存器,而不占内存单元,因此使用寄存器变量就省去了访问内存的时间,从而提高了程序的执行速度。也由于这一特点,使用时请注意: u 寄存器非常有限,不可能让变量长时间占有,所以寄存器变量只可能是自动变量,且程序中使用的寄存器变量的数目也是有限的。 u 由于长度的限制,寄存器变量不能是long、flot、doubleint类型。 u 寄存器变量没有地址,不能对它进行求地址运算。 那些被频繁使用,占用字节数又不多的变量,适合定义为寄存器变量。 例5_14 使用寄存器变量 main() { int s; s=power(5,3); printf(“%d \n”,s); } power(int x,register int n)/* n为寄存器变量 */ { int p; for(p=1;n;n--) p*=x; return p; } 程序运行结果为: 125 n是寄存器变量,当传给n的值较大时,可以节省很多到内存存取变量值的时间。 5.8.5全局变量的存储类型 全局变量有两种存储类型:extern(外部)和static(静态)。 一个大的C程序由若干文件组成,为了增加联系,文件之间应该可以相互引用全局变量,如何引用全局变量呢?这就是我们要讨论的问题。 1.外部变量 没有用static定义的全局变量,就是外部变量,引用这种外部变量时,用extern加以说明,就可以使它的作用范围扩大到它所在的整个源程序文件,甚至其他文件。 例5_15 阅读下面的程序,注意外部变量的使用方法 /* 源程序文件f1.c*/ #include “f2.c” extern int a=5;/*定义外部变量 ,extern可缺省*/ char c1=’a’,c2=’b’; /*定义外部变量 */ main() {extern int b; /*外部变量b的定义在后,使用在前,必须用extern进行引用性声明 */ char c2=’B’; printf(“%c \n”,c1-32); printf(“%c ,%d\n”,c2,b*b);/*局部变量c2起作用*/ fact(); } int b=2; /*定义外部变量b2*/ /* 源程序文件f2.c*/ extern int a;/*使用另一个源程序文件中定义的外部变量必须进行引用性声明*/ fact() {int k,p=1; for(k=1;k<=a;k++) p*=k; printf(“%d!=%d”,a,p); } 使用外部变量时应注意以下问题: u 在同一个源程序文件中使用外部变量时,如果使用在前,定义在后,必须用extern进行引用性声明。 u 使用另一个源程序文件中定义的外部变量,必须用extern声明后,才能使用。 u 当全局变量和局部变量名相同时,在局部变量的作用范围内,局部变量起作用,全局变量被屏蔽 extern有两种声明形式,定义性声明和引用性声明,定义性声明是为了建立实体,即建立变量的存储空间。引用性声明是为了建立标识符与实体之间的联系。 定义性声明的形式为: [extern]类型 变量名[=初始化表达式]; 定义在函数体外的变量都是外部变量,所以extern通常被缺省,初始化表达式被缺省时,C系统为数值型外部变量赋0,字符型赋空格。 引用性声明的形式为: extern 类型 变量名; 定义性声明和引用性声明的区别如下: u 定义性声明一定是在外部,引用性声明不限于外部,只要在使用该外部变量前声明即可。 u 定义性声明可以初始化,引用性声明不能初始化。 u 定义性声明在程序中只有一次,而引用性声明可以有多次。 2.静态全局变量 当用static定义全局变量时,该全局变量不允许其他文件引用,只能在定义它的源文件中使用,这种变量称静态全局变量。如上例中的外部变量a,若定义成:static int a;则f2.c文件不能引用。 C语言中的函数与全局变量类似,无论定义时是否用extern修饰,都是外部的,都具有静态生存期,若要使用在一个程序中的其他文件定义的函数,用extern进行引用性声明,如: extern float ff(float);/*声明将引用一个其他文件中的函数*/ 如果函数不允许其他文件引用,定义时在前面加static修饰,如: static float fn(int n)/*定义一个不能被其他文件引用的函数。 {… } 5.9编译预处理 在程序头部出现的以#开头,末尾没有分号的命令是编译预处理命令,C系统在进行实质性的编译前,即将源程序文件翻译成目标文件前,先要对这些命令进行处理,故而把它们叫做编译预处理命令,以区别于在程序执行时才起作用的执行语句。C提供的编译预处理命令有: u 宏定义命令 #define u 文件包含命令 #include u 条件编译命令 #if #else #end if u 行控制命令 #line 其他:#pragma、#error等 我们主要介绍前两种。 5.9.1宏定义命令 用一个宏名(标识符)来代表一个字符串,就叫宏定义,其形式如下: # define 宏名 字符串 宏名用大写字母表示,以区别于变量,宏名后的字符串可以是表达式或语句,并可以进行嵌套定义(引用已定义的宏名)。 例5_16 #define X 5 /* 用X代替5 */ #define Y X+1 /* 用Y代替X+1(嵌套定义) */ #define Z Y*X/2 /* 用Z代替Y*X/2(嵌套定义) */ main() {int a=Y; /* 替换为int a=5+1 */ printf(“Z=%d,”,Z); /* Z处替换为5+1*5/2 */ printf (“%d\n”,--a); } 程序的运行结果为: Z=7,5 宏定义命令可以带参数,带参数的宏定义的形式: #define 宏名(参数表) 字符串 在字符串中应包含参数表中的参数。如: #define CUBE(X) (X)*(X)(X) … int b=0,a=3; b=CUBE(a);/* 替换为b=(a)*(a)*(a) */ 使用有参数的宏时请注意几点: (1)在实参数为表达式时,字符串中的参数是否用括号括起来,将影响替换结果,如上例改为: #define CUBE(X) X*X*X … int b=2,a=3; b=CUBE(b+a);/* 替换为b=b+a*b+a*b+a,而不是(b+a)*(b+a)*(b+a) */ (2)宏名与带参数的括号之间不应加空格,否则将空格以后的字符串都作为替代字符串的一部分,成为无参宏,如: #define CUBE (X) (X)*(X)*(X) int b=2,a=3; b=CUBE(a);/* 替换为b=(X) (X)*(X)*(X)(a) */ 带参数的宏与函数的主要区别是宏调用不需调用及返回时的开销,宏调用在编译时进行简单的字符串替换,宏调用多,将使编译时间增加,源程序增长。 5.9.2文件包含 文件包含的作用是将另一文件包含(嵌入)到本文件中,如图5-8所示: 目标文件 当前源程序文件 file.obj file.c #include “file1.h” 被包含的文件 file1.h 编译 A代码 B代码 图5-8 文件包含命令中,被包含文件用双引号括起来时,编译系统按以下方式搜索所要包含的文件: 源程序所在的文件目录中有所要的包含的文件? Y N 按标准方式搜索其他目录 嵌入该文件 图5-9 被包含文件用尖括号括起来时,编译系统只按标准方式搜索其他目录,搜索范围较小,但搜索时间短。 注意,一个#include命令只能包含一个文件,文件包含可以嵌套使用,即一个包含文件又包含另一个文件,当被包含文件的内容被修改后,包含这个文件的源程序文件要重新编译。 5.10语法练习 1. 选择题 (1) 下面哪个是正确的函数定义 A double fun(int x, int y) B double fun(int x; int y) C double fun(int x, y) D double fun(int x, y;) (2) 缺省修饰符的情况下 ,函数自身是____。 A. static B. auto C. register D. extern (3) 下面不正确的说法是____。 A.C程序通常是由许多小函数组成的,而不是少量的大函数组成的. B. 在源文件中可以以不同的顺序定义函数。 C. 通常调用函数前,函数必须被定义和声明。 D. dummy(){ } 是无用的函数。 (4) 选择程序的结果___ increment() { static int x=0; x+=1;printf("%d " , x ); } main() { increment(); increment(); increment(); } A. 1 1 1 B. 1 2 3 C. 0 1 2 D. 0 0 0 (5) 选择程序的结果___ main() { static char a[80] = "AB" , b[80] = "LAMP" ; int i = 0;
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服