C语言 主函数中输入数组 怎样在子函数中调用该数组值

tuyekai 2015-07-18 03:11:15
RT
我想数组中输入3人的5门功课成绩,并利用子函数的方法,计算每门功课的平均值。编程如下
#include<stdio.h>
#include <stdlib.h>
void gkpj();
int main()
{
int a[3][5];
int i,j;
float sum=0,avg1=0,avg2=0;
int b[5],c[10];
printf("请分别输入10个学生的语文、英语、数学、体育、计算机5门课程的成绩:\n");
for(i=0;i<3;i++)
{
for(j=0;j<5;j++)
{
scanf("%d",&a[i][j]);}
}
for(i=0;i<3;i++)
{
for(j=0;j<5;j++)
{printf("%d ",a[i][j]);}
printf("\n");
}
gkpj(a[3][5]);
printf("%d",&b[5]);
}
void gkpj(int a[3][5]) //各科平均成绩
{int k,t,b[5],avg1;
float sum;
for(k=0;k<3;k++)
{
for(t=0;t<5;t++)
{
sum=sum+a[k][t]; //*(*(avg1+i)+j)
avg1=sum/10;
b[k]=avg1;
}

// printf("%d ",a[2][2]);
}
// printf("\n");
return b[5];
}
编译以后有错误
--------------------配置: mingw5 - CUI Debug, 编译器类型: MinGW--------------------

