谭浩强《C程序分析》(第五版)第七章

为什么要用函数

模块化程序设计

可以实现编好一批常用的函数来实现不同的功能,例如sin函数实现求一个数的正弦值,用abs函数实现求一个数的绝对值,把他们保存在函数库中。需要用时,直接在程序中写上sin(a)或abs(a)就可以调用系统函数库中的函数代码。执行这些代码,就得到预期的结果。

注意:

函数就是功能。每一个函数用来实现一个特定的功能。函数的名字应反映其代表的功能。

使用函数可以使程序清晰、精炼、简单、灵活。

在设计较大程序时,往往把它们分成若干个程序模块,每一个模块包括一个或多个函数,每个函数实现一个特定的功能。

一个C程序可以由一个主函数和若干个其它函数构成。由主函数调用其它函数,其他的函数也可以相互调用。

同一个函数可以被一个或多个函数调用任意多次。

想输出以下的结果,用函数调用实现。

******************
How do you do !
******************

解题思路:

在输出的文字上下分别有一行“*”号,显然不必重复写这段代码,用一个函数print_star来实现输出一行“*”号的功能。再写一个print_message函数来输出中间一行文字信息,用主函数分别调用者两个函数即可。

编写程序:

#include <stdio.h>

void print_star();	//声明print_star函数用于输出“*”号
void print_message();	//声明print_message函数用于输出文字信息

int main() {
	print_star();	//调用函数输出*
	print_message();	//调用函数输出文字
	print_star();	//调用函数输出*
	return 0;
}

void print_star() {	//定义print_star函数
	printf("******************\n");	//输出一行*号
}

void print_message() {	//定义print_message函数
	printf("How do you do !\n");	//输出文字信息
}

 运行结果:

 程序分析:

print_star和print_message都是用户定义的函数名,分别用来输出一排“*”号和一行文字信息。

在定义这两个函数时指定函数的类型为void,意为函数无类型,即无函数值,也就是说,执行这两个函数后不会把任何值带回main函数,在编写这两个程序的时候也不需要写return函数用来返回值。

在程序中,定义print_star函数和print_message函数的位置是在main函数的后面,在这种情况下,应当在main函数之前或main函数中的开头部分,对以上两个函数进行“声明”。函数声明的作用是把有关函数的信息(函数名、函数类型、函数参数的个数与类型)通知编译系统,以便在编译系统对程序进行编译时,在进行到main函数调用print_star()和 print_message()时知道它们是函数而不是变量或其他对象。此外,还对调用函数的正确性进行检查(如类型、函数名、参数个数、参数类型等是否正确)。

说明:

(1) 一个C程序由一个或多个程序模块组成,每一个程序模块作为一个源程序文件。较大的程序,可分别放在若干个源文件中。这样便于分别编写和编译,提高调试效率。一个源程序文件可以为多个C程序共用。

(2) 一个源程序文件由一个或多个函数以及其他有关内容(如指令、数据声明与定义等)组成。一个源程序文件是一个编译单位,在程序编译时是以源程序文件为单位进行编译的,而不是以函数为单位进行编译的。

(3) C程序的执行是从main函数开始的,如果在main函数中调用其他函数,在调用后流程返回到main函数,在main函数中结束整个程序的运行。

(4) 所有函数都是平行的,即在定义函数时是分别进行的,是互相独立的。一个函数并不从属于另一个函数,即函数不能嵌套定义。函数间可以互相调用,但不能调用main函数。main函数是被操作系统调用的。

(5) 从用户使用的角度看,函数有两种。

        ① 库函数,它是由系统提供的,用户不必自己定义,可直接使用它们。应该说明,不同的C语言编译系统提供的库函数的数量和功能会有一些不同,当然许多基本的函数是共同的。

        ② 用户自己定义的函数。它是用以解决用户专门需要的函数。

(6) 从函数的形式看,函数分两类。

        ① 无参函数。在调用无参函数时,主调函数不向被调用函数传递数据。

        ② 有参函数。主调函数在调用被调用函数时,通过参数向被调用函数传递数据。一般情况下,执行被调用函数时会得到一个函数值,供主函数使用。

定义函数

为什么要定义函数

C语言要求,在程序中用到的所有函数,必须“先定义,后使用”。

定义函数应包括以下几个内容:

(1) 指定函数的名字,以便以后按名调用。

