欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

用函数实现模块化程序设计

程序员文章站 2022-06-12 16:48:17
...

7.1 为什么要用函数

在设计一个较大的程序时,往往把它分成若干个程序模块,每一个模块包括一个或多个函数,每个函数实现一个特定的功能。一个C程序可由一个主函数和若干个其他函数构成。由主函数调用其他函数,其他函数也可以相互调用。同一个函数可以被一个或多个函数调用任意多次。

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

**********
How do you do!
**********
#include <stdio.h>
int main()
{
	void print_star();
	void print_message();
	print_star();
	print_message();
	print_star();
	return 0;
}
void print_star()
{
	printf("**********\n"); 
}
void print_message()
{
	printf("How do you do!\n");
}

7.2 怎样定义函数

7.2.1 为什么要定义函数

定义函数应包括以下几个内容:
(1)指定函数的名字,以便以后按名调用
(2)指定函数的类型,即函数返回值的类型
(3)指定函数的参数的名字和类型,以便在调用函数时向它们传递数据。对无参函数不需要这项。
(4)指定函数应当完成什么操作,也就是函数做什么,即函数的功能。

7.2.2 定义函数的方法

1.定义无参函数
定义无参函数的一般形式为:
类型名 函数名()
{
函数体
}
或类型名 函数名(void)
{
函数体
}
2.定义有参函数
定义有参函数的一般形式为:
类型名 函数名(形式参数表列)
{
函数体
}
3.定义空函数
在程序设计中有时会用到空函数,它的形式是:
类型名 函数名()
{}
例如:
void dummy()
{}

7.3调用函数

7.3.1 函数调用的形式

函数调用的一般形式是:
函数名(实参表列)
1.函数调用语句
把函数调用单独作为一个语句,如“print_star()",这时不要求函数带返回值,只要求函数完成一定的操作。
2.函数表达式
函数调用出现在另一个表达式中,如“c=max(a,b);”max(a,b)是一次函数调用,它是赋值表达式中的一部分。这时要求函数带回一个返回值以参加表达式的运算。
3.函数参数
函数调用作为另一个函数调用时的参数。例如:
m = max(a,max(b,c));
其中max(b,c)是一次函数调用,它的值作为max另一次调用的实参。经过赋值后,m是a,b,c中的最大者。

7.3.2 函数调用时的数据传递

1.形式参数和实际参数
在调用有参函数时,主调函数和被调用函数之间有数据传递关系。从前面已知:在定义函数时函数名后面括号的变量名称为形式参数或虚拟参数。在主调函数中调用一个函数时,函数名括号中的参数称为实际参数。实际参数可以是常数,变量或表达式。
2.实参和形参间的数据传递
在调用函数过程中,系统会把实参的值传递给被调用函数的形参。或者说,形参从实参得到一个值。该值在函数调用期间有效,可以参加该函数的运算。
例 输入两个整数,要求输出其中最大者。要求用函数找到大数。

#include <stdio.h>
int main()
{
	int max(int x,int y);
	int a,b,c;
	printf("please input two number:\n");
	scanf("%d",&a);
	scanf("%d",&b);
	c=max(a,b);
	printf("max = %d\n",c);
	return 0;
}
int max(int x,int y){
	int max;
	max = x>y?x:y;
	return max;
}

7.3.3 函数调用的过程

(1)在定义函数中指定的形参,在未出现函数调用时,它们并不占用内存中的存储单元。在发生函数调用时,函数max的形参被临时分配内存单元。
(2)将实参对应的值传递给形参。
(3)在执行max函数期间,由于形参已经有值,就可以利用形参进行有关的运算。
(4)通过return语句将函数值带回到主调函数。
(5)调用结束,形参单元被释放。

7.3.4 函数的返回值

(1)函数的返回值是通过函数中的return语句获得的
(2)函数值的类型。既然函数值有返回值,这个值当然应属于某一个确定的类型,应当在定义函数是指定函数值的类型。
(3)在定义函数时指定的函数类型应当和return语句中的表达式类型一致。

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

在一个函数中调用另一个函数(即被调函数)需要具备如下条件:
(1)首先被调用函数必须是已经定义的函数(是库函数或用户自己定义的函数)
(2)如果使用库函数,应该在本文件开头用#include指令将调用函数时所需用到的信息包含到本文件来。
(3)如果用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)后面(在同一个文件),应该在主调函数中对被调用函数做声明。声明的作用是把函数名,函数参数的个数和参数类型等信息通知编译系统,以便在遇到函数调用时,编译系统能正确识别函数并检查调用是否合法。
例:输入两个实数,用一个函数求出它们之和。

