c语言 static函数在函数中用static定义一个m 两次调用这个函数 为什么m的值会累计 每次调用函数不

百度知道 - 信息提示
知道宝贝找不到问题了&_&!!
该问题可能已经失效。
秒以后自动返回文档分类:
在线文档经过高度压缩,下载原文更清晰。
北京航空航天大学C语言i第九讲(第八章) 函数2.ppt 北京航空航天大学 C 语言 i 第九讲(第八章) 函数 2第八章函数教学目标掌握函数调用参数的值传递和地址传递如何使用局部变量和全局变量及静态变量如何运行一个多文件的程序8.7数组作为函数的参数1、数组元素做函数的实参---值传递例:比较整型数组 a[9]和 b[9]对应的元素,分别将a[i]&b[i], a[i]==b[i], a[i]&b[i]的个数记录在变量 n,m,k 中。a[9]={1, 3, 5 ,7 , 9 , 8, 6, 4, 2} 分析:a[i]b[9]={2, 3, 6, 9, -1, -3, 5, 6, 0} b[i]比较函数: compare(int x,int y)1 x&y 0 x==y -1 x&pare(int x, int y) void main() { { if (x&y) flag=1; int i,n=0,m=0,k=0;//变量使用前要赋初值 else if (x&y) flag=-1; int a[9]={1, 3, 5 ,7 , 9 , 8, 6, 4, 2};else flag=0; int b[9]={2, 3, 6, 9, -1, -3, 5, 6, 0}; return (flag); for (i=0;i&9;i++) }{注意函数的定义与引用的} printf(&a&b%d 次\na=b%d 次\na&b%d 次\n&,n,m,k); 书写格式!!}if (compare(a[i],b[i])==1) n++; else if (compare(a[i],b[i])==0) m++; else k++;值传递—函数参数用变量定义(复习)void main() { int x,y; …. z=fn(x,y); …} 实参单元 X y 调用前 X y 实参单元复制 Xy 调用 int fn(int x,int y){…... …}形参单元实参单元 X 保留原值 y 保留原值调用后2 、数组名作函数参数地址传递在主调函数与被调函数中分别定义数组, 且类型应一致 形参数组大小(多维数组第一维)可不指定 形参数组名是地址变量地址传递方式: 函数调用时,将数据的存储地址作为参数传递给形参。特点: 形参与实参占用同样的存储单元; “双向”传递;
实参和形参必须是地址常量或变量。例求 10 个学生一门课的平均成绩.float average(int stu[10], int n); void float average(int stu[], int n) main() { intscore[10], { float aver,total=0; printf(&Input 10 scores:\n&);for (i=0; i&n; i++) for ( i=0; i&10; i++ ) scanf(&%d&, &score[i]); total += stu[i];av=average(score,10); aver = total/n; printf(&Average is : %.2f&, av); return(aver); } } score82 56 73 … 45 76 84 81 stu数组元素与数组名作函数参数的比较(数组元素)void swap2(int x,int y) 形参用变量定义{ z=x; x=y; y=z; 值传递} void main(){ int a[2]={1,2}; 实参用数组元素 swap2(a[0],a[1]);printf(&a[0]=%d\na[1]=%d\n&,a[0],a[1]); }形参单元1 2aa[0] a[1] 1 2 调用前aa[0] a[1] 1X2a1 22调用1 y 交换返回数组元素与数组名作函数参数的比较(数组名)void swap2(int x[]) 形参用数组定义{ z=x[0]; x[0]=x[1]; x[1]=z; } 地址传递void main() { int a[2]={1,2}; 实参用数组名 swap2(a);printf(&a[0]=%d\na[1]=%d\n&,a[0],a[1]); } a1 2 调用前 aa x 1 2 调用 aa x a 2 1 2 1交换返回例:用选择法对数组中 10 个整数按由小到大排序函数 sort(int array[], int n): 数组元素小大排序主函数 main() : 输入 array 数组,调用 sort 函数排序,输出排序后的 array 数组。void sort (int array[ ], int n ) { int i, j, k, 这是数组作形参 for (i=0; i&n-1;i++) 时常使用的技巧! { k=i; for (j=i+1; j&n; j++) if (array[j]&array[k]) k=j;t=array[k]; array[k]=array[i]; array[i]=t; } }void main ( ) { int a[10], printf(&enter array: \n&); for (i=0; i&10; i++) scanf(&%d&,&a[i]); sort(a, 10); //由于地址传递,实参数组//a 的值被改变 printf(&thesorted array: \n&); for (i=0; i&10; i++) printf (& %d &, a[i]); printf(&\n&); }例:求二维数组中各行元素之和void get_sum_row(int x[][3], int result[] ,int row, int col) { int i,j; for(i=0;i&i++) result { a sum result[i]=0; 18 x 3 6 9 for (j=0;j&j++) 12 1 47 result[i]+=x[i][j]; } } void main() { int a[2][3]={3,6,9,1,4,7}; intsum[2],row=2,col=3,i; get_sum_row(a,sum,row,col); for (i=0;i&i++) printf(&Thesum of row[%d]=%d\n&,i+1,sum[i]); }数组名做参数的好处:⑴由于只需复制一个地址值,而无须复制全部需要处理的数据,因此节约存储空间并提高效率。⑵由于主调函数和被调函数是在相同的内存区域上对数据进行操作, 因此可以实现数据的同步更新。8 局部变量和全局变量 8int main()局部变量:在函数内部定义的变量叫~, 它在本函数内有效,形参是局部变量。float f1(int a) { int x,b,c; … a,b,c,x 有效区} char f2(int x,int y) { int i,j; …x,y,i,j 有效区} {int y m,n; …y,m,n 有效区}不同的函数可以使用相同的局部变量名, 彼此互不干扰。全局变量:在函数体之外定义的变量叫外部变量, 外部变量是全局变量。有效范围:从定义的位置开始到最后。int p=1,q=5; /*全局变量*/ float f1(int a) { int b,c; …} char c1,c2; /*全局变量*/ char f2(int x, int y); { int i,j; 全局变量… c1,c2 的} 作用范围 main() { intx,y; …}全局变量 p,q 的作用范围例 8 计算数组的最大值、最小值和平均值。 15float max=0,min=0;//定义 min,max 为全局变量 float average(float array[], int n){ void main() float aver,sum=array[0];//注意 sum 的初值{ max=min=array[0];float ave,score[10]; for (i=1;i&n;i++) { if (array[i]&max) max=array[i]; for(i=0;i&10;i++) if (array[i]&min) min=array[i]; scanf(&%f&,&score[i]);sum+=array[i]; ave=average(score,10); } printf(&\nmax=%6.2f\nmin=aver=sum/n; %6.2f\naverage=%6.2f \n&,max,min,ave); return (aver); //返回平均值} }当全局变量与局部变量同名时, 局部变量优先int a=3,b=5; //全局变量 a,b int max(int a,int b) {c=a&b ? a:b; // a,b 为 max 函数的局部变量 return (c);} void main() { int a=8; //a 为 main 函数的局部变量 printf(“%d”,max(a,b)); 运行结果为 8 }全局变量副作用void part();void main() { for (i=0;i&5;i++) part(); 思考: } 若改为 i&6 运行结果? void part() 或i&7 ? { for (i=0;i&5;i++) printf(“*”); printf(“\n”); }打印:***** ***** ***** ***** *****从程序设计的观点看使用全局变量: 优点:⑴增加了函数间数据联系。同一文件中的一些函数引用全局变量, 当某个函数中改变了全局变量的值,其它函数中的全局变量值也随之改变。⑵函数可以得到多个返回值缺点:⑴全局变量在程序的全部执行过程中都占用存储单元,浪费内存资源。⑵使用全局变量不符合程序设计中要求模块间“强内聚性、弱偶合性”的原则。⑶使用全局变量过多,会降低程序的可读性和可维护性。建议少用全局变量!课堂练习:写出下面程序的输出结果char array[9]={&AAAA&}; void f(char note) { char array[9]; strcpy(array, &BBBB&);note = 'A'; printf(&%s\n %c&,array,note); } void main() { char note ='D';printf(&%s %c\n&,array,note); AAAA D strcpy(array, &&); f(note); BBBB Aprintf(&%s %c\n&,array,note);
D }8.9 变量的存储类别变量是对程序中数据的存储空间的抽象, 编译或函数调用时为其分配内存单元。内存int main() { a=10; printf(&%d&,a); return 0; }1byte 8bit程序中使用变量名对内存操作…….一、静态存储方式和动态存储方式静态存储方式:程序运行期间分配固定存储空间的方式。动态存储方式:程序运行期间根据需要进行动态的分配存储空间的方式。程序区静态存储区全局变量,局部静态变量形式参数局部变量(自动) 函数调用的现场保护和返回地址动态存储区二、静态存储变量和动态存储变量静态存储变量:用静态存储方式存储的变量。特点:在静态存储区分配存储单元,整个程序运行期间都不释放。动态存储变量:用动态存储方式存储的变量。特点:函数开始调用时为变量分配存储空间, 函数结束时释放这些空间。一个程序两次调用同一函数,其中同一个局部变量的内存地址可能不同。三、变量的属性及其定义如: 操作属性:变量所持有的数据的性质;sum, float 存储属性:变量存储的位置。 auto int a,b,c; 存储器的类型:寄存器、静态存储区、动态存储区。static float x,y 生存期:变量在某一时刻存在--静态变量与动态变量 extern floatmin, 作用域:变量在某区域内有效--局部变量与全局变量 变量的存储类型– auto----- 自动型– register-----寄存器型– static ------ 静态型– extern ----- 外部型 变量的数据类型: –int ---整型–float ---实型–char ---字符型–数组、结构、指针变量定义格式:[存储类型] 数据类型变量表;四、变量的存储类型1. Auto 变量⑴说明局部变量是自动变量, auto 只适用于说明局部变量;例如:在一函数内有定义 则定义 x 为自动变量(即存储类型是自动的), 其数据类型是整型的。⑵自动变量存放在动态存储区,属于动态存储变量; ⑶此类变量的作用域是其所在的函数内部,即程序在未进入函数之前或退出函数之后,其内部所定义的所有自动变量都没有意义。说明: ⑴在一个函数内如果局部变量不作存储类型说明,均为自动变量;如:int b, c=3 等价于 auto int b, c=3;⑵形式参数缺省存储类型是 auto,但不能将 auto 加在形参说明之前。╳ 2. 用 static 声明局部变量static 可用于说明如:int max(auto int x, auto int y){……}╳局部变量——静态局部变量全局变量——静态外部变量静态局部变量⑴静态局部变量作用域仅限于定义它的函数内部。⑵存放在静态存储区,整个程序运行期间都不释放。而自动变量函数调用结束后即释放。⑶编译时赋初值,每次调用时不再赋初值,只保留调用结束时变量的值。而自动变量调用一次,重新赋值一次。例如:打印 1!~5!main() { for(i=1;i&=5;i++) ⑷如果局部静态变量不赋初值,编译时自动赋 0。printf(“%d!=%d\n”,i,fac(i)); 而自动变量不赋初值,其值不确定。}int fac(int n) { static int f=1; f=f*n; return (f); }【例】求程序运行结果void f(int a); void main() { int a=2,i; for (i=0;i&3;i++) printf(&%4d&,f(a)); } voidf(int a) { int b=0; static int c=3; b++; c++; return (a+b+c); } 变量及函数的值分析:a 2 2 2ibcf(a) 7 8 9 90 0→1 4 1 0→1 5 2 0→1 6 8【结果】 7课堂练习:求程序运行结果void increment() { int x=0; x++; printf(“%d\n”,x); } void main() { increment();increment(); increment(); } 运行结果:1 1 1void increment() { static int x=0; x++; printf(“%d\n”,x); } void main(){ increment(); increment(); increment(); } 运行结果:1 2 34.用 extern 声明外部变量(1)在一个文件内声明外部变量,扩展它的作用域extern int a,b; //声明外部变量 int max() { z=a&b?a:b; return (z); } void main(){ printf(&max=%d&,max()); } int a=13,b=-8; //定义全局变量⑵在多个文件的程序中声明外部变量 main() { . . } func2() { . . . }file2.cppfloat x=1.0f; func3() { . . . }变量global可在变量global和x file2.cpp文件中变量number仅在可在file1.cpp文使用,number 不 file3.cpp 文件中使用, x 其它文件可调用。能使用。件中使用。file1.cppfile3.cpp例引用其它文件中的变量,输出和 a 的 m 次方。#include &stdio.h& #include “power.h” int power(int n) { int i,y=1;//定义外部变量 main() for(i=1;i&=n;i++) { y*=a; int b=3,c,d,m; return(y);printf(&Enter the number a and its power:\n&); } scanf(&%d,%d&,&a,&m);power.cpp 文件 c=a*b; printf(&%d*%d=%d\n&,a,b,c); #ifndef POWER_H d=power(m);#define POWER_H printf(&%d**%d=%d\n&,a,m,d); int power(int); #endif }main.cpp 文件power.h 文件生成自己的头文件#ifndef POWER_H #define POWER_H …//函数声明 Workspacepower:projects(1) powerfiles #endifSource Files main.cpp power.cpp Header Files power.h工作空间文件目录结构8.10 内部函数和外部函数一、内部函数(static)定义:如果一个函数只能被本文件中其它函数调用,称为内部函数(又称静态函数)。格式:static 类型标识符函数名(形参表)例如:static int fun(a, b) { ..…. }作用:函数的作用域限于所在文件,不同文件中同名函数互不干扰,便于程序的局部化。二、外部函数(extern)定义:如果一个函数允许被其它文件调用,称为外部函数。格式: extern 类型标识符函数名(形参表)或类型标识符函数名(形参表)例如:extern int fun(a, b) { ..…. }=int fun(a, b) { ..…. }通常不加 static 标识符的函数都是外部函数。在需要调用此函数的文件中,一般要用extern 说明所用的函数是外部函数。课堂练习递归 1、直接或间接调用自身的函数称为______函数。 auto register _____和 2、存储类别说明符有_____、____、 extern static _____。外部 3、在函数之外定义的变量称之为______变量或全局_____变量. 局部变量 4、只能被定义它的函数内部所识别的变量为_____ 5、要想使函数中的局部变量在函数调用之间保持其 static 值,该变量必须用存储类别说明符______声明. return 6、被调用函数中的_____ 语句用来把表达式的值传回给调用函数。习题 8.7写一函数,将一个字符串中的元音字母复制到另一字符串,然后输出。void strvowel(char a[50],char b[50]) { int n=0,i; for (i=0;a[i]!=\0;i++) 、 if(a[i]=='a'||a[i]=='e'||a[i]=='i'||a[i]=='o'||a[i]=='u') { b[n]=a[i]; void main()n++; { } char a[50]={&I love you&},b[50]; b[n]=\0 strvowel(a,b); }printf(“%s\n”,b); }上机调试七播放器加载中,请稍候...
该用户其他文档
文档介绍:
北京航空航天大学C语言i第九讲(第八章) 函数2.ppt 北京航空航天大学 C 语言 i 第九讲(第八章) 函数 2第八章函数教学目标掌握函数调用参数的值传递和地址传递如何使用局部变量和全局变量及静态变量如何运行一个多文件的程序8.7数组作为函数的参数1、数组元素做函数的实参---值传递例:比较整型数组 a[9]和 b[9]对应的元素,分别...
内容来自淘豆网转载请标明出处.
时间: 21:54关于C语言编程的问题_百度知道
关于C语言编程的问题
func (int a,int b){ static int m=0,i=2; i+=m+1; m=i+a+b; }#include&stdio.h&int main(){ int k=4,m=1,p1,p2; p1=func(k,m); p2=func(k,m); printf(&%d,%d\n&,p1,p2); return 0;}求教大神这条题目的解释,为什么p1不等于p2?
提问者采纳
static int m=0,i=2;
//static表示静态存储,也就是第一次调用函数时候赋值,第二次直接调用而不是重新定义赋值
其他类似问题
按默认排序
其他5条回答
你p1=func(k,m);就调用 了一次func(),所以m就成func()的返回值了,而不再是初始值1.
static int m=0,i=2; 指明 m是静态的,
p2=func(k,m); 调用这个函数的时候, m是p1=func(k,m);求得的m 而不是0.
m =2; 分成2步去看, 比较容易理解
因为函数func (int a,int b)中变量 static int m=0,i=2;是static(静态变量)这种变量可理解为全局变量语句static int m=0,i=2;在程序运行期间只被执行一次,第二次调用函数时,m、i的值已经改变了所以结果也不一样了这样解释你能明白吗
这是static的问题喽~ 函数中的m和i的声明中有static,也就是说,在func函数调用结束后,m和i的值仍然保留,到下次再调用这个函数时继续使用,一直到main函数结束,这两个变量的存储空间才会被释放。两次调用函数时m和i的值是不同的,所以算出来的p1和p2也就不同了。
静态局部变量属于静态存储方式,它具有以下特点:(1)静态局部变量在函数内定义
它的生存期为整个源程序,但是其作用域仍与自动变量相同,只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。(2)允许对构造类静态局部量赋初值
例如数组,若未赋以初值,则由系统自动赋以0值。(3)对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。而对自动变量不赋初值,则其值是不定的。 根据静态局部变量的特点, 可以 看出它是一种生存期为整个源程序的量。虽然离开定义它的函数后不能使用,但如再次调用定义它的函数时,它又可继续使用, 而且保存了前次被调用后留下的 值。 因此,当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的,但全局变量有时会造成 意外的副作用,因此仍以采用局部静态变量为宜。所以第二次调用的时候
函数func里面的m和i就和第一次不同
c语言编程的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁您现在的位置: &
C语言程序设计基础讲座之函数
C语言程序设计基础讲座之函数
概述   在第一章中已经介绍过,C源程序是由函数组成的。 虽然在前面各章的程序中都只有一个主函数main(), 但实用程序往往由多个函数组成。函数是C源程序的基本模块, 通过对函数模块的调用实现特定的功能。C语言中的函数相当于其它高级语言的子程序。 C语言不仅提供了极为丰富的库函数(如Turbo C,MS C 都提供了三百多个库函数),还允许用户建立自己定义的函数。用户可把自己的算法编成一个个相对独立的函数模块,然后用调用的方法来使用函数。  可以说C程序的全部工作都是由各式各样的函数完成的, 所以也把C语言称为函数式语言。 由于采用了函数模块式的结构, C语言易于实现结构化程序设计。使程序的层次结构清晰,便于程序的编写、阅读、调试。  在C语言中可从不同的角度对函数分类。  1. 从函数定义的角度看,函数可分为库函数和用户定义函数两种。  (1)库函数  由C系统提供,用户无须定义, 也不必在程序中作类型说明,只需在程序前包含有该函数原型的头文件即可在程序中直接调用。在前面各章的例题中反复用到printf 、 scanf 、 getchar 、putchar、gets、puts、strcat等函数均属此类。  (2)用户定义函数  由用户按需要写的函数。对于用户自定义函数, 不仅要在程序中定义函数本身, 而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能使用。  2. C语言的函数兼有其它语言中的函数和过程两种功能,从这个角度看,又可把函数分为有返回值函数和无返回值函数两种。  (1)有返回值函数  此类函数被调用执行完后将向调用者返回一个执行结果, 称为函数返回值。如数学函数即属于此类函数。 由用户定义的这种要返回函数值的函数,必须在函数定义和函数说明中明确返回值的类型。  (2)无返回值函数  此类函数用于完成某项特定的处理任务, 执行完成后不向调用者返回函数值。这类函数类似于其它语言的过程。 由于函数无须返回值,用户在定义此类函数时可指定它的返回为“空类型”, 空类型的说明符为“void”。  3. 从主调函数和被调函数之间数据传送的角度看又可分为无参函数和有参函数两种。  (1)无参函数  函数定义、函数说明及函数调用中均不带参数。 主调函数和被调函数之间不进行参数传送。 此类函数通常用来完成一组指定的功能,可以返回或不返回函数值。  (2)有参函数  也称为带参函数。在函数定义及函数说明时都有参数, 称为形式参数(简称为形参)。在函数调用时也必须给出参数, 称为实际参数(简称为实参)。 进行函数调用时,主调函数将把实参的值传送给形参,供被调函数使用。  4. C语言提供了极为丰富的库函数, 这些库函数又可从功能角度作以下分类。  (1)字符类型分类函数  用于对字符按ASCII码分类:字母,数字,控制字符,分隔符,大小写字母等。  (2)转换函数  用于字符或字符串的转换;在字符量和各类数字量 (整型, 实型等)之间进行转换;在大、小写之间进行转换。  (3)目录路径函数  用于文件目录和路径操作。  (4)诊断函数  用于内部错误检测。  (5)图形函数  用于屏幕管理和各种图形功能。   (6)输入输出函数  用于完成输入输出功能。  (7)接口函数  用于与DOS,BIOS和硬件的接口。  (8)字符串函数   用于字符串操作和处理。  (9)内存管理函数  用于内存管理。  (10)数学函数  用于数学函数计算。  (11)日期和时间函数  用于日期,时间转换操作。  (12)进程控制函数  用于进程管理和控制。  (13)其它函数  用于其它各种功能。    以上各类函数不仅数量多,而且有的还需要硬件知识才会使用,因此要想全部掌握则需要一个较长的学习过程。 应首先掌握一些最基本、 最常用的函数,再逐步深入。由于篇幅关系,本书只介绍了很少一部分库函数, 其余部分读者可根据需要查阅有关手册。  还应该指出的是,在C语言中,所有的函数定义,包括主函数main在内,都是平行的。也就是说,在一个函数的函数体内, 不能再定义另一个函数, 即不能嵌套定义。但是函数之间允许相互调用,也允许嵌套调用。习惯上把调用者称为主调函数。 函数还可以自己调用自己,称为递归调用。main 函数是主函数,它可以调用其它函数,而不允许被其它函数调用。 因此,C程序的执行总是从main函数开始, 完成对其它函数的调用后再返回到main函数,最后由main函数结束整个程序。一个C源程序必须有,也只能有一个主函数main。 函数定义的一般形式  1.无参函数的一般形式   类型说明符 函数名()   {    类型说明    语句   }  其中类型说明符和函数名称为函数头。 类型说明符指明了本函数的类型,函数的类型实际上是函数返回值的类型。 该类型说明符与第二章介绍的各种说明符相同。 函数名是由用户定义的标识符,函数名后有一个空括号,其中无参数,但括号不可少。{} 中的内容称为函数体。在函数体中也有类型说明, 这是对函数体内部所用到的变量的类型说明。在很多情况下都不要求无参函数有返回值, 此时函数类型符可以写为void。  我们可以改为一个函数定义: void Hello(){ printf ("Hello,world \n");}  这里,只把main改为Hello作为函数名,其余不变。Hello 函数是一个无参函数,当被其它函数调用时,输出Hello world字符串。  2.有参函数的一般形式  类型说明符 函数名(形式参数表)   型式参数类型说明   {    类型说明    语句   }  有参函数比无参函数多了两个内容,其一是形式参数表, 其二是形式参数类型说明。在形参表中给出的参数称为形式参数, 它们可以是各种类型的变量, 各参数之间用逗号间隔。在进行函数调用时,主调函数将赋予这些形式参数实际的值。 形参既然是变量,当然必须给以类型说明。例如,定义一个函数, 用于求两个数中的大数,可写为:int max(a,b)int a,b;{if (a&b)}   第一行说明max函数是一个整型函数,其返回的函数值是一个整数。形参为a,b。第二行说明a,b均为整型量。 a,b 的具体值是由主调函数在调用时传送过来的。在{}中的函数体内, 除形参外没有使用其它变量,因此只有语句而没有变量类型说明。 上边这种定义方法称为“传统格式”。 这种格式不易于编译系统检查,从而会引起一些非常细微而且难于跟踪的错误。ANSI C 的新标准中把对形参的类型说明合并到形参表中,称为“现代格式”。  例如max函数用现代格式可定义为:int max(int a,int b){if(a&b)}  现代格式在函数定义和函数说明(后面将要介绍)时, 给出了形式参数及其类型,在编译时易于对它们进行查错, 从而保证了函数说明和定义的一致性。例1.3即采用了这种现代格式。 在max函数体中的return语句是把a(或b)的值作为函数的值返回给主调函数。有返回值函数中至少应有一个return语句。 在C程序中,一个函数的定义可以放在任意位置, 既可放在主函数main之前,也可放在main之后。例如例1.3中定义了一个max 函数,其位置在main之后, 也可以把它放在main之前。  修改后的程序如下所示。int max(int a,int b){if(a&b)}void main(){int max(int a,int b);int x,y,z;printf("input two numbers:\n");scanf("%d%d",&x,&y);z=max(x,y);printf("maxmum=%d",z);}  现在我们可以从函数定义、 函数说明及函数调用的角度来分析整个程序,从中进一步了解函数的各种特点。程序的第1行至第5行为max函数定义。进入主函数后,因为准备调用max函数,故先对max函数进行说明(程序第8行)。函数定义和函数说明并不是一回事,在后面还要专门讨论。 可以看出函数说明与函数定义中的函数头部分相同,但是末尾要加分号。程序第12 行为调用max函数,并把x,y中的值传送给max的形参a,b。max函数执行的  结果 (a或b)将返回给变量z。最后由主函数输出z的值。  函数调用的一般形式前面已经说过,在程序中是通过对函数的调用来执行函数体的,其过程与其它语言的子程序调用相似。C语言中, 函数调用的一般形式为:   函数名(实际参数表) 对无参函数调用时则无实际参数表。 实际参数表中的参数可以是常数,变量或其它构造类型数据及表达式。 各实参之间用逗号分隔。'Next of Page在C语言中,可以用以下几种方式调用函数:  1.函数表达式  函数作表达式中的一项出现在表达式中,以函数返回值参与表达式的运算。这种方式要求函数是有返回值的。例如: z=max(x,y)是一个赋值表达式,把max的返回值赋予变量z。'Next of Page  2.函数语句  函数调用的一般形式加上分号即构成函数语句。例如: printf ("%D",a);scanf ("%d",&b);都是以函数语句的方式调用函数。  3.函数实参  函数作为另一个函数调用的实际参数出现。 这种情况是把该函数的返回值作为实参进行传送,因此要求该函数必须是有返回值的。例如: printf("%d",max(x,y)); 即是把max调用的返回值又作为printf函数的实参来使用的。在函数调用中还应该注意的一个问题是求值顺序的问题。 所谓求值顺序是指对实参表中各量是自左至右使用呢,还是自右至左使用。 对此, 各系统的规定不一定相同。在3.1.3节介绍printf 函数时已提  到过,这里从函数调用的角度再强调一下。 看例5.2程序。void main(){int i=8;printf("%d\n%d\n%d\n%d\n",++i,--i,i++,i--);}  如按照从右至左的顺序求值。例5.2的运行结果应为:  8  7  7  8  如对printf语句中的++i,--i,i++,i--从左至右求值,结果应为:  9  8  8  9  应特别注意的是,无论是从左至右求值, 还是自右至左求值,其输出顺序都是不变的, 即输出顺序总是和实参表中实参的顺序相同。由于Turbo C现定是自右至左求值,所以结果为8,7,7,8。上述问题如还不理解,上机一试就明白了。函数的参数和函数的值 一、函数的参数  前面已经介绍过,函数的参数分为形参和实参两种。 在本小节中,进一步介绍形参、实参的特点和两者的关系。 形参出现在函数定义中,在整个函数体内都可以使用, 离开该函数则不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。 形参和实参的功能是作数据传送。发生函数调用时, 主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送。  函数的形参和实参具有以下特点:  1.形参变量只有在被调用时才分配内存单元,在调用结束时, 即刻释放所分配的内存单元。因此,形参只有在函数内部有效。 函数调用结束返回主调函数后则不能再使用该形参变量。  2.实参可以是常量、变量、表达式、函数等, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值。  3.实参和形参在数量上,类型上,顺序上应严格一致, 否则会发生“类型不匹配”的错误。  4.函数调用中发生的数据传送是单向的。 即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。 因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。例5.3可以说明这个问题。void main(){printf("input number\n");scanf("%d",&n);s(n);printf("n=%d\n",n);}int s(int n){for(i=n-1;i&=1;i--)n=n+i;printf("n=%d\n",n);}  本程序中定义了一个函数s,该函数的功能是求∑ni=1i 的值。在主函数中输入n值,并作为实参,在调用时传送给s 函数的形参量n( 注意,本例的形参变量和实参变量的标识符都为n, 但这是两个不同的量,各自的作用域不同)。 在主函数中用printf 语句输出一次n值,这个n值是实参n的值。在函数s中也用printf 语句输出了一次n值,这个n值是形参最后取得的n值0。从运行情况看,输入n值为100。即实参n的值为100。把此值传给函数s时,形参 n 的初值也为100,在执行函数过程中,形参n的值变为5050。 返回主函数之后,输出实参n的值仍为100。可见实参的值不随形参的变化而变化。  二、函数的值  函数的值是指函数被调用之后, 执行函数体中的程序段所取得的并返回给主调函数的值。如调用正弦函数取得正弦值,调用例5.1的max函数取得的最大数等。对函数的值(或称函数返回值)有以下一些说明:  1. 函数的值只能通过return语句返回主调函数。return 语句的一般形式为:   return 表达式;   或者为:  return (表达式);  该语句的功能是计算表达式的值,并返回给主调函数。 在函数中允许有多个return语句,但每次调用只能有一个return 语句被执行, 因此只能返回一个函数值。  2. 函数值的类型和函数定义中函数的类型应保持一致。 如果两者不一致,则以函数类型为准,自动进行类型转换。  3. 如函数值为整型,在函数定义时可以省去类型说明。  4. 不返回函数值的函数,可以明确定义为“空类型”, 类型说明符为“void”。如例5.3中函数s并不向主函数返函数值,因此可定义为:void s(int n){ ……}  一旦函数被定义为空类型后, 就不能在主调函数中使用被调函数的函数值了。例如,在定义s为空类型后,在主函数中写下述语句 sum=s(n); 就是错误的。为了使程序有良好的可读性并减少出错, 凡不要求返回值的函数都应定义为空类型。函数说明在主调函数中调用某函数之前应对该被调函数进行说明, 这与使用变量之前要先进行变量说明是一样的。 在主调函数中对被调函数作说明的目的是使编译系统知道被调函数返回值的类型, 以便在主调函数中按此种类型对返回值作相应的处理。 对被调函数的说明也有两种格式,一种为传统格式,其一般格式为: 类型说明符 被调函数名(); 这种格式只给出函数返回值的类型,被调函数名及一个空括号。  这种格式由于在括号中没有任何参数信息, 因此不便于编译系统进行错误检查,易于发生错误。另一种为现代格式,其一般形式为:   类型说明符 被调函数名(类型 形参,类型 形参…);   或为:  类型说明符 被调函数名(类型,类型…);   现代格式的括号内给出了形参的类型和形参名, 或只给出形参类型。这便于编译系统进行检错,以防止可能出现的错误。例5.1 main函数中对max函数的说明若  用传统格式可写为:int max();  用现代格式可写为:int max(int a,int b);  或写为:int max(int,int);  C语言中又规定在以下几种情况时可以省去主调函数中对被调函数的函数说明。  1. 如果被调函数的返回值是整型或字符型时, 可以不对被调函数作说明,而直接调用。这时系统将自动对被调函数返回值按整型处理。例5.3的主函数中未对函数s作说明而直接调用即属此种情形。  2. 当被调函数的函数定义出现在主调函数之前时, 在主调函数中也可以不对被调函数再作说明而直接调用。例如例5.1中, 函数max的定义放在main 函数之前,因此可在main函数中省去对 max函数的函数说明int max(int a,int b)。  3. 如在所有函数定义之前, 在函数外预先说明了各个函数的类型,则在以后的各主调函数中,可不再对被调函数作说明。例如:char str(int a);float f(float b);main(){……}char str(int a){……}float f(float b){……}  其中第一,二行对str函数和f函数预先作了说明。 因此在以后各函数中无须对str和f函数再作说明就可直接调用。  4. 对库函数的调用不需要再作说明, 但必须把该函数的头文件用include命令包含在源文件前部。数组作为函数参数数组可以作为函数的参数使用,进行数据传送。 数组用作函数参数有两种形式,一种是把数组元素(下标变量)作为实参使用; 另一种是把数组名作为函数的形参和实参使用。一、数组元素作函数实参数组元素就是下标变量,它与普通变量并无区别。 因此它作为函数实参使用与普通变量是完全相同的,在发生函数调用时, 把作为实参的数组元素的值传送给形参,实现单向的值传送。例5.4说明了这种情况。[例5.4]判别一个整数数组中各元素的值,若大于0 则输出该值,若小于等于0则输出0值。编程如下:void nzp(int v){if(v&0)printf("%d ",v);elseprintf("%d ",0);}main(){int a[5],i;printf("input 5 numbers\n");for(i=0;i&5;i++){scanf("%d",&a[i]);nzp(a[i]);}}void nzp(int v){ ……}main(){int a[5],i;printf("input 5 numbers\n");for(i=0;i&5;i++){ scanf("%d",&a[i]);nzp(a[i]);}}   本程序中首先定义一个无返回值函数nzp,并说明其形参v 为整型变量。在函数体中根据v值输出相应的结果。在main函数中用一个for 语句输入数组各元素, 每输入一个就以该元素作实参调用一次nzp函数,即把a[i]的值传送给形参v,供nzp函数使用。 二、数组名作为函数参数  用数组名作函数参数与用数组元素作实参有几点不同:  1. 用数组元素作实参时,只要数组类型和函数的形参变量的类型一致,那么作为下标变量的数组元素的类型也和函数形参变量的类型是一致的。因此, 并不要求函数的形参也是下标变量。 换句话说,对数组元素的处理是按普通变量对待的。用数组名作函数参数时, 则要求形参和相对应的实参都必须是类型相同的数组,都必须有明确的数组说明。当形参和实参二者不一致时,即会发生错误。  2. 在普通变量或下标变量作函数参数时,形参变量和实参变量是由编译系统分配的两个不同的内存单元。在函数调用时发生的值传送是把实参变量的值赋予形参变量。在用数组名作函数参数时,不是进行值的传送,即不是把实参数组的每一个元素的值都赋予形参数组的各个元素。因为实际上形参数组并不存在,编译系统不为形参数组分配内存。那么,数据的传送是如何实现的呢? 在第四章中我们曾介绍过,数组名就是数组的首地址。因此在数组名作函数参数时所进行的传送只是地址的传送, 也就是说把实参数组的首地址赋予形参数组名。形参数组名取得该首地址之后,也就等于有了实在的数组。实际上是形参数组和实参数组为同一数组,共同拥有一段内存空间。图5.1说明了这种情形。图中设a为实参数组,类型为整型。a占有以2000 为首地址的一块内存区。b为形参数组名。当发生函数调用时,进行地址传送, 把实参数 组a的首地址传送给形参数组名b,于是b也取得该地址2000。 于是a,b两数组共同占有以2000 为首地址的一段连续内存单元。从图中还可以看出a和b下标相同的元素实际上也占相同的两个内存单元(整型数组每个元素占二字节)。例如a[0]和b[0]都占用单元,当然a[0]等于b[0]。类推则有a[i]等于b[i]。  [例5.5]数组a中存放了一个学生5门课程的成绩,求平均成绩。float aver(float a[5]){float av,s=a[0]; for(i=1;i&5;i++) s=s+a[i];av=s/5;}void main(){float sco[5],printf("\ninput 5 scores:\n");for(i=0;i&5;i++)scanf("%f",&sco[i]);av=aver(sco);printf("average score is %5.2f",av);}float aver(float a[5]){ ……}void main(){……for(i=0;i&5;i++)scanf("%f",&sco[i]);av=aver(sco);……}   本程序首先定义了一个实型函数aver,有一个形参为实型数组a,长度为5。在函数aver中,把各元素值相加求出平均值,返回给主函数。主函数main 中首先完成数组sco的输入,然后以sco作为实参调用aver函数,函数返回值送av,最后输出av值。 从运行情况可以看出,程序实现了所要求的功能  3. 前面已经讨论过,在变量作函数参数时,所进行的值传送是单向的。即只能从实参传向形参,不能从形参传回实参。形参的初值和实参相同, 而形参的值发生改变后,实参并不变化, 两者的终值是不同的。例5.3证实了这个结论。 而当用数组名作函数参数时,情况则不同。 由于实际上形参和实参为同一数组, 因此当形参数组发生变化时,实参数组也随之变化。 当然这种情况不能理解为发生了“双向”的值传递。但从实际情况来看,调用函数之后实参数组的值将由于形参数组值的变化而变化。为了说明这种情况,把例5.4改为例5.6的形式。[例5.6]题目同5.4例。改用数组名作函数参数。void nzp(int a[5]){printf("\nvalues of array a are:\n");for(i=0;i&5;i++){if(a[i]&0) a[i]=0;printf("%d ",a[i]);}}main(){int b[5],i;printf("\ninput 5 numbers:\n");for(i=0;i&5;i++)scanf("%d",&b[i]);printf("initial values of array b are:\n");for(i=0;i&5;i++)printf("%d ",b[i]);nzp(b);printf("\nlast values of array b are:\n");for(i=0;i&5;i++)printf("%d ",b[i]);}void nzp(int a[5]){ …… }main(){int b[5],i;……nzp(b);……}  本程序中函数nzp的形参为整数组a,长度为 5。 主函数中实参数组b也为整型,长度也为5。在主函数中首先输入数组b的值,然后输出数组b的初始值。 然后以数组名b为实参调用nzp函数。在nzp中,按要求把负值单元清0,并输出形参数组a的值。 返回主函数之后,再次输出数组b的值。从运行结果可以看出,数组b 的初值和终值是不同的,数组b 的终值和数组a是相同的。这说明实参形参为同一数组,它们的值同时得以改变。 用数组名作为函数参数时还应注意以下几点:  a. 形参数组和实参数组的类型必须一致,否则将引起错误。  b. 形参数组和实参数组的长度可以不相同,因为在调用时,只传送首地址而不检查形参数组的长度。当形参数组的长度与实参数组不一致时,虽不至于出现语法错误(编译能通过),但程序执行结果将与实际不符,这是应予以注意的。如把例5.6修改如下:void nzp(int a[8]){printf("\nvalues of array aare:\n");for(i=0;i&8;i++){if(a[i]&0)a[i]=0;printf("%d",a[i]);}}main(){int b[5],i;printf("\ninput 5 numbers:\n");for(i=0;i&5;i++)scanf("%d",&b[i]);printf("initial values of array b are:\n");for(i=0;i&5;i++)printf("%d",b[i]);nzp(b);printf("\nlast values of array b are:\n");for(i=0;i&5;i++)printf("%d",b[i]);}  本程序与例5.6程序比,nzp函数的形参数组长度改为8,函数体中,for语句的循环条件也改为i&8。因此,形参数组 a和实参数组b的长度不一致。编译能够通过,但从结果看,数组a的元素a[5],a[6],a[7]显然是无意义的。c. 在函数形参表中,允许不给出形参数组的长度,或用一个变量来表示数组元素的个数。  例如:可以写为:void nzp(int a[])  或写为void nzp(int a[],int n)其中形参数组a没有给出长度,而由n值动态地表示数组的长度。n的值由主调函数的实参进行传送。  由此,例5.6又可改为例5.7的形式。  [例5.7]void nzp(int a[],int n){printf("\nvalues of array a are:\n");for(i=0;i&n;i++){if(a[i]&0) a[i]=0;printf("%d ",a[i]);}}main(){int b[5],i;printf("\ninput 5 numbers:\n");for(i=0;i&5;i++)scanf("%d",&b[i]);printf("initial values of array b are:\n");for(i=0;i&5;i++)printf("%d ",b[i]);nzp(b,5);printf("\nlast values of array b are:\n");for(i=0;i&5;i++)printf("%d ",b[i]);}void nzp(int a[],int n){ ……}main(){……nzp(b,5);……}  本程序nzp函数形参数组a没有给出长度,由n 动态确定该长度。在main函数中,函数调用语句为nzp(b,5),其中实参5将赋予形参n作为形参数组的长度。  d. 多维数组也可以作为函数的参数。 在函数定义时对形参数组可以指定每一维的长度,也可省去第一维的长度。因此,以下写法都是合法的。 int MA(int a[3][10])  或int MA(int a[][10])  函数的嵌套调用  C语言中不允许作嵌套的函数定义。因此各函数之间是平行的,不存在上一级函数和下一级函数的问题。 但是C语言允许在一个函数的定义中出现对另一个函数的调用。 这样就出现了函数的嵌套调用。即在被调函数中又调用其它函数。 这与其它语言的子程序嵌套的情形是类似的。其关系可表示如图5.2。  图5.2表示了两层嵌套的情形。其执行过程是:执行main函数中调用a函数的语句时,即转去执行a函数,在a函数中调用b 函数时,又转去执行b函数,b函数执行完毕返回a函数的断点继续执行,a 函数执行完毕返回main函数的断点继续执行。  [例5.8]计算s=22!+32!  本题可编写两个函数,一个是用来计算平方值的函数f1, 另一个是用来计算阶乘值的函数f2。主函数先调f1计算出平方值, 再在f1中以平方值为实参,调用 f2计算其阶乘值,然后返回f1,再返回主函数,在循环程序中计算累加和。long f1(int p){long f2(int);k=p*p;r=f2(k);}long f2(int q){long c=1;for(i=1;i&=q;i++)c=c*i;}main(){long s=0;for (i=2;i&=3;i++)s=s+f1(i);printf("\ns=%ld\n",s);}long f1(int p){……long f2(int);r=f2(k);……}long f2(int q){ ……}main(){ ……s=s+f1(i);……}  在程序中,函数f1和f2均为长整型,都在主函数之前定义, 故不必再在主函数中对f1和f2加以说明。在主程序中, 执行循环程序依次把i值作为实参调用函数f1求i2值。在f1中又发生对函数f2的调用,这时是把i2的值作为实参去调f2,在f2 中完成求i2! 的计算。f2执行完毕把C值(即i2!)返回给f1,再由f1 返回主函数实现累加。至此,由函数的嵌套调用实现了题目的要求。 由于数值很大, 所以函数和一些变量的类型都说明为长整型,否则会造成计算错误。 函数的递归调用  一个函数在它的函数体内调用它自身称为递归调用。 这种函数称为递归函数。C语言允许函数的递归调用。在递归调用中, 主调函数又是被调函数。执行递归函数将反复调用其自身。 每调用一次就进入新的一层。例如有函数f如下:int f (int x){z=f(y);}  这个函数是一个递归函数。 但是运行该函数将无休止地调用其自身,这当然是不正确的。为了防止递归调用无终止地进行, 必须在函数内有终止递归调用的手段。常用的办法是加条件判断, 满足某种条件后就不再作递归调用,然后逐层返回。 下面举例说明递归调用的执行过程。  [例5.9]用递归法计算n!用递归法计算n!可用下述公式表示:n!=1 (n=0,1)n×(n-1)! (n&1)按公式可编程如下:long ff(int n){if(n&0) printf("n&0,input error");else if(n==0n==1) f=1;else f=ff(n-1)*n;return(f);}main(){printf("\ninput a inteager number:\n");scanf("%d",&n);y=ff(n);printf("%d!=%ld",n,y);}long ff(int n){ ……else f=ff(n-1)*n;……}main(){ ……y=ff(n);……}   程序中给出的函数ff是一个递归函数。主函数调用ff 后即进入函数ff执行,如果n&0,n==0或n=1时都将结束函数的执行,否则就递归调用ff函数自身。由于每次递归调用的实参为n-1,即把n-1 的值赋予形参n,最后当n-1的值为1时再作递归调用,形参n的值也为1,将使递归终止。然后可逐层退回。下面我们再举例说明该过程。 设执行本程序时输入为5, 即求 5!。在主函数中的调用语句即为y=ff(5),进入ff函数后,由于n=5,不等于0或1,故应执行f=ff(n-1)*n,即f=ff(5-1)*5。该语句对ff作递归调用即ff(4)。 逐次递归展开如图5.3所示。进行四次递归调用后,ff函数形参取得的值变为1,故不再继续递归调用而开始逐层返回主调函数。ff(1)的函数返回值为1,ff(2)的返回值为1*2=2,ff(3)的返回值为2*3=6,ff(4) 的返回值为6*4=24,最后返回值ff(5)为24*5=120。  例5. 9也可以不用递归的方法来完成。如可以用递推法,即从1开始乘以2,再乘以3…直到n。递推法比递归法更容易理解和实现。但是有些问题则只能用递归算法才能实现。典型的问题是Hanoi塔问题。    [例5.10]Hanoi塔问题  一块板上有三根针,A,B,C。A针上套有64个大小不等的圆盘, 大的在下,小的在上。如图5.4所示。要把这64个圆盘从A针移动C针上,每次只能移动一个圆盘,移动可以借助B针进行。但在任何时候,任何针上的圆盘都必须保持大盘在下,小盘在上。求移动的步骤。  本题算法分析如下,设A上有n个盘子。  如果n=1,则将圆盘从A直接移动到C。  如果n=2,则:  1.将A上的n-1(等于1)个圆盘移到B上;  2.再将A上的一个圆盘移到C上;  3.最后将B上的n-1(等于1)个圆盘移到C上。  如果n=3,则:  A. 将A上的n-1(等于2,令其为n`)个圆盘移到B(借助于C),   步骤如下:  (1)将A上的n`-1(等于1)个圆盘移到C上,见图5.5(b)。  (2)将A上的一个圆盘移到B,见图5.5(c)  (3)将C上的n`-1(等于1)个圆盘移到B,见图5.5(d)  B. 将A上的一个圆盘移到C,见图5.5(e)  C. 将B上的n-1(等于2,令其为n`)个圆盘移到C(借助A),  步骤如下:  (1)将B上的n`-1(等于1)个圆盘移到A,见图5.5(f)  (2)将B上的一个盘子移到C,见图5.5(g)  (3)将A上的n`-1(等于1)个圆盘移到C,见图5.5(h)。  到此,完成了三个圆盘的移动过程。  从上面分析可以看出,当n大于等于2时, 移动的过程可分解为三个步骤:  第一步 把A上的n-1个圆盘移到B上;  第二步 把A上的一个圆盘移到C上;  第三步 把B上的n-1个圆盘移到C上;其中第一步和第三步是类同的。   当n=3时,第一步和第三步又分解为类同的三步,即把n`-1个圆盘从一个针移到另一个针上,这里的n`=n-1。 显然这是一个递归过程,据此算法可编程如下:move(int n,int x,int y,int z){if(n==1)printf("%c--&%c\n",x,z);else{move(n-1,x,z,y);printf("%c--&%c\n",x,z);move(n-1,y,x,z);}}main(){printf("\ninput number:\n");scanf("%d",&h);printf("the step to moving %2d diskes:\n",h);move(h,'a','b','c');}move(int n,int x,int y,int z){if(n==1)printf("%--&%c\n",x,z);else{move(n-1,x,z,y);printf("%c--&%c\n",x,z);move(n-1,y,x,z);}}main(){ ……move(h,'a','b','c');}  从程序中可以看出,move函数是一个递归函数,它有四个形参n,x,y,z。n表示圆盘数,x,y,z分别表示三根针。move 函数的功能是把x上的n个圆盘移动到z 上。当n==1时,直接把x上的圆盘移至z上,输出x→z。如n!=1则分为三步:递归调用move函数,把n-1个圆盘从x移到y;输出x→z;递归调用move函数,把n-1个圆盘从y移到z。在递归调用过程中n=n-1,故n的值逐次递减,最后n=1时,终止递归,逐层返回。当n=4 时程序运行的结果为input number:4the step to moving 4 diskes:a→ba→cb→ca→bc→ac→ba→ba→cb→cb→ac→ab→ca→ba→cb→c 变量的作用域  在讨论函数的形参变量时曾经提到, 形参变量只在被调用期间才分配内存单元,调用结束立即释放。 这一点表明形参变量只有在函数内才是有效的, 离开该函数就不能再使用了。这种变量有效性的范围称变量的作用域。不仅对于形参变量, C语言中所有的量都有自己的作用域。变量说明的方式不同,其作用域也不同。 C语言中的变量,按作用域范围可分为两种, 即局部变量和全局变量。  一、局部变量  局部变量也称为内部变量。局部变量是在函数内作定义说明的。其作用域仅限于函数内, 离开该函数后再使用这种变量是非法的。  例如:int f1(int a) /*函数f1*/{int b,c; ……}a,b,c作用域int f2(int x) /*函数f2*/{int y,z; }x,y,z作用域main(){int m,n; }  m,n作用域 在函数f1内定义了三个变量,a为形参,b,c为一般变量。在 f1的范围内a,b,c有效,或者说a,b,c变量的作用域限于f1内。同理,x,y,z的作用域限于f2内。 m,n的作用域限于main函数内。关于局部变量的作用域还要说明以下几点:  1. 主函数中定义的变量也只能在主函数中使用,不能在其它函数中使用。同时,主函数中也不能使用其它函数中定义的变量。因为主函数也是一个函数,它与其它函数是平行关系。这一点是与其它语言不同的,应予以注意。  2. 形参变量是属于被调函数的局部变量,实参变量是属于主调函数的局部变量。  3. 允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。如在例5.3 中,形参和实参的变量名都为n,是完全允许的。4. 在复合语句中也可定义变量,其作用域只在复合语句范围内。例如:main(){int s,a;……{s=a+b; ……b作用域 }……s,a作用域}[例5.11]main(){int i=2,j=3,k;k=i+j;{int k=8;if(i==3) printf("%d\n",k);}printf("%d\n%d\n",i,k);}main(){int i=2,j=3,k;k=i+j;{int k=8;if(i=3) printf("%d\n",k);}printf("%d\n%d\n",i,k);}   本程序在main中定义了i,j,k三个变量,其中k未赋初值。 而在复合语句内又定义了一个变量k,并赋初值为8。应该注意这两个k不是同一个变量。在复合语句外由main定义的k起作用,而在复合语句内则由在复合语句内定义的k起作用。因此程序第4行的k为main所定义,其值应为5。第7行输出k值,该行在复合语句内,由复合语句内定义的k起作用,其初值为8,故输出值为8,第9行输出i,k值。i是在整个程序中有效的,第7行对i赋值为3,故以输出也为3。而第9行已在复合语句之外,输出的k应为main所定义的k,此k值由第4 行已获得为5,故输出也为5。  二、全局变量  全局变量也称为外部变量,它是在函数外部定义的变量。 它不属于哪一个函数,它属于一个源程序文件。其作用域是整个源程序。在函数中使用全局变量,一般应作全局变量说明。 只有在函数内经过说明的全局变量才能使用。全局变量的说明符为extern。 但在一个函数之前定义的全局变量,在该函数内使用可不再加以说明。 例如:int a,b; /*外部变量*/void f1() /*函数f1*/{……}float x,y; /*外部变量*/ int fz() /*函数fz*/{……}main() /*主函数*/{……}/*全局变量x,y作用域 全局变量a,b作用域*/  从上例可以看出a、b、x、y 都是在函数外部定义的外部变量,都是全局变量。但x,y 定义在函数f1之后,而在f1内又无对x,y的说明,所以它们在f1内无效。 a,b定义在源程序最前面,因此在f1,f2及main内不加说明也可使用。  [例5.12]输入正方体的长宽高l,w,h。求体积及三个面x*y,x*z,y*z的面积。int s1,s2,s3;int vs( int a,int b,int c){v=a*b*c;s1=a*b;s2=b*c;s3=a*c;}main(){int v,l,w,h;printf("\ninput length,width and height\n");scanf("%d%d%d",&l,&w,&h);v=vs(l,w,h);printf("v=%d s1=%d s2=%d s3=%d\n",v,s1,s2,s3);}  本程序中定义了三个外部变量s1,s2,s3, 用来存放三个面积,其作用域为整个程序。函数vs用来求正方体体积和三个面积, 函数的返回值为体积v。由主函数完成长宽高的输入及结果输出。由于C语言规定函数返回值只有一个, 当需要增加函数的返回数据时,用外部变量是一种很好的方式。本例中,如不使用外部变量, 在主函数中就不可能取得v,s1,s2,s3四个值。而采用了外部变量, 在函数vs中求得的s1,s2,s3值在main 中仍然有效。因此外部变量是实现函数之间数据通讯的有效手段。对于全局变量还有以下几点说明:  1. 对于局部变量的定义和说明,可以不加区分。而对于外部变量则不然,外部变量的定义和外部变量的说明并不是一回事。外部变量定义必须在所有的函数之外,且只能定义一次。其一般形式为: [extern] 类型说明符 变量名,变量名… 其中方括号内的extern可以省去不写。  例如: int a,b;  等效于:  extern int a,b;   而外部变量说明出现在要使用该外部变量的各个函数内, 在整个程序内,可能出现多次,外部变量说明的一般形式为: extern 类型说明符 变量名,变量名,…; 外部变量在定义时就已分配了内存单元, 外部变量定义可作初始赋值,外部变量说明不能再赋初始值, 只是表明在函数内要使用某外部变量。  2. 外部变量可加强函数模块之间的数据联系, 但是又使函数要依赖这些变量,因而使得函数的独立性降低。从模块化程序设计的观点来看这是不利的, 因此在不必要时尽量不要使用全局变量。  3. 在同一源文件中,允许全局变量和局部变量同名。在局部变量的作用域内,全局变量不起作用。  [例5.13]int vs(int l,int w){v=l*w*h;}main(){extern int w,h;int l=5;printf("v=%d",vs(l,w));}int l=3,w=4,h=5;  本例程序中,外部变量在最后定义, 因此在前面函数中对要用的外部变量必须进行说明。外部变量l,w和vs函数的形参l,w同名。外部变量都作了初始赋值,mian函数中也对l作了初始化赋值。执行程序时,在printf语句中调用vs函数,实参l的值应为main中定义的l值,等于5,外部变量l在main内不起作用;实参w的值为外部变量w的值为4,进入vs后这两个值传送给形参l,wvs函数中使用的h 为外部变量,其值为5,因此v的计算结果为100,返回主函数后输出。变量的存储类型各种变量的作用域不同, 就其本质来说是因变量的存储类型相同。所谓存储类型是指变量占用内存空间的方式, 也称为存储方式。  变量的存储方式可分为“静态存储”和“动态存储”两种。   静态存储变量通常是在变量定义时就分定存储单元并一直保持不变, 直至整个程序结束。5.5.1节中介绍的全局变量即属于此类存储方式。动态存储变量是在程序执行过程中,使用它时才分配存储单元, 使用完毕立即释放。 典型的例子是函数的形式参数,在函数定义时并不给形参分配存储单元,只是在函数被调用时,才予以分配, 调用函数完毕立即释放。如果一个函数被多次调用,则反复地分配、 释放形参变量的存储单元。从以上分析可知, 静态存储变量是一直存在的, 而动态存储变量则时而存在时而消失。我们又把这种由于变量存储方式不同而产生的特性称变量的生存期。 生存期表示了变量存在的时间。 生存期和作用域是从时间和空间这两个不同的角度来描述变量的特性,这两者既有联系,又有区别。 一个变量究竟属于哪一种存储方式, 并不能仅从其作用域来判断,还应有明确的存储类型说明。  在C语言中,对变量的存储类型说明有以下四种:  auto     自动变量register   寄存器变量  extern    外部变量  static    静态变量   自动变量和寄存器变量属于动态存储方式, 外部变量和静态变量属于静态存储方式。在介绍了变量的存储类型之后, 可以知道对一个变量的说明不仅应说明其数据类型,还应说明其存储类型。 因此变量说明的完整形式应为: 存储类型说明符 数据类型说明符 变量名,变量名…; 例如:  static int a,b;           说明a,b为静态类型变量  auto char c1,c2;          说明c1,c2为自动字符变量  static int a[5]={1,2,3,4,5};    说明a为静整型数组  extern int x,y;           说明x,y为外部整型变量  下面分别介绍以上四种存储类型: 一、自动变量的类型说明符为auto  这种存储类型是C语言程序中使用最广泛的一种类型。C语言规定, 函数内凡未加存储类型说明的变量均视为自动变量, 也就是说自动变量可省去说明符auto。 在前面各章的程序中所定义的变量凡未加存储类型说明符的都是自动变量。例如:{ int i,j,k;……}等价于: { auto int i,j,k;……}  自动变量具有以下特点:  1. 自动变量的作用域仅限于定义该变量的个体内。在函数中定义的自动变量,只在该函数内有效。在复合语句中定义的自动变量只在该复合语句中有效。 例如: int kv(int a){auto int x,y;{ } /*c的作用域*/……} /*a,x,y的作用域*/  2. 自动变量属于动态存储方式,只有在使用它,即定义该变量的函数被调用时才给它分配存储单元,开始它的生存期。函数调用结束,释放存储单元,结束生存期。因此函数调用结束之后,自动变量的值不能保留。在复合语句中定义的自动变量,在退出复合语句后也不能再使用,否则将引起错误。例如以下程序: main(){ auto int a,s,p;printf("\ninput a number:\n");scanf("%d",&a);if(a&0){s=a+a;p=a*a;}printf("s=%d p=%d\n",s,p);}{printf("\ninput a number:\n");scanf("%d",&a);if(a&0){auto int s,p;s=a+a;p=a*a;}printf("s=%d p=%d\n",s,p);}  s,p是在复合语句内定义的自动变量,只能在该复合语句内有效。而程序的第9行却是退出复合语句之后用printf语句输出s,p的值,这显然会引起错误。  3. 由于自动变量的作用域和生存期都局限于定义它的个体内( 函数或复合语句内), 因此不同的个体中允许使用同名的变量而不会混淆。 即使在函数内定义的自动变量也可与该函数内部的复合语句中定义的自动变量同名。例5.14表明了这种情况。  [例5.14]main(){auto int a,s=100,p=100;printf("\ninput a number:\n");scanf("%d",&a);if(a&0){auto int s,p;s=a+a;p=a*a;printf("s=%d p=%d\n",s,p);}printf("s=%d p=%d\n",s,p);}  本程序在main函数中和复合语句内两次定义了变量s,p为自动变量。按照C语言的规定,在复合语句内,应由复合语句中定义的s,p起作用,故s的值应为a+ a,p的值为a*a。退出复合语句后的s,p 应为main所定义的s,p,其值在初始化时给定,均为100。从输出结果可以分析出两个s和两个p虽变量名相同, 但却是两个不同的变量。  4. 对构造类型的自动变量如数组等,不可作初始化赋值。 二、外部变量外部变量的类型说明符为extern  在前面介绍全局变量时已介绍过外部变量。这里再补充说明外部变量的几个特点:  1. 外部变量和全局变量是对同一类变量的两种不同角度的提法。全局变是是从它的作用域提出的,外部变量从它的存储方式提出的,表示了它的生存期。  2. 当一个源程序由若干个源文件组成时, 在一个源文件中定义的外部变量在其它的源文件中也有效。例如有一个源程序由源文件F1.C和F2.C组成: F1.Cint a,b; /*外部变量定义*/ /*外部变量定义*/main(){ ……}  F2.Cextern int a,b; /*外部变量说明*/ /*外部变量说明*/func (int x,y){……}  在F1.C和F2.C两个文件中都要使用a,b,c三个变量。在F1.C文件中把a,b,c都定义为外部变量。在F2.C文件中用extern把三个变量说明为外部变量,表示这些变量已在其它文件中定义,并把这些变量的类型和变量名,编译系统不再为它们分配内存空间。 对构造类型的外部变量, 如数组等可以在说明时作初始化赋值,若不赋初值,则系统自动定义它们的初值为0。 三、静态变量  静态变量的类型说明符是static。 静态变量当然是属于静态存储方式,但是属于静态存储方式的量不一定就是静态变量, 例如外部变量虽属于静态存储方式,但不一定是静态变量,必须由 static加以定义后才能成为静态外部变量,或称静态全局变量。 对于自动变量,前面已经介绍它属于动态存储方式。 但是也可以用static定义它为静态自动变量,或称静态局部变量,从而成为静态存储方式。由此看来, 一个变量可由static进行再说明,并改变其原有的存储方式。  1. 静态局部变量  在局部变量的说明前再加上static说明符就构成静态局部变量。  例如:static int a,b;static float array[5]={1,2,3,4,5};   静态局部变量属于静态存储方式,它具有以下特点:  (1)静态局部变量在函数内定义,但不象自动变量那样,当调用时就存在,退出函数时就消失。静态局部变量始终存在着,也就是说它的生存期为整个源程序。  (2)静态局部变量的生存期虽然为整个源程序,但是其作用域仍与自动变量相同,即只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。  (3)允许对构造类静态局部量赋初值。在数组一章中,介绍数组初始化时已作过说明。若未赋以初值,则由系统自动赋以0值。  (4)对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。而对自动变量不赋初值,则其值是不定的。 根据静态局部变量的特点, 可以看出它是一种生存期为整个源程序的量。虽然离开定义它的函数后不能使用,但如再次调用定义它的函数时,它又可继续使用, 而且保存了前次被调用后留下的值。 因此,当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的,但全局变量有时会造成意外的副作用,因此仍以采用局部静态变量为宜。  [例5.15]main(){void f(); /*函数说明*/for(i=1;i&=5;i++)f(); /*函数调用*/}void f() /*函数定义*/{auto int j=0;++j;printf("%d\n",j);}  程序中定义了函数f,其中的变量j 说明为自动变量并赋予初始值为0。当main中多次调用f时,j均赋初值为0,故每次输出值均为1。现在把j改为静态局部变量,程序如下:main(){void f();for (i=1;i&=5;i++)f();}void f(){static int j=0;++j;printf("%d\n",j);}void f(){static int j=0;++j;printf("%d/n",j);}  由于j为静态变量,能在每次调用后保留其值并在下一次调用时继续使用,所以输出值成为累加的结果。读者可自行分析其执行过程。  2.静态全局变量  全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在其它源文件中引起错误。从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。应予以注意。  四、寄存器变量  上述各类变量都存放在存储器内, 因此当对一个变量频繁读写时,必须要反复访问内存储器,从而花费大量的存取时间。 为此,C语言提供了另一种变量,即寄存器变量。这种变量存放在CPU的寄存器中,使用时,不需要访问内存,而直接从寄存器中读写, 这样可提高效率。寄存器变量的说明符是register。 对于循环次数较多的循环控制变量及循环体内反复使用的变量均可定义为寄存器变量。  [例5.16]求∑200i=1imain(){register i,s=0;for(i=1;i&=200;i++)s=s+i;printf("s=%d\n",s);}  本程序循环200次,i和s都将频繁使用,因此可定义为寄存器变量。对寄存器变量还要说明以下几点:  1. 只有局部自动变量和形式参数才可以定义为寄存器变量。因为寄存器变量属于动态存储方式。凡需要采用静态存储方式的量不能定义为寄存器变量。  2. 在Turbo C,MS C等微机上使用的C语言中, 实际上是把寄存器变量当成自动变量处理的。因此速度并不能提高。 而在程序中允许使用寄存器变量只是为了与标准C保持一致。3. 即使能真正使用寄存器变量的机器,由于CPU 中寄存器的个数是有限的,因此使用寄存器变量的个数也是有限的。 内部函数和外部函数  函数一旦定义后就可被其它函数调用。 但当一个源程序由多个源文件组成时, 在一个源文件中定义的函数能否被其它源文件中的函数调用呢?为此,C语言又把函数分为两类:  一、内部函数  如果在一个源文件中定义的函数只能被本文件中的函数调用,而不能被同一源程序其它文件中的函数调用, 这种函数称为内部函 数。定义内部函数的一般形式是: static 类型说明符 函数名(形参表) 例如: static int f(int a,int b)   内部函数也称为静态函数。但此处静态static 的含义已不是指存储方式,而是指对函数的调用范围只局限于本文件。 因此在不同的源文件中定义同名的静态函数不会引起混淆。  二、外部函数  外部函数在整个源程序中都有效,其定义的一般形式为: extern 类型说明符 函数名(形参表) 例如:  extern int f(int a,int b)  如在函数定义中没有说明extern或static则隐含为extern。在一个源文件的函数中调用其它源文件中定义的外部函数时,应 用extern说明被调函数为外部函数。例如:  F1.C (源文件一)main(){extern int f1(int i); /*外部函数说明,表示f1函数在其它源文件中*/……}F2.C (源文件二)extern int f1(int i); /*外部函数定义*/{……}   
&&&主编推荐
&&&热门试卷
&&&最新视频
&&&热门阅读
&&&最新问答
&&&&&&&&&&&&&&&
希赛网 版权所有 & &&&&湘教QS2-164&&增值电信业务经营许可证湘B2-

我要回帖

更多关于 c语言函数的调用 的文章

 

随机推荐