(2) 指定函数的类型,即函数返回值的类型。

(3) 指定函数的参数的名字和类型,以便在调用函数时向它们传递数据。对无参函数不需要这项。

(4) 指定函数应当完成什么操作,也就是函数是做什么的,即函数的功能。这是最重要的,是在函数体中解决的。

由于C编译系统提供的库函数是事先定义好的,库文件中包括了对各函数的定义,因此我们在需要的时候只需要用#include指令把相关的头文件包含到文件模块中就可以了。有关的头文件中包括了对函数的声明。库函数只提供了最基本、最通用的一些函数,如果需要其他的函数,则需要自己定义。

定义函数的方法

定义无参函数

函数名后面的括号中没有任何参数,无参函数定义的一般形式为:

类型名  函数名() {

    函数体

}

类型名  函数名(void) {

    函数体

}

函数后面括号内的void表示“空”,即函数没有参数。

函数体包括声明部分语句部分

在定义函数时要用“类型标识符”(即类型名)指定函数值的类型,即指定函数带回来的值的类型。

定义有参函数

定义有参函数的一般形式为

类型名  函数名(形式参数表列) {

    函数体

例如:

int max(int x,int y){
	int z;		//声明部分
	z=x>y?x:y;	//执行语句部分
	return z;
}

 这是一个求x和y二者中大者的函数,第一行第一个关键字int表示函数值是整型的,max为函数名,体现了这个函数的功能是求最大值。括号中有两个形式参数x和y,这两个参数都是整型。花括号内是函数体,可以包括声明部分和语句部分。声明部分包括对函数中用到的变量进行定义以及对要调用的函数进行声明等内容。使用return z将z的值作为函数值带到主函数,在函数定义的时候就已经指定max函数为整型,即指定函数的值是整型的,在函数体中定义z为整型,并将z的值作为返回值,这是一致的。此时函数max的值等于z。

定义空函数

类型名  函数名() { }

函数体为空,什么也不做。

在程序设计中,往往根据需要确定若干个模块,分别由一些函数来实现。而在第一阶段值设计最基本的模块。其他的一些次要功能在以后需要的时候陆续补上。书写一个空函数,函数名取将来采用的实际函数名,代表这些函数暂时没有编写好,先用一个空函数占位,这样程序结构清楚,可读性好,扩充起来也方便,对程序结构影响不大。

调用函数

函数调用的形式

函数调用的一般形式为:

函数名(实参表列)

 如果是调用无参函数,则实参表列可以没有,但括号不能省略。如果实参表列包含多个实参,则各参数间用逗号隔开。例如:

print_star();	//调用无参函数
c=max(a,b);	//调用有参函数

按函数调用在程序中出现的形式位置来分,可以有以下3种函数调用方式。

1.函数调用语句

把函数调用单独作为一个语句。如printf_star(); 这时不要求函数带回值,只要求函数完成一定的操作。

2.函数表达式

函数调用出现在另一个表达式中,如c=max(a,b); 这时要求函数带回一个确定的值以参加表达式的运算。例如:

c=2*max(a,b);

3.函数参数

函数调用作为另一个函数调用时的实参。如:

m=max(a,max(b,c));

其中,max(b,c)是一次函数调用,它的值是b和c二者中更大的那个,把它作为max另一次调用的实参,经过赋值后,m的值是a,b,c三者中最大的一个。又如:

printf (″%d″, max (a,b));

也是把max(a,b)作为printf函数的一个参数。

说明:

调用函数并不一定要求包括分号,例如print_star();,只有作为函数调用语句的时候才需要有分号,如果作为函数表达式或函数参数,函数调用本身是不必有分号的。

函数调用时的数据传递

形式参数和实际参数

在调用有参函数时,主调函数和被调用函数之间有数据传递关系。 在定义函数时函数名后面括号中的变量名称为“形式参数”(简称“形参”)或“虚拟参数”。 在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”(简称“实参”)。 实际参数可以是常量、变量或表达式,但要求它们有确定的值。 实参与形参的类型应相同或赋值兼容。赋值兼容是指实参与形参类型不同时能按不同类型数值的赋值规则进行转换。

实参和形参间的数据传递

在调用函数过程中,系统会把实参的值传递个被调用函数的形参,或者说,形参从实参得到一个值。该值在函数调用期间有效,可以参加该函数中的运算。在调用函数过程中发生的实参与形参间的数据传递成为“虚实结合”。

输入两个整数,要求输出其中值较大者。要求用函数来找到大数。

解题思路:

从两个数中找出其中的大者,需要定义一个函数用于调用。在定义函数的时候需要

(1)确定函数名,函数名应该反映函数功能,因此在这题定义为max。

(2)确定函数的类型。由于给定的两个数是整数,所以函数返回值应该是整数。

(3)确定max函数的参数个数和类型。max函数应该有两个参数,以便从主函数接收两个整数。

在调用max函数的时候,应当给出两个整数作为实参,传给max函数中的两个形参。

编写程序:

#include <stdio.h>

int max(int x,int y);		//对max函数的声明

int main(){
	int a,b,c;
	printf("please enter two integer numbers:\n");  //提示输入数据
	scanf("%d,%d",&a,&b);	//输入两个整数
	c=max(a,b);	//调用max函数,有两个实参。大数赋给变量c
	printf("max is %d\n",c);	//输出大数c
	return 0; 
}

int max(int x,int y)	//定义max函数,有两个形参
{
	int z;		//定义临时变量z
	z=x>y?x:y;	//把x和y中大者赋给z
	return z;		//把z作为max函数的值带回main函数
}

运行结果:

 程序分析:

a,b是在main函数里面定义的实参,x,y是在max函数中的形参,通过函数调用,在两个函数之间发生数据传递,实参a,b的值传给x和y,在max函数中把x和y中的更大的一个赋值给变量z,z的值作为函数值返回main函数,赋值给变量c。

说明:

(1)实参可以使常量、变量或表达式,例如:

max(3,a+b)

但要求他们有确定的值,在调用时将实参的值赋给形参。

(2)实参与形参的类型应相同或赋值兼容。如果实参为int而形参为float,则按不同类型数值的赋值规则进行转换。例如如果实参float a = 3.5;而形参为int x;则在传递的时候现将实数3.5转换成整数3,然后传送到形参x,字符型与int型可以互相通用。

函数调用的过程

(1) 在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。在发生函数调用时,函数的形参才被临时分配内存单元。

(2) 将实参的值传递给对应形参。

(3) 在执行函数期间,由于形参已经有值,就可以利用形参进行有关的运算。

(4) 通过return语句将函数值带回到主调函数。应当注意返回值的类型与函数类型一致。如果函数不需要返回值,则不需要return语句。这时函数的类型应定义为void类型。

(5) 调用结束,形参单元被释放。注意: 实参单元仍保留并维持原值,没有改变。如果在执行一个被调用函数时,形参的值发生改变,不会改变主调函数的实参的值。因为实参与形参是两个不同的存储单元。

注意:

实参向形参的数据传递是“值传递”,单向传递,只能由实参传给形参,而不能由形参传给实参。实参和形参在内存中占有不同的存储单元,实参无法得到形参的值。

 函数的返回值

(1)函数的返回值是通过函数中的return语句获得的。一个函数中可以有一个以上的return语句,执行到哪一个return语句,哪一个return语句就起作用。return语句后面的括号可以不要,如“return z;”与“return(z);”等价。return后面的值可以是一个表达式。如果return后面还有语句,执行完return后,其后的语句将不再执行。

(2) 函数值的类型。函数值的类型在定义函数时指定。

(3) 在定义函数时指定的函数类型一般应该和return语句中的表达式类型一致。 如果函数值的类型和return语句中表达式的值不一致,则以函数类型为准。对数值型数据,可以自动进行类型转换。即函数类型决定返回值的类型

(4) 对于不带回值的函数,应当用定义函数为“void类型”(或称“空类型”)。这样,系统就保证不使函数带回任何值,即禁止在调用函数中使用被调用函数的返回值。此时在函数体中也没有return语句。

输入两个整数,要求输出其中值较大者。要求用函数来找到大数。

将在max函数中定义的变量z改为float型。

解题思路:

如果函数返回值的类型与指定的函数类型不同,按照赋值规则处理。

编写程序:

#include <stdio.h>

int max(float x, float y);		//对max函数的声明

int main() {
	float a, b;
	int c;
	printf("please enter two integer numbers:\n");  //提示输入数据
	scanf("%f %f", &a, &b);	//输入两个整数
	c = max(a,b);	//调用max函数,有两个实参。大数赋给变量c
	printf("max is %d\n", c);	//输出大数c
	return 0;
}

int max(float x, float y)	//定义max函数,有两个形参
{
	int z;		//定义临时变量z
	z = x > y ? x : y;	//把x和y中大者赋给z
	return z;		//把z作为max函数的值带回main函数
}

 运行结果:

程序分析:

如果将c也改成float型,输出格式为%f,则运行结果为:

 因为调用max函数得到的是int型,函数值为整数2。

对被调用函数的声明和函数原型

在一个函数中调用另一个函数(即被调用函数)需要具备如下条件:

(1) 被调用的函数必须是已经定义的函数(是库函数或用户自己定义的函数)。

(2) 如果使用库函数,应该在本文件开头用#include指令将调用有关库函数时所需用到的信息“包含”到本文件中来。

(3) 如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)的后面(在同一个文件中),应该在主调函数中对被调用的函数作声明(declaration)。声明的作用是把函数名、函数参数的个数和参数类型等信息通知编译系统,以便在遇到函数调用时,编译系统能正确识别函数并检查调用是否合法。

 输入两个实数,用一个函数求出它们之和。

