第八章 函数
8.1 函数概述
8.2 函数定义的一般形式
8.3 函数参数和函数的值
8.4 函数的调用
8.5 函数的嵌套调用
8.6 函数的递归调用
8.7 数组作为函数参数
8.8 局部变量和全局变量
8.1 函数概述一、有关 C 函数的介绍
1,一个源程序文件由一个或多个函数组成
2,一个 C程序由一个或多个源程序文件组成
3,C程序从 main函数开始执行,
调用其他函数后再返回到 main 函数
4,所有函数都是平行的,定义函数时是互相独立的,
函数之间可以互相调用,但是不能调用 main函数
#include <stdio.h>
#include <math.h>
void main( )
{ int a,b,c;
scanf(“%d%d”,&a,&b) ;
c=a+abs(b);
printf(“c=%d\n”,c);
}
二,C 函数的分类
1,从用户使用角度分为,标准 函数和 用户自定义 函数
2,从函数的形式分为,无参 函数和 有参 函数
#include <stdio.h>
int max(int x,int y)
{ int z ;
if (x>y) z=x ;
else z=y ;
return(z) ;
}
void main( )
{ int a,b,c ;
scanf(“%d%d”,&a,&b);
c=max(a,b);
printf(“max=%d”,c);
}
函数定义函数调用形式参数
#include <stdio.h>
void main( )
{ printf(“******\n”);
printf(,good \n”);
printf(“******\n”);
}
/*printf是标准函数 */
#include <stdio.h>
void list(void)
{
printf(“******\n”);
}
void main( )
{ list( ) ;
printf(,good \n”);
list ( );/*用户自定义函数 */
}
实在参数
8.2 函数定义的一般形式
1,定义格式,函数类型 函数名 ( 形式参数表 )
{
函数体 ;
}
2,说明
(1) 函数名,用户定义的合法标识符
(2) 函数的类型,函数返回值的类型若返回值为 int 或 char 类型,函数定义时可省略不写若函数无返回值,函数定义时应写上 void 类型
void list(void)
{ printf(“******\n”); }
(3) 形式参数
① 书写格式,每一个参数都要写上数据类型和参数名参数之间以逗号分隔,无参数时一般写上 void 声明它为空
② 作用,表示将从主函数中接收哪些类型的信息例,int max( int x,int y )
{ int z ;
z = x > y? x,y ;
return( z ) ;
}
(4) 函数体,由变量声明和语句组成
① 函数体内定义的变量是局部量,只在执行该函数时存在
② 函数体可以为空 (即存在空函数 ) void f (void) { }
8.3 函数参数和函数的值一、形式参数和实在参数
1,形式参数,在 定义函数 时写在函数名后面的括号内的变量
2,实在参数,在 调用函数 时写在函数名后面的括号内的变量
3,说明
(1) 程序在编译时不为形参分配存储空间,只有当函数被调用时,形参被分配存储空间,在调用结束后,形参所占的空间被释放
(2) 实参可以是常量,变量或表达式
(3) 实参和形参的类型应相同或赋值相容
(4) 实参对形参的数据传递是,值传递,,即 单向 传递实参?形参二,函数的返回值
1,函数的返回值通过函数中 return语句 获得
return语句的作用,使流程返回主调函数 ;
将函数值送回到调用表达式中
2,函数类型决定函数返回值的类型一般函数值的类型和 return语句中表达式的类型应一致如果二者不一致,则以函数值的类型为准
3,有的函数中没有 return语句,因为它不需要带回值,这时可以将函数类型定义为 void 型
void list(void)
{
printf(“******\n”);
}
4,一个函数允许有一个或多个 return语句,但每个 return
后的表达式类型要相同 ; 当执行到其中任何一个 return
语句时会立即返回主调函数
int fun( int n )
{ if ( n >10 )
return( n ) ;
else
return( 2*n ) ;
}
8.4 函数的调用一、函数调用
1,一般形式,函数名 ( 实参表列 ) ;
2,说明
(1) 实参表列可以包含多个实参,各个实参之间用逗号分隔
(2) 实参与形参的个数应相等,类型一致,顺序要一一对应
(3) 调用无参函数时格式为,函数名 ( ) 注意 ( )不能省略
(4) BC31中按从左至右的顺序对实参求值
3,函数调用的方式
(1) 函数 语句 printf(“hello!”) ; /*标准函数调用 */
(2) 函数 表达式 c = 2 * max( a,b ) ; /*自定义函数调用 */
(3) 函数 参数 m = max ( c,max ( a,b ) ) ;
4,函数调用过程,
(1) 形参与实参各自占有一个独立的存储空间
(2) 形参的存储空间在函数被调用时才分配
(3) 函数返回时,形参占据的临时存储区被撤消特点,函数中对形参变量的操作不会影响到实参
#include <stdio.h>
int f (int x,int y)
{ x = x+2 ;
y = y*2 ;
return( x+y ) ;
}
void main ( )
{ int a,b,c ;
scanf(“%d%d”,&a,&b) ;
c = f(a,b) ;
printf(“%d\t”,c) ;
printf(“%d,%d\n”,a,b);
}
假设输入为,3 5
main函数 f 函数
a
b
c
x
y
3
5
3
5
5
10
15
运行结果为,15 3,5
二、函数声明
1,在调用函数前,一般要对函数事先作出声明声明有利于编译系统对函数调用的合法性进行检查
2,声明格式,函数类型 函数名 ( 形参表 ) ;
3,注意事项,
(1) 函数声明末尾要加分号
(2) 形参表中可以只写数据类型如 int f ( int,float ) ;
(3) 形参的先后次序不能写错
(4) 当被调用的函数定义写在主调函数之前时,允许省略函数声明
(5) 建议将程序中用到的函数都在程序的前面加以声明
C程序的一般格式,
# 命令函数声明
main函数的定义其他函数的定义
#include <stdio.h>
void main ( )
{ int f (int x,int y);
int a,b,c ;
scanf(“%d%d”,&a,&b) ;
c = f(a,b) ;
printf(“%d\n”,c) ;
}
int f (int x,int y)
{ x = x+2 ;
y = y*2 ;
return( x+y ) ;
}
#include <stdio.h>
int f (int x,int y);
void main ( )
{ int a,b,c ;
scanf(“%d%d”,&a,&b);
c = f(a,b) ;
printf(“%d\n”,c) ;
}
int f (int x,int y)
{ x = x+2 ;
y = y*2 ;
return( x+y ) ;
}
4,声明的位置,
(1) 在 main 函数中声明 (2) 所有函数进行外部声明函数声明函数声明函数定义函数定义例,求 100-200间的素数
#include <stdio.h>
#include <math.h>
int prime( int n ) ;
void main( )
{ int m,p,num=0 ;
for (m=101 ; m<=200 ; m=m+2)
{ p=prime(m);
if (p==1)
{ printf(“%5d”,m );
num++ ;
}
}
printf(“\n num=%d \n”,num);
}
int prime( int n )
{ int i,k,flag=1 ;
k=sqrt(n);
for(i=2 ; i<=k ; i++)
if(n%i==0)
{ flag=0 ;
break ;
}
return(flag);
}
若 m为素数则返回值为
1;否则返回值为 0
函数声明函数调用函数定义
8.5 函数的嵌套调用
C语言规定函数定义都是平行的、独立的,即函数 不能进行嵌套的定义,但是函数 可以进行嵌套调用,
函数的嵌套调用是指 在调用一个函数的过程中调用另一个函数,
void main( )
{,
fun( ) ;
:
}
void fun( void )
{,
g( ) ;
:
}
main函数
fun函数
g函数调用调用嵌套调用
#include <stdio.h>
void fa (void)
{
putchar(?a? ) ;
}
void fb (void)
{
fa( ) ;
putchar(? t? ) ;
}
void main (void)
{
putchar(?c? );
fb( ) ;
}
main
输出 c
调用 fb
结束
fb
调用 fa
输出 t
fa
输出 a
结束结束输出结果,cat
8.6 函数的递归调用
1,函数递归调用的概念,在调用一个函数的过程中又直接或间接地调用该函数自己
2.用递归求解问题的特点
(1)存在某个特定条件,在此条件下可以得到指定的解,
(即,递归的终止条件 )
(2)对任意给定的条件有明确的定义规则,可以产生新的状态,并将最终导出终止状态,
(即,存在导致问题求解的递归方式 )
3,优点,程序简洁,代码紧凑缺点,占用存储空间较多,时间效率较差例,用递归的方法求 n!
1 n=0,1
n*(n-1) ! n>0
n!=
递归的终止条件递归方式
4!=4*(4-1)! 返回值 6
返回值 2
返回值 1
3!=3*(3-1)!
2!=2*(2-1)!
1!=1
主调函数返回值 24调用
#include <stdio.h>
float fac( int n ) ;
void main( )
{ int m ; float y ;
scanf(“%d”,&m );
y=fac(m); /*调用函数 fac ()*/
printf(“%d!=%15.0f \n”,m,y ) ;
}
函数声明函数定义递归调用
float fac(int n)
{ float f ;
if ( n<0 )
{ printf(“error!”); f=-1; }
else if (n==0 || n==1 ) f=1;
else f=n*fac( n-1 ) ;/*递归调用自己 */
return(f ) ;
}
main函数输入 m ③
y=fac(m)
输出 y ⑥
调用 fac
m?n ③
因 3!=0,1
f=3*fac(3-1)
返回 f ⑥
调用 fac
m?n ②
返回 f ② 返回 f ①
因 2!=0,1
f=2*fac(2-1)
调用 fac
m?n ①
因 1==1
f=1
结束递归调用过程,
8.7 数组作为函数参数一,数组元素作函数参数 (值传递)
其用法与变量作实在参数一样,实参和形参都使用不同的数组内容,是单向传递二,数组名作函数参数 (地址传递)
实参和形参都使用同一数组内容。
说明,
1,必须在主调函数和被调函数中分别定义数组
2,实参数组和形参数组的类型必须一致
3,数组名作参数 实际上是将实参数组的 首地址 传给形参数组,即实参数组和形参数组占用同一片内存单元
4.数组名作参数时,定义形参数组时可以不指定其大小,
当形参数组发生变化时,实参数组也随之发生变化
#include <stdio.h>
void main( )
{ int i,sum,s[40] ;
float ave ;
for(i=0; i<=39 ; i++)
scanf(“%d”,&s[i]);
sum=0;
for(i=0; i<=39 ; i++)
sum=sum+s[i];
ave=sum/40;
printf(“ave=%f”,ave);
}
#include <stdio.h>
float average(int s[40]);
void main( )
{ int i,sc[40] ; float aver ;
for(i=0; i<=39 ; i++)
scanf(“%d”,&sc[i]);
aver=average(sc);/*数组名作参数 */
printf(“aver=%f”,aver);
}
例,求某班成绩的平均分
float average(int s[40])
{ int sum,i ; float ave ;
sum=0;
for(i=0; i<=39 ; i++)
sum=sum+s[i];
ave=sum/40;
return(ave);
}
数组占用存储空间
sc[0]
sc[1]
sc[2]
:
:
:
:
sc[39]
main average
s[0]
s[1]
s[2]
:
:
:
:
s[39]
main
输入 sc的
40个元素
aver=average(sc)
输出 aver
调用 average
sc?s
sum=0
计算 sum
返回 ave的值结束
ave=sum/40
78
84
90
:
:
:
:
66
#include <stdio.h>
void main( )
{ int a[6],i,j,k,t;
for ( i=0 ; i<6 ; i++)
scanf(“%d”,&a[i] );
for ( i=0 ; i<5 ; i++)
{ k=i ;
for ( j=i+1 ; j<6 ; j++)
if ( a[k]>a[j] ) k=j ;
if ( k!=i )
{ t=a[i] ; a[i]=a[k] ;
a[k]=t ;
}
}
for ( i=0 ; i<6 ; i++)
printf(,%3d”,a[i] );
}
例,选择排序 #include <stdio.h>void sort ( int a[ ],int n);
void main( )
{ int b[6],i ;
for ( i=0 ; i<6 ; i++)
scanf(“%d”,&b[i] ) ;
sort( b,6 ); /*数组名作参数*/
for ( i=0 ; i<6 ; i++)
printf(,%3d”,b[i] ) ;
}
void sort ( int a[ ],int n)
{ int i,j,k,t ;
for ( i=0 ; i<n-1 ; i++)
{ k=i ;
for ( j=i+1 ; j<n ; j++)
if ( a[k]>a[j] ) k=j ;
if ( k!=i )
{ t=a[i] ; a[i]=a[k] ;
a[k]=t ; }
}}
8.8 局部变量和全局变量一、局部变量,指在一个函数内部定义的变量,它只在本函数的范围内有效,在此函数之外不能使用这些变量说明,
1,main函数中定义的变量是局部变量,它们只能在
main函数中使用
2,不同函数中可以使用相同名字的变量,它们互不干扰
3,形式参数也是局部变量
4,在复合语句中定义的变量,它们只在复合语句内有效
#include <stdio.h>
void sort ( int a[ ],int n);
void main( )
{ int b[6],i ;
for ( i=0 ; i<6 ; i++) scanf(“%d”,&b[i] ) ;
sort( b,6 );
for ( i=0 ; i<6 ; i++) printf(,%3d”,b[i] ) ;
}
void sort ( int a[ ],int n)
{ int i,j,k ;
for ( i=0 ; i<n-1 ; i++)
{ k=i ;
for ( j=i+1 ; j<n ; j++) if ( a[k]>a[j] ) k=j ;
if ( k!=i )
{ int t ;
t=a[i] ; a[i]=a[k] ; a[k]=t ;
}
}
}
main函数中的局部变量,b和 i
sort函数中的局部变量,i,j,k
参数 a和 n也是局部变量
t 是复合语句中的局部变量,
只能以下在 3个赋值语句中使用二、全局变量,指在所有函数之外定义的变量 ( 外部变量 ),
它的有效范围从定义变量的位置开始到本源文件结束
#include <stdio.h>
int p=1,q=5 ;
float f1( int a )
{ float r ;
:
}
int s[10] ;
int f2( int b,int c ) ;
{ int sum ;
:
}
float m,n ;
void main( )
{ float x,y ;
:
}
全局变量 m和
n的有效范围全局变量数组
s的有效范围全局变量 p和
q的有效范围
1,全局变量的使用增加了函数间数据联系的渠道例,求某班成绩的平均分,最高分和最低分
#include <stdio.h>
int max,min ;
float average( int s[ ] );
void main( )
{ int i,sc[40] ;
float aver ;
for(i=0; i<=39 ; i++)
scanf(“%d”,&sc[i]);
aver=average(sc);
printf(“aver=%f\n”,aver);
printf(“max=%d\n”,max);
printf(“min=%d\n”,min);
}
float average( int s[ ] )
{ int sum,i ;
float ave ;
sum=0;
max=min=s[0];
for(i=0; i<=39 ; i++)
{ sum=sum+s[i];
if (s[i]>max) max=s[i];
else
if ( s[i]<min ) min=s[i];
}
ave=sum/40;
return(ave);
}
由于在同一文件中的所有函数都能使用全局变量,所以可以利用全局变量从函数中得到一个以上的返回值
2,建议不要过多的使用全局变量
(1) 全局变量在程序的执行过程中一直占用存储单元
(2) 它使函数的通用性降低
(3) 它会降低程序的清晰性
3,在一个源文件中如果局部变量和全局变量同名,在局部变量起作用的范围内,全局变量不起作用
#include <stdio.h>
int x=10;
void f(void)
{ int x=1;
x=x+1;
printf(“x=%d\n”,x );
}
void main()
{ x=x+1;
printf(“x=%d\t”,x);
f( );
}
输出结果,
x=11
x=2
课后作业:
P202 8.3,8.5,8.10