#include <stdio.h>
int main()
{
	float max(int x,int y);
	float a,b,c;
	printf("please input two number:\n");
	scanf("%d",&a);
	scanf("%d",&b);
	c=add(a,b);
	printf("sum = %d\n",c);
	return 0;
}
float add(int x,int y){
	float sum;
	sum = x+y;
	return sum;
}

函数原型的一般形式有两种,分别为:
(1)函数类型 函数名(参数类型1 参数1,参数类型2 参数2···)
(2)函数类型 函数名(参数类型1,参数类型2···)

7.5 函数的嵌套调用

C语言的函数定义是互相平行,独立的,也就是说,在定义函数时,一个函数内不能再定义另一个函数,也就是说不能嵌套定义,但是可以嵌套调用函数,也就是说,在调用函数的过程中,又调用另一个函数。
例输入4个整数,找出其中最大的数。用函数的嵌套调用来处理。

#include <stdio.h>
int main()
{
	int max(int x,int y,int j,int k);
	int a,b,c,d,m;
	printf("please input four number:\n");
	scanf("%d",&a);
	scanf("%d",&b);
	scanf("%d",&c);
	scanf("%d",&d);
	m=max(a,b,c,d);
	printf("max = %d\n",m);
	return 0;
}
int max(int x,int y,int j,int k){
	int max;
	int max2(int a,int b);
	max=max2(x,y);
	max=max2(max,j);
	max=max2(max,k);
	return max;
}
int max2(int a,int b)
{
	int max;
	max=a>b?a:b;
	return max;
}

7.6 函数的递归调用

在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数地递归调用。
例如

int f(int x)
{
int y,z;
z=f(y);
return(2*z);
}

例 有5个学生坐在一起,问第五个学生多少岁,他说比第四个学生大两岁。问第四个学生多少岁,他说比第三个学生大两岁。问第三个学生多少岁,他说比第二个学生大两岁。问第二个学生多少岁,他说比第一个学生大两岁。问第一个学生多少岁,他说是十岁。请问第五个学生多大?

#include <stdio.h>
int main()
{
	int age(int x);
	printf("NO.5,age:%d",age(5));
	return 0;
}
int age(int x){
	int a;
	if(x==1)
	a = 10;
	else
	a = age(x-1)+2;
	return a;
}

例 用递归方法求n!

#include <stdio.h>
int main()
{
	int f(int x);
	int n,y;
	printf("please input integer number:");
	scanf("%d",&n);
	y=f(n);
	printf("%d!=%d\n",n,y);
	return 0;
}
int f(int n){
	int a;
	if(n<0)
	printf("n<0,data error1"); 
	else if(n==1||n==0)
	a = 1;
	else
	a = f(n-1)*n;
	return a;
}

例Hanoi塔问题,这是一个经典的数学问题,是一个递归方法解题的典型例子。问题是这样的:古代有一个塔,塔内有三个座,A,B,C开始时A座有64个盘子,盘子大小不一,大的在下,小的在上。有一个老和尚想把这64个盘子从A座移到C座,但规定每次只允许移动一个盘子,且在移动过程中,始终保持,大盘在下,小盘在上。在移动过程中,可以利用B座。要求编程序输出移动盘子的步骤。

#include <stdio.h>
int main()
{
	void hanoi(int n,char one, char two,char three);
	int m;
	printf("please input the number of diskes:");
	scanf("%d",&m);
	hanoi(m,'A','B','C');
	return 0;
}
void hanoi(int n,char one, char two,char three)
{
	void move(char x,char y);
	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);
}

7.7 数组作为函数参数

7.7.1 数组元素作函数实参

数组元素可以用作函数实参,不能用作实参。因为形参是在函数被调用时临时分配存储单元,不可能为一个数组单元单独分配存储单元。
例 输入10个数,要求输出其中值最大的元素和该数是第几个数。