解题思路:

因为要体现函数的功能,所以函数定义为add,因为要求输入的是两个实数,所以函数定义和参数定义为float型。

编写程序:

#include <stdio.h>

float add(float x, float y);	//函数声明

int main() {
	float a, b, c;	//定义变量
	printf("请输入两个实数:\n");
	scanf("%f %f", &a, &b);
	c = add(a, b);	//调用函数,传参
	printf("两个实数的和为:%f\n", c);
	return 0;
}

float add(float x, float y) {	//定义函数
	float z;
	z = x + y;
	return z;
}

 运行结果:

 用函数首部作为函数声明是为了便于函数调用的合法性进行检查。

说明:

函数类型 函数名(参数类型1  参数名1, 参数类型2  参数名2, …, 参数类型n  参数名n);

函数类型 函数名(参数类型1, 参数类型2, …, 参数类型n);

在函数声明中的形参名可以省写,而只写形参的类型。 如果已在文件的开头(在所有函数之前),已对本文件中所调用的函数进行了声明,则在各函数中不必对其所调用的函数再作声明。、

注意:

对函数的“定义”和“声明”不是同一回事。函数的定义是指对函数功能的确立,包括指定函数名、函数值类型、形参及其类型以及函数体等,它是一个完整的、独立的函数单位。而函数的声明的作用则是把函数的名字、函数类型以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时系统按此进行对照检查,它不包含函数体。 