检查文件依赖性...
正在编译 C:\Users\Administrator\Desktop\C语言练习\1成绩管理小.c...
[Warning] C:\Users\Administrator\Desktop\C语言练习\1成绩管理小.c:41: warning: `return' with a value, in function returning void
[Warning] C:\Users\Administrator\Desktop\C语言练习\1成绩管理小.c:42:2: warning: no newline at end of file
正在连接...

完成构建 1成绩管理小: 0 个错误, 2 个警告
生成 C:\Users\Administrator\Desktop\C语言练习\1成绩管理小.exe

想问下 1、如何声明子函数 2、在子函数中调用main函数的数组
...全文
7356 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
hongss 2017-05-19
  • 打赏
  • 举报
回复
引用 3 楼 baidu_38833990 的回复:
自定义函数的返回值是什么?没有吗?
那是个 过程 不是 函数
baidu_38833990 2017-05-18
  • 打赏
  • 举报
回复
自定义函数的返回值是什么?没有吗?
二班的码农 2015-07-21
  • 打赏
  • 举报
回复
函数中对数组参数的声明,第一维长度是没有意义的, 二楼函数中第一个参数a[3][5]和a[][5]是等价的,楼主在调用的时候,应该把数组名传进去。gkpj(a[3][5])应该改为gkpj(a)。 数组传递,传的是地址,不是整个数组,所以实参和形参中的数组共有一个内存空间和数据
缘中人 2015-07-18
  • 打赏
  • 举报
回复
void gkpj(int a[3][5],int b[5]);

int main()
{
	int a[3][5];
	int i, j;
	float sum = 0, avg1 = 0, avg2 = 0;
	int b[5], c[10];
	printf("请分别输入10个学生的语文、英语、数学、体育、计算机5门课程的成绩:\n");
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			scanf("%d", &a[i][j]);
		}
	}
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
	gkpj(a,b);
	printf("%d", &b[5]);
}

void gkpj(int a[3][5],int b[5]) // 各科平均成绩
{
	int k, t, avg1;
	float sum;
	for (k = 0; k < 3; k++)
	{
		for (t = 0; t < 5; t++)
		{
			sum = sum + a[k][t]; // *(*(avg1+i)+j)
			avg1 = sum / 10;
			b[k] = avg1;
		}

		// printf("%d ",a[2][2]);
	}
	// printf("\n");
	return ;
}
适于初学者 第五章:函数 概述   在第一章已经介绍过,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) return a; else return b; }   第一行说明max函数是一个整型函数,其返回的函数是一个整数。形参为a,b。第二行说明a,b均为整型量。 a,b 的具体是由主调函数调用时传送过来的。在{}函数体内, 除形参外没有使用其它变量,因此只有语句而没有变量类型说明。 上边这种定义方法称为“传统格式”。 这种格式不易于编译系统检查,从而会引起一些非常细微而且难于跟踪的错误。ANSI C 的新标准把对形参的类型说明合并到形参表,称为“现代格式”。   例如max函数用现代格式可定义为: int max(int a,int b) { if(a>b) return a; else return 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)return a; else return 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() { int n; printf("input number\n"); scanf("%d",&n); s(n); printf("n=%d\n",n); } int s(int n) { int i; 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是形参最后取得的n0。从运行情况看,输入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); else printf("%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]都占用2000和2001单元,当然a[0]等于b[0]。类推则有a[i]等于b[i]。 [例5.5]数组a存放了一个学生5门课程的成绩,求平均成绩。 float aver(float a[5]) { int i; float av,s=a[0]; for(i=1;i<5;i++) s=s+a[i]; av=s/5; return av; } void main() { float sco[5],av; int i; 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]) { int i; 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]) { int i; 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) { int i; printf("\nvalues of array a are:\n"); for(i=0;i函数形参数组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=22!+32! 本题可编写两个函数,一个是用来计算平方函数f1, 另一个是用来计算阶乘函数f2。函数先调f1计算出平方, 再在f1以平方为实参,调用 f2计算其阶乘,然后返回f1,再返回函数,在循环程序计算累加和。 long f1(int p) { int k; long r; long f2(int); k=p*p; r=f2(k); return r; } long f2(int q) { long c=1; int i; for(i=1;i<=q;i++) c=c*i; return c; } main() { int i; 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求i2。在f1又发生对函数f2的调用,这时是把i2的作为实参去调f2,在f2 完成求i2! 的计算。f2执行完毕把C(即i2!)返回给f1,再由f1 返回函数实现累加。至此,由函数的嵌套调用实现了题目的要求。 由于数很大, 所以函数和一些变量的类型都说明为长整型,否则会造成计算错误。 函数的递归调用   一个函数在它的函数体内调用它自身称为递归调用。 这种函数称为递归函数。C语言允许函数的递归调用。在递归调用, 主调函数又是被调函数。执行递归函数将反复调用其自身。 每调用一次就进入新的一层。例如有函数f如下: int f (int x) { int y; z=f(y); return z; }   这个函数是一个递归函数。 但是运行该函数将无休止地调用其自身,这当然是不正确的。为了防止递归调用无终止地进行, 必须在函数内有终止递归调用的手段。常用的办法是加条件判断, 满足某种条件后就不再作递归调用,然后逐层返回。 下面举例说明递归调用的执行过程。 [例5.9]用递归法计算n!用递归法计算n!可用下述公式表示: n!=1 (n=0,1) n×(n-1)! (n>1) 按公式可编程如下: long ff(int n) { long f; if(n<0) printf("n<0,input error"); else if(n==0||n==1) f=1; else f=ff(n-1)*n; return(f); } main() { int n; long y; 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() { int h; 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: 4 the step to moving 4 diskes: a→b a→c b→c a→b c→a c→b a→b a→c b→c b→a c→a b→c a→b a→c b→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; …… { int b; 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) { int v; v=a*b*c; s1=a*b; s2=b*c; s3=a*c; return v; } 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) { extern int h; int v; v=l*w*h; return v; } 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; char c; …… }等价于: { auto int i,j,k; auto char c; …… }   自动变量具有以下特点: 1. 自动变量的作用域仅限于定义该变量的个体内。在函数定义的自动变量,只在该函数内有效。在复合语句定义的自动变量只在该复合语句有效。 例如: int kv(int a) { auto int x,y; { auto char c; } /*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); } 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.C int a,b; /*外部变量定义*/ char c; /*外部变量定义*/ main() { …… } F2.C extern int a,b; /*外部变量说明*/ extern char c; /*外部变量说明*/ 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() { int i; 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() { int i; 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); /*外部函数定义*/ { …… } 本章小结 1. 函数的分类 (1)库函数:由C系统提供的函数; (2)用户定义函数:由用户自己定义的函数; (3)有返回函数调用者返回函数,应说明函数类型( 即返回的类型 ); (4)无返回函数:不返回函数,说明为空(void)类型; (5)有参函数:主调函数向被调函数传送数据; (6)无参函数:主调函数与被调函数间无数据传送; (7)内部函数:只能在本源文件使用的函数; (8)外部函数:可在整个源程序使用的函数。 2. 函数定义的一般形式 [extern/static] 类型说明符 函数名([形参表]) 方括号内为可选项。 3. 函数说明的一般形式 [extern] 类型说明符 函数名([形参表]); 4. 函数调用的一般形式 函数名([实参表]) 5. 函数的参数分为形参和实参两种,形参出现在函数定义,实参出现在函数调用,发生函数调用时,将把实参的传送给形参。 6. 函数是指函数的返回,它是在函数由return语句返回的。 7. 数组名作为函数参数时不进行传送而进行地址传送。形参和实参实际上为同一数组的两个名称。因此形参数组发生变化,实参数组当然也变化。 8. C语言,允许函数的嵌套调用函数的递归调用。 9. 可从三个方面对变量分类,即变量的数据类型,变量作用域和变量的存储类型。在第二章主要介绍变量的数据类型,本章介绍了变量的作用域和变量的存储类型。 10.变量的作用域是指变量在程序的有效范围, 分为局部变量和全局变量。 11.变量的存储类型是指变量在内存的存储方式,分为静态存储和动态存储,表示了变量的生存期。 12.变量分类特性表存储方式存储类型说明符何处定义生存期作用域赋前的可赋初类型动态存储自动变量 auto 寄存器变量 register 函数或复合语句内被调用时在定义它的函数或复合语句内不定基本类型int或char外部变量extern函数之外整个源程序整个源程序静态局部变量static 函数或复合语句内静态全局变量static 函数之外整个源程序在定义它的函数或复合语句内在定义它的源文件内0任何类型 资料收集:beck Copyright 2002 www.vcok.com, All Rights Reserved
一:需求分析 二:总体设计 三:详细设计 四:调试与测试 五;测试结果 六:本次课程设计的心得体会 七:附录 本程序设计亮点介绍(及)改进方面 1.在修改函数,可以实现单条记录修改(运用switch) 2.查询函数,能返回该函数继续查询(运用了递归) 3.使用变量少,仅通过变量i的递加和表示成员数的变量n就实现了数据结构体之间的联 系 4.界面美观,使用方便 5.允许编号输入为字符 6.在显示函数上能够分屏显示(换页) 缺点 在输入数据时,未输完一个成员的所有信息不能退出输入 一:需求分析 A.需要实现的功能有:输入功能、输出功能、显示功能、查找功能、以及增加、删 除、修改功能。输入功能要求一次完成若干信息的输入;显示功能要能完成所有信息的 显示,并且自动分页;查找可以通过多种方式进行。 按需求的功能,应该设计出输入函数、输出函数、显示函数、查找函数、修改函数。另 需要一个标准界面。 B.数据存储问题 这个系统数据庞大复杂,需要需要适当应用数组,循环,结构体,文件操作等基本 语句及稍有难度的语句。因此要求我们熟练掌握结构化程序设计的基本思路和方法,在 所掌握的基本知识和技能的基础上,进一步提高自学能力和查找资料的能力,解决一定 程度的复杂的结构化程序设计问题,加深对所学知识的理解与掌握,利用自己所学知识 解决实际问题的能力,为以后的程序开发打下基础。 二:总体设计 主要结构图示 本程序由函数和多个子函数组成,可执行多种数据管理的功能。具体通过c语言 结构体数组来实现。函数主体为定义的结构体,和一个switch函数,以提供多种程序 操作功能。 主要需运用到结构体数组、文件、函数等内容知识。 子函数有六个,分别负责添加、显示、删除、查询、修改、保存的功能。通过变量i使结 构体数组之间达成数据链表的功能,利用该方法主要考虑到成员对于结构体指针尚未 熟练掌握,而这种方法更加便捷易懂。 子函数功能介绍 1. 添加:输入成员信息,依次输入编号、姓名、QQ、电话号码、单位、电子邮箱、身 份。 2. 显示:以表格形式输出所有通讯记录 3. 删除:只需输入该成员姓名,便删除其所有记录 4. 查询:可选用姓名、电话、单位身份4种方式来查询 5. 修改:输入该成员姓名后,再依次输入更新信息完成修改 6. 保存:输入文件名(带后缀)后保存。C-free会默认以txt文件保存至c- free standard/temp文件夹 三:详细设计 1. 函数 完成变量的初始化与函数的定义之后,运用for( ; ; )来开启一个无限循环。循环内有switch函数来实现函数的选择功能,case1、case2等分 别执行不同的函数操作,每个函数调用了相应的函数,并采用函数system("pause") ;暂停等待按键。在case6采用函数exit(0)跳出循环。 下面在讲一下函数定义的n的作用,n的初是1,当执行完case1函数input ( )后返回为n+i,i为输入的成员数,返回重新赋给n,n变为成员数,从而确定了之后 display()等函数循环的终止条件。 2. 分函数 【1】添加 【2】显示 【3】删除 【4】查询 【5】修改 保存函数:【1】定义一个文件指针fp 【2】输入想创建的文件名称,如filename 【3】开辟文件,用fopen以"写"的方式打开,并把fp指针指向该文件 【4】在文件输入信息 【5】保存(即fclose(filename)) 查询函数:运用了if来实现选择,并通过递归返回继续查询。 注:身份以及单位的查询方式有所不同,单位只能查找一条符合的记录,而按身份查询 可显示所有符合的信息。若查找不到,则返回继续查询,选择5返回则退出查询。 四:调试与测试 我们编写的函数具有一定的容错能力。具体表现为 【1】如select函数输入0-6以外的数,则循环继续进行,直到输入0-6为止 【2】sign!='n'&&sign!='N'语句保证了大小写不会影响结果 【3】while(m!='1'&&m!='2'&&m!='3'&&m!='4') { printf("输入错误,请重新选择:"); scanf("%c",&m); }保证了输入正确 【4】当输入的成员信息错误时,可通过修改函数进行改动 五:测试结果 1.函数运行正常。通过for语句是界面循环,并有switch及system函数进行配合调节 达到目的。(界面显示如下) 2.测试结果:运行正常,达到了预订的目标。 此处为通过保存函数保存后的txt文本的学生信息 六:心得 课程设计是一个不断学习、思考、编写、修改的过程。它对我们的能力提出了挑战。 我不仅意识到c语言基础的重要性,更意识到了自学能力的重要性,同学学习书本,查找 资料等多种途径来构建思
1 调用函数结构程序题: 1.1 比较两个数,输出最大者 1.2 输入两个实数,用一个函数求出他们之和 1.3 递推方法求n! 1.4 有两个班级,分别有35名和30名学生,调用函数,分别求这两个班的平均 1.5 用选择法对数组的10个整数按由小到大排序 1.6 有一个3*4的矩阵,求所以=有元素的最大 1.7 有一个数,内放10个数,不用全局变量求出最大,最小,和平均。 1.8 用调用函数求最大公约数和最小公倍数。两个整数由键盘输入 1.9 写出一个判素数的函数,在函数输入一个整数,输出是否为素数的信息 1.10 用调用函数求水仙花数 1.11 用调用函数将3*3的二维数组行和列互换 1.12 对10个数由小到大排序,用函数调用冒泡法 2 数组结构程序题: 2.1 求Fibonacci(斐波那契)数列的前40个数 2.2 用数组来处理求Fibonacci 数列问题 2.3 将一个二维数组行和列的元素互换,存到另一个二维数组 2.4 求一个3*3的矩阵的对角线之和 2.5 将一个数组按逆序重新存放,例如:原来顺序为8,6,5,4,1.要求改为1,4,5,6,8.注:考虑偶数或奇数时怎么交换 2.6 输入十个数,去掉最大数和最小数后求平均 2.7输入六个数到数列,再输入一数,如果与该数相同数组的元素相同时,则输出其下标,否则,输出“-1” 2.8降序排列十个数 2.9 求一个二维数组的最大和最小 2.10 求一个4*4矩阵所有元素的和 3 循环结构程序题 3.1 输入一个大于3的整数n,判断它是否为素数(素数又称质数,就是除了能被1和它本身整除之外,不能被其它自然数整除的自然数) 3.2 输入两个数,求最大公约数和最大公倍数 3.3输入一行字符,输出其英文字母,空格,数字,和其它字符的个数 3.4 2+22+222+2222+2222,位数和数自己输入 3.5“水仙花数”是一个三位数其各位数字的立方和等于该数本身 3.6 有一个分数序列 2/1,3/2,5/3,8/5,13/8,21/13,......求出这个序列数的前20项之和 3.7 计算1-3+5-7+.....-99+101的 3.8 求n!的 3.9 .判断一个数是否为素数? 3.10 输出九九乘法表

13,825

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder相关内容讨论区
社区管理员
  • 基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