#include <stdio.h>
int main()
{
	int a[10],i,m,n;
	int max(int x,int y);
	printf("please input ten number:\n");
	for(i=0;i<10;i++)
	scanf("%d",&a[i]);
	m=a[0];
	n=1;
	for(i=1;i<10;i++)
	{
		if(max(m,a[i])>m)
		{
			m=max(m,a[i]);
			n=i;
		}
	}
	printf("The largest number is %d \n it is the  %dth number\n",m,n+1);
	return 0;
}
int max(int x,int y)
{
	int m;
	m=x>y?x:y;
	return m;
}

7.7.2 数组名作函数参数

用数组元素作实参时,向形参变量传递的是数组元素的值,而用数组名作函数实参时,向形参(数组名或指针变量)传递的是数组首元素的地址。
例 有一个一维数组score,内放10个学生成绩,求平均成绩。

#include <stdio.h>
int main()
{
	float average(float a[10]);
	float score[10],m;
	int i;
	printf("please input 10 score:\n");
	for(i=0;i<10;i++)
	scanf("%f",&score[i]);
	m=average(score);
	printf("The average of 10 scores is %f\n",m);
	return 0;
}
float average(float a[10])
{
	float av =a[0];
	int i;
	for(i=1;i<10;i++)		
	av += a[i];
	return av/10;
}

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

#include <stdio.h>
int main()
{
	float average(float a[10],int n);
	float a[35],b[30],m,n;
	int i;
	printf("please input A score:\n");
	for(i=0;i<35;i++)
	scanf("%f",&a[i]);
	printf("please input B score:\n");
	for(i=0;i<30;i++)
	scanf("%f",&b[i]);
	m=average(a,35);
	n=average(b,30);
	printf("The average of A's' scores is %f\n",m);
	printf("The average of B's' scores is %f\n",m);
	return 0;
}
float average(float a[10],int n)
{
	float sum =a[0],av;
	int i;
	for(i=1;i<n;i++)		
	sum += a[i];
	av = sum/n;
	return av;
}

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

#include <stdio.h>
int main()
{
	void sort(int array[],int n);
	int a[10],i;
	printf("please input ten number:");
	for(i=0;i<10;i++)
	scanf("%d",&a[i]);
	sort(a,10);
	printf("The sorted array\n");
	for(i=0;i<10;i++)
	printf("%5d",a[i]);
	return 0;
}
void sort(int array[],int n)
{
	int i,j,min;
	for(i=0;i<n;i++)
	{
		min=array[i];
		for(j=i;j<n;j++)
		{
			if(min>array[j])
			{
				min=array[j];
				array[j]=array[i];
				array[i]=min;
			}
		}
	}
}

7.7.3 多维数组名作函数参数

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

#include <stdio.h>
int main()
{
	int Get_Max(int a[][4]);
	int a[3][4],i,j,max;
	printf("please input number:");
	for(i=0;i<3;i++)
	for(j=0;j<4;j++)
	scanf("%d",&a[i][j]);
	printf("The array is:\n");
	for(i=0;i<3;i++)
	for(j=0;j<4;j++)
	printf("%5d",a[i][j]);
	max=Get_Max(a);
	printf("\n");
	printf("max = %d\n",max);
	return 0;
}
	int Get_Max(int a[][4])
	{
		int max,i,j;
		max = a[0][0];
		for(i=0;i<3;i++)
		for(j=0;j<4;j++)
		if(max<a[i][j]) max=a[i][j];
		return(max);
	}

7.8 局部变量和全局变量

7.8.1局部变量

定义变量有3种情况;
(1)在函数的开头定义
(2)在函数的复合语句内定义
(3)在函数的外部定义
在一个函数内部定义的变量只在本函数范围内有效,也就是说只有在本函数内才能引用它们,在此函数之外是不能使用这些变量的。在复合语句内定义的变量也只在本复合语句范围内有效,只有在本复合语句内才能引用它们。在复合语句外是不能引用它们的,以上这些称为局部变量。
说明:
(1)主函数中定义的变量也只在主函数中有效,并不因为在主函数定义而在整个文件或程序中有效。主函数也不能使用其他函数中定义的变量。
(2)不同函数中可以使用同名的函数,它们代表不同的对象;互不干扰。
(3)形式参数也是局部变量。
(4)在一个函数内部,可以在复合语句中定义变量,这些变量也只在复合语句中有效,这些复合语句也称为分程序或程序块。

7.8.2 全局变量