函数的嵌套调用

C语言的函数定义是互相平行、独立的,也就是说,在定义函数时,一个函数内不能再定义另一个函数,即不能嵌套定义,但可以嵌套调用函数,即在调用一个函数的过程中,又调用另一个函数。

 输入4个整数,找出其中最大的数。用函数的嵌套调用来处理。

解题思路:

因为是找出最大者,所以函数名可以为max,因为需要使用函数嵌套,因此将max分为函数max2和max4分别用于找出两个数和四个数的最大值。首先在main函数中调用max4,得到最大值。随后运行max4,在max4中第一次调用max2,用于找到第一个数和第二个数中较大的一个,然后第二次调用max2用于找到前三个数中最大的一个,然后第三次调用max2用于找到四个数中最大的一个。

编写程序:

#include <stdio.h>

//函数声明
int max2(int y1, int y2);
int max4(int z1, int z2, int z3, int z4);

int main() {
	//定义变量
	int x1, x2, x3, x4, max;
	printf("请输入4个整数:\n");
	scanf("%d %d %d %d", &x1, &x2, &x3, &x4);
	//调用函数max4得到最大值
	max = max4(x1, x2, x3, x4);
	printf("这4个数中最大值为:%d\n", max);
	return 0;
}

//定义函数max4
int max4(int z1, int z2, int z3, int z4) {
	//定义变量用于存储最大值
	int m;
	//调用max2得到第一个和第二个值中较大的一个并存储
	m = max2(z1, z2);
	//调用max2得到前三个数中最大的一个并存储起来
	m = max2(m, z3);
	//调用max2得到4个数中最大的一个并存储起来
	m = max2(m, z4);
	//将最大值返回
	return m;
}

//定义函数max2
int max2(int y1, int y2) {
	//找到两个数中较大的一个并返回
	if (y1 > y2)
		return y1;
	else
		return y2;
}

运行结果:

 上述代码用了递推的方法。

函数的递归调用

在调用一个函数的过程中又出现直接或间接地调用该函数本身,成为函数的递归调用。函数递归调用是C的一大特点之一,例如:

int f(int x)
{
	int y,z;
	z=f(y);	//在执行f函数的过程中又要调用f函数
	return (2*z);
}

在调用函数f的过程中,又要调用f函数(本函数),这是直接调用本函数,如下图:

 如果在调用f1函数过程中要调用f2函数,而在调用f2函数过程中又要调用f1函数,这就是间接调用本函数,如下图:

 注意:

程序中不应出现无终止的递归调用,而只应出现有限次数的、有终止的递归调用,这可以用if语句来控制,只有在某一条件成立时才继续执行递归调用;否则就不再继续。

有5个学生坐在一起,问第5个学生多少岁,他说比第4个学生大2岁。问第4个学生岁数,他说比第3个学生大2岁。问第3个学生,又说比第2个学生大2岁。问第2个学生,说比第1个学生大2岁。最后问第1个学生,他说是10岁。请问第5个学生多大。 

解题思路:

要求第5个学生的年龄,就必须先知道第4个学生的年龄,而知道第4个学生的年龄需要知道第3个学生的年龄,想要知道第3个学生的年龄需要知道第2个学生的年龄,第2个学生的年龄可以通过第1个推出来,并且每一个学生都比前一个学生大两岁,即:

 因此可以写一个函数用来通过前一个学生的年龄计算后一个:age(n)=age(n-1)+2;然后通过主函数调用来计算。

编写程序:

#include <stdio.h>

int age(int n);	//函数声明

int main() {
	printf("第五个学生的年龄为:%d\n", age(5));	//调用函数计算并打印出第五个学生的年龄
	return 0;
}

int age(int n) {
	int old;	//定义变量存储学生的年龄
	if (n == 1)
		old = 10;	//第一个学生的年龄为10
	else
		old = age(n - 1) + 2;	//后一个学生的年龄是前一个学生的年龄+2
	return old;	//将第五个学生的年龄作为返回值
}

运行结果:

 程序分析:

在这个程序中,age函数被调用了5次,其中age(5)是main函数调用的,其余4次是age函数自己调用自己进行递归的。在进行递归调用的时候需要注意终止条件,这里的终止条件是n==1。根据题设条件,我们知道age(1)=10;通过分析我们知道最后一个未知数是age(2),而age(2)=age(1)+2;所以这时的age函数调用时最后一次调用。

用递归方法求n!。

解题思路:

用递归的方法分析就是从最后的答案往前一步一步分析,最后要求的是n!,n!=(n-1)!*n,而(n-1)!=(n-2)!*(n-1)……到最后是2!=1!*2。所以我们可以编写一个函数f用来计算n!,那么递归调用就为f(n)=f(n-1)*n;终止条件为n==1。

编写程序:

#include <stdio.h>

int f(int n);	//函数声明

int main() {
	int x;
	printf("请输入一个正整数:");
	scanf("%d", &x);
	printf("%d!=%d\n", x, f(x));	//调用函数
	return 0;
}

int f(int n) {
	int s;
	if (n == 1)
		s = 1;
	else
		s = f(n - 1) * n;	//f(2)=f(1)*2;f(3)=f(2)*3;……
	return s;
}

运行结果:

 程序分析:

 Hanoi(汉诺)塔问题。古代有一个梵塔,塔内有3个座A,B,C。开始时A座上有64个盘子,盘子大小不等,大的在下,小的在上。有一个老和尚想把这64个盘子从A座移到C座,但规定每次只允许移动一个盘,且在移动过程中在3个座上都始终保持大盘在下,小盘在上。在移动过程中可以利用B座。要求编程序输出移动盘子的步骤。

解题思路:

假设将最后一个盘子移到C,这样我们的任务就变成了将剩余的63个盘子移到C并且大盘在下,小盘在上。 随后是倒数第二个盘子移到C,这样就变成了将剩余的62个盘子移到C并且大盘在下……将第二个盘子移到C,这样就只需要移动第一个盘子就可以完成任务了。

那么首先移动第一个盘子,A->C,

如果想要将前两个移到C上,A->B,A->C,B->C,