在函数内定义的变量是局部变量,在函数之外定义的变量是外部变量,外部变量是全局变量。全局变量可以为本文件中其他函数所公用。它的有效范围是定义变量的位置开始到本源文件结束。
说明:
设置全局变量的作用是增加了函数间数据联系的渠道。由于同一文件中所有函数都能引用全局变量的值,如果在一个函数中改变了全局变量的值,就能影响到其他函数中全局变量的值。相当于各个函数间有直接的传递通道。由于函数的调用只能带回一个函数返回值,因此有时可以用全局变量在对增加函数间的联系渠道,通过函数调用能得到一个以上的值。
例 有一个一维数组,内放十个学生的成绩,写一个函数,当主函数调用此函数后,能求出平均分,最高分和最低分。

#include <stdio.h>

float max =0, min = 0;

int main()
{
	float ave(float a[10]);
	float average,arr[10];
	int i;
	printf("please input 10 students' scores:\n");
	for(i=0;i<10;i++)
	scanf("%f",&arr[i]);
	average = ave(arr);
	printf("max=%6.2f\n",max);
	printf("min=%6.2f\n",min);
	printf("average=%6.2f\n",average);
	return 0;
 } 
 float ave(float a[10])
 {
 	float av,sum =0;
 	int i;
 	max=min = a[0];
 	for(i=0;i<10;i++)
 	{
 	 sum+=a[i];
	 if(max<a[i])
	 max=a[i];
	 else if(min>a[i])
	 min=a[i];	 
	 }
 	av = sum /10;
 	return av;
 }

7.9 变量的存储方式和生存期

7.9.1 动态存储方式与静态存储方式

变量的存储有两种方式:静态存储方式和动态存储方式。静态存储方式是指在程序运行期间由系统分配固定的存储空间的方式,而动态存储方式则是在程序运行期间根据需要进行动态的分配存储空间的方式。
内存*用户使用的存储空间分为3部分:
(1)程序区
(2)静态存储区
(3)动态存储区
数据分别存放在静态存储区和动态存储区,全局变量全部存放在静态存储区,在程序开始执行时给全局变量分配存储区,程序执行完毕就释放。在程序执行过程中,它们占据固定的存储单元,而不是动态地进行分配和释放。
在动态存储区重存放以数据:
(1)函数形式参数,在调用函数时给形参分配存储空间
(2)函数重定义地没有用关键字static声明的变量,即自动变量
(3)函数调用时的现场保护和返回地址等。
C的存储类别包括4种:自动的auto,静态的static,寄存器的register,外部的extern。

7.9.2 局部变量的存储类别

1.自动变量auto变量
2.静态局部变量static变量
例 输出1到5的阶乘值

#include <stdio.h>

int main()
{
	int f(int n),i;
	for(i=1;i<=5;i++)
	printf("%5d",f(i));
	return 0;
 } 
int f(int n)
{
	static int sum = 1;
	sum*=n;
	return sum;
}

3.寄存器变量register变量

7.9.3 全局变量的存储类别

1.在一个文件内拓展外部变量的作用域
例,调用函数,求3个整数中的最大值

#include <stdio.h>

int main()
{
	int Get_Max(int a,int b,int c);
	extern int A,B,C;
	printf("please input three number\n");
	scanf("%d%d%d",&A,&B,&C);
	printf("max=%d\n",Get_Max(A,B,C));
	return 0;
 } 
 int A,B,C;
int Get_Max(int a,int b,int c)
{
	int max;
	max =a>b?a:b;
	max=max>c?max:c;
	return max;
}

2.将外部变量的作用域扩展到其他文件
3.将外部变量的作用域限制在本文件中
对static声明一个变量的作用是:
(1)对局部变量用static声明,把他分配在静态存储区,该变量在整个程序执行期间不释放,其所分配的空间始终存在
(2)对全部变量用static声明,该变量的作用域只限于本文件(即被声明的文件中)

7.10 关于变量的声明与定义

在声明部分的变量有两种情况:一种是需要建立存储空间的,另一种是不需要建立存储空间的。前者称为定义性,声明,或简称定义,后者称为引用性声明

7.11内部函数和外部函数

7.11.1 内部函数

如果一个函数只能被本文件中其他函数所调用,它称为内部函数。在定义内部函数时,在函数名和函数类型的前面加static,即
static 类型名 函数名(形参表)

7.11.2 外部函数

如果在定义函数时,在函数首部的最左端加关键字extern,则称此函数是外部函数,可供其他文件调用。