如果是前三个,A->C,A->B,C->B,A->C,B->A,B->C,A->C,由此可以推出如果想要移动n个盘子,那么就需要进行2^n-1次移动。将前n-1个盘子看为一个整体,则先将前n-1个盘子移动到B,然后将第n个盘子移动到C,然后将前n-1个盘子移动到C。

这样就可以编写一个递归函数hanoi(int n,char one,char two,char three),其中n表示需要移动的盘子数量,one,two,three分别对应不同的底座,因为每一步实际利用到的底座ABC并不是固定的,所以用one,two和three代指第一个,第二个,第三个底座。因为需要打印出移动的步骤,因此递归函数不需要返回任何值,使用void,编写一个move函数打印出移动步骤,在hanoi中进行调用就行了。

递归方法:第一步是将前n-1个盘子移动到two,盘子的初始位置是one,需要借助three(对应移动两个的步骤),所以操作为hanoi(n-1,one,three,two),然后移动第n个,此时需要的操作时move(one,three),表示将盘子从A移到C,最后将前n-1个盘子从B移到C,进行操作hanoi(n-1,two,one,three)。

编写代码:

#include <stdio.h>

//函数声明
void hanoi(int n, char one, char two, char three);
void move(char x, char y);

int main() {
	int s;
	printf("请输入需要移动的盘子的数量:\n");
	scanf("%d", &s);
	printf("移动%d个盘子的步骤为:\n", s);
	hanoi(s, 'A', 'B', 'C');
	return 0;
}

//移动盘子
void hanoi(int n, char one, char two, char three) {
	if (n == 1)
		move(one, three);
	else {
		hanoi(n - 1, one, three, two);
		move(one, three);
		hanoi(n - 1, two, one, three);
	}
}

//打印步骤
void move(char x, char y) {
	printf("%c->%c\n", x, y);
}

运行结果:

 数组作为函数参数

数组元素可以作为函数实参,其用法与变量相同,向形参传递数组元素的值,数组名也可以作为实参和形参,传递的是数组的第一个元素的地址。

数组元素作为函数实参

数组元素可以用作函数实参,但是不能作为形参。在用数组传参的时候进行的是值传参,数据传递方向是从实参到形参,单项传递。

输入10个数,要求输出其中值最大的元素和该数是第几个数。

 解题思路:

可以定义一个数组a,长度为10,用来存放10个数据。设计一个函数max用于求两个数中较大的那一个。在主函数中定义一个变量m,m的初值为a[0],每次调用max函数后的返回值存放在m中。

编写程序:

#include <stdio.h>

int max(int x, int y);	//找最大值

int main() {
	int a[10];
	int i = 0, m, n=1;
	printf("请输入10个整数:\n");
	for (; i < 10; i++)
		scanf("%d", &a[i]);	//获得数组元素
	m = a[0];
	for (i = 1; i < 10; i++) {
		if (m < max(m, a[i])) {
			m = max(m, a[i]);
			n = i+1;
		}
	}
	printf("这十个整数中第%d个数最大。\n最大值为%d。\n", n, m);
	return 0;
}

int max(int x, int y) {
	return x > y ? x : y;
}

运行结果:

 程序分析:

 首先是对数组进行赋值,赋值结束之后对m进行初始化,通过上图可以发现,数组a已经赋值完成并对m进行了初始化,因为对m的初始化是a[0],是数组中的第一个数,所以给n的初始化为1。

  经过第一次比较,已经返回了a[0]和a[1]中的最大值并赋值给m,因为a[1]<a[0],所以此时n依旧是1。

 找到第一个大于a[0]的值a[4],将a[4]的值赋值给m并记录这个数是数组中的位置是第五个数。

一维数组名作函数参数

有一个一维数组score,内放10个学生成绩,求平均成绩。

解题思路:

用一个函数average来求平均成绩,不用数组元素作为函数实参,而是用数组名作为函数实参,形参也用数组名,在average函数中引用各数组元素,求平均成绩并返回main函数。

编写程序:

#include <stdio.h>

//函数声明,求平均值
float average(float array[10]);

int main() {
	float score[10], aver;
	int i;
	printf("请依次输入十个学生的成绩:\n");
	//获得十个学生的成绩
	for (i = 0; i < 10; i++)
		scanf("%f", &score[i]);
	//调用函数average求平均值
	aver = average(score);
	printf("这十个学生的平均成绩为:\n%5.2f", aver);
	return 0;
}

//函数定义
float average(float array[10]) {
	int i;
	float aver1, sum = 0.0;
	//求十个学生成绩总和
	for (i = 0; i < 10; i++)
		sum += array[i];
	//求十个学生成绩平均值
	aver1 = sum / 10;
	//将平均值作为返回值返回到main函数中
	return aver1;
}

 运行结果:

 程序分析:

1.用数组名作为函数参数,应该在主调函数和被调用函数分别定义数组。

2.实参数组与形参数组类型应一致。

3.在定义子函数average的时候,将实参数组的首元素地址传给形参数组名。形参数组名获得了实参数组的首元素的地址,因此array[10]和score[10]的每一个单元地址都是相同的,这和之前的值传参是不一样的。通过修改形参数组的值就可以修改实参数组的值,而值传参修改形参的值并不能修改实参的值。例如:score[0]=10;如果在average中修改为array[0]=25.3;那么在主函数中打印score[0]的值就为25.3。

4.形参数组可以不指定大小,在定义数组的时候在数组名后面跟一个空的方括号也可以的。例如:float average(float array[]);

有两个班级,分别有5和10名学生,调用average函数,分别求这两个班的学生的平均成绩。

解题思路:

定义average函数求平均值,在形参表中增加一个整型变量i,从主函数吧数组的实际长度分别从实参传递给形参i。用i控制average函数中的循环次数。 

编写程序:

#include <stdio.h>

//声明函数求平均值
float average(float array[], int n);

int main() {
	//初始化两个班级的成绩
	float score1[5] = { 98.5,97,91.5,60,55 };
	float score2[10] = { 67.5,89.5,99,69.5,77,89.5,76,54,60,99.5 };
	//调用average函数求两个班的平均成绩
	printf("两个班级的平均成绩分别为:\n%5.2f\t%5.2f", average(score1,5), average(score2,10));
	return 0;
}

//定义求平均成绩函数
float average(float array[], int n) {
	int i;
	float aver, sum = 0;
	//求成绩总和
	for (i = 0; i < n; i++) 
		sum += array[i];
	//求平均成绩
	aver = sum / n;
	//将平均成绩作为返回值返回到main函数中
	return aver;
}

运行结果:

 用选择法对数组中10个整数按由小到大排序。

解题思路:

所谓选择法就是先将10个数中最小的数与a[0]对换;再将a[1]~a[9]中最小的数与a[1]对换……每比较一轮,找出一个未经排序的数中最小的一个。共比较9轮,例如:

a[0]a[1]a[2]a[3]a[4]
未排序时36194
将最小的和a[0]交换16394
将剩下的最小的和a[1]交换13694
将剩下的最小的和a[2]交换13496
将剩下的最小的和a[3]交换13469

编写程序:

#include <stdio.h>

//函数声明
void sort(int array[], int n);

int main() {
	int a[10], i;
	printf("请输入十个整数:\n");
	for (i = 0; i < 10; i++)
		scanf("%d", &a[i]);
	sort(a, 10);
	printf("将这十个整数由小到大排序:\n");
	for (i = 0; i < 10; i++)
		printf("%d ", a[i]);
	return 0;
}


//定义排序函数
void sort(int array[], int n) {
	int i, j, k, t;
	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;
	}
}

运行结果:

 运行过程:

多维数组名作函数参数

多维数组元素可以做函数参数。以下两种形式都可以:

int array[3][4];
int array[][4];

有一个3×4的矩阵,求所有元素中的最大值。 

解题思路:

先使变量max的初值为a[0][0],然后将矩阵中各个元素的值与max相比,如果a[i][j]>max,则max=a[i][j]。

编写程序:

#include <stdio.h>

//声明函数求数组最大值
int max(int array[][4]);

int main() {
	int a[3][4] = { {1,3,5,7},{2,4,6,8},{15,17,34,13} };
	printf("矩阵中最大值为:\n%d", max(a));
	return 0;
}

//定义函数max求最大值
int max(int array[][4]) {
	int i, j, maximum=array[0][0];
	for (i = 0; i < 3; i++) {
		for (j = 0; j < 4; j++) {
			if (array[i][j] > maximum)
				maximum = array[i][j];	//找到最大值
		}
	}
	return maximum;
}

 运行结果:

 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

箬渊凡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值