第六章函数
2
回顾 2-1
指针是一个变量,它存储另一个对象的内存地址
指针的声明由基本类型、星号 (*) 和变量名组成
为指针赋值,赋值运算符右侧必须是一个地址如果是普通变量需要在前面加一个取地址运算符 &;
如果是另一个指针变量或者是一个数组,不需要加 &运算符
运算符 * 用于返回指针指向的内存地址中存储的值
3
回顾 2-2
指针算术运算的含义是指针的移动,将指针执行加上或者减去一个整数值 n的运算相当于指针向前或向后移动 n个数据单元
指针可以执行比较相等的运算,用来判断两个指针是否指向同一个变量
指向数组的指针,存储的是数组中元素的地址。
数组 data的第 (i + 1) 个元素的地址可表示为
&data[i] 或 (data+i)
4
目标
描述 C 语言中的函数
掌握 C 语言中常见的内置函数
理解函数原型和函数的返回值
熟练掌握自定义函数的定义和调用
5
void main()
{
::::::
x=x*x*x;
y=y*y*y;
z=z*z*z;
ans1=x+y+z;
a=a*a*a;
b=b*b*b;
c=c*c*c;
ans2=a+b+c;
:::::
}
为什么使用函数重复多次的同一计算类型
void main()
{
::::::
ans1=cube(x,y,z);
ans2=cube(a,b,c);
:::::
}
int cube(int a,b,c)
{
int ans;
ans=(a*a*a)+(b*b*b)+(c*c*c);
return ans;
} 函数主程序
ans
函数可以把相对独立的某个功能抽象出来,使之成为程序中的一个独立实体。可以在同一个程序或其他程序中多次重复使用
6
函数机制的优点
使程序变得更简短而清晰
有利于程序维护
可以提高程序开发的效率
提高了代码的重用性
7
函数类型内置函数:
由C语言系统提供;
用户无须定义,也不必在程序中作类型说明;
只需在程序前包含有该函数定义的头文件 ; 自定义函数:
用户在程序中根据需要而编写的函数 ;
8
6.1 函数的定义与调用
6.1.1 函数定义的一般形式从形式上看,函数分为 有参函数 和 无参函数 两种形式。
1.有参函数定义的一般形式类型名 函数名 (形式参数列表 )
{
局部变量说明;
语句序列部分;
}
一个函数由两大部分组成:函数的说明部分和函数体部分。
其中类型名、函数名和函数参数列表为函数的说明部分,
称为函数头部。花括号,{ }”中的部分称为函数体,包括局部变量说明和语句序列部分。
9
类型名指明了本函数的类型。函数的类型实际上是函数返回值的类型,说明函数将给调用者提供什么类型的返回值。
函数名是由用户定义的标识符。对自定义函数,其命名遵循 C语言标识符的命名规则。
形式参数表在函数名后的括号“()”内,由一个或多个类型标识符及变量标识符组成。在形参表中给出的参数称为形式参数,简称形参,各参数之间用逗号间隔。形式参数可以是各种类型的变量,必须在形参表中给出形参的类型说明。在进行函数调用时,主调函数将赋予这些形式参数实际的值。
在函数体中局部变量说明部分,是对函数体内部所用到的变量的类型说明。语句序列部分是实现函数功能的核心部分,它由C语言的基本语句组成。
10
2.无参函数定义的一般形式类型名 函数名 ()
{
局部变量说明;
语句序列部分;
}
无参函数从定义形式上看与有参函数的主要区别是函数名后的括号“()”没有形式参数,但函数名后的括号不可缺少。
由于无参函数没有参数,因此在调用无参函数时,主调用函数将不给被调用函数传递数据。并且大多数情况下,
都不要求无参函数有返回值,对没有返回值的函数,其
“类型名”可写为 void,表示该函数不返回任何类型的值。
11
函数定义
void displayDiscount()
{
float price,discount_amt;
printf("请输入价格 ");
scanf("%f,&price);
discount_amt = 0.75 * price;
printf("折扣额为 %f",discount_amt);
}
d uble max(double x,double y)
double m;
m=x>y?x:y;
return m;
}
该函数名为 displayDiscount,无参数,使用 void说明无返回值,函数体内的语句用于根据产品的价格求折扣后的价格。
该函数名为 max,它有两个 double类型的参数,返回值为 double类型。在函数体内有三条语句实现了求两个数中较大的数,并将它返回。
12
通过在程序中使用函数名称,可以执行函数中包含的语句,
这称为调用函数
函数之间允许相互调用,也允许嵌套调用
函数还可以自己调用自己,称为递归调用
#include<stdio.h>
void main()
{
:::::::::
set_discount();
displayDiscount();
::::::::
::::::::
}
float set_discount()
{
::::::::::
::::::::::
}
float displayDiscount()
{
::::::::::
::::::::::
}
6.1.2 函数的调用对被调用函数的声明和函数原型
1.首先被调用的函数必须是已经存在的函数(是库函数或用户自己定义的函数)。但光有这一条件还不够。
3.如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)
的后面,应该在主调函数中对被调用的函数作声明。
2.如果使用库函数,还应该在本文件开头用 #include 命令将调用有关库函数时所需用到的信息“包含”到本文件中来。
函数声明的一般形式为,
函数类型 函数名 (参数类型 1,参数名 1,参数类型 2,
参数名 2) ;
声明的作用是把函数名、函数参数的个数和参数类型等信息通知编译系统,以便在遇到函数调用时,编译系统能正确识别函数并检查调用是否合法。
16
函数声明
#include <stdio.h>
float count (int,int);
void main()
{
.,,
.,,
}
float count(int x,int y)
{
.,,
}
函数原型说明在形式上与函数头部类似,最后加一个分号。
原型说明中参数表里的参数名可以不写(只写参数类型)。
注意:
函数的,定义,和,声明,的区别:
函数的定义是指对函数功能的确立,包括指定函数名,函数值类型、形参及其类型、函数体等,它是一个完整的、独立的函数单位。
函数的声明的作用则是把函数的名字、函数类型以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时系统按此进行对照检查。
对被调用的函数作声明
# include <stdio.h>
void main()
{ float add( float x,float y);
/ *对被调用函数 add的声明 */
float a,b,c;
scanf("% f,% f",& a,& b);
c= add( a,b) ;
printf(" sum is % f \n",c);

float add( float x,float y) / *函数首部 */
{ float z; /* 函数体 */
z=x+y;
return( z);

对被调用的函数作声 明
# include <stdio.h>
float add( float x,float y) / *函数首部 */
{ float z; /* 函数体 */
z=x+y;
return( z);

void main()

float a,b,c;
scanf("% f,% f",& a,& b);
c= add( a,b) ;
printf(" sum is % f \n",c);

20
C语言规定,对以下几种情况可以省去主调用函数中对被调用函数的函数说明
(声明)。
(1)当被调用函数的返回值是整型或字符型时,可以不在主调用函数中说明而直接调用。这时系统将自动对被调用函数返回值按整型处理。
2)当被调用函数的函数定义出现在主调用函数之前时,主调用函数中不用再对被调用函数说明,可以直接调用。
21
3)如在所有函数定义之前,在函数外预先说明了各个函数的类型,则在以后的各主调用函数中,不用再对被调用函数作说明。
4)对库函数的调用不需要再作说明,但必须把该函数的头文件用 include命令包含在源文件前部。
22
6.1.3 函数的参数和函数的值
1,函数的参数在调用函数时,主调用函数把实参的值传送给被调用函数的形参,从而实现了主调用函数向被调用函数的数据传递。
参数传递具有以下特点:
( 1)实参与形参各自占用独立的存储单元,
函数调用时才将实参的值传递给形参单元。
23
( 2)形参变量只有在发生函数调用时才临时分配内存单元,在调用结束时,即释放所分配的内存单元。因此形参只在函数内部有效,函数调用结束返回主调用函数后,形参变量不再存在,不要企图在主调用函数中去使用形参变量。
( 3)实参可以是常量、变量、表达式、
函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传递给形参。
24
( 4)实参和形参在数量上、类型上、顺序上应严格一致,否则会发生,类型不匹配,错误。
( 5)函数调用中发生的数据传递是单向的。即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。因此在函数调用过程中,形参的值发生改变不会影响实参的值。
25
函数的值是指函数被调用结束之后,返回给主调用函数的值。
有两种情况可以终止被调用函数的执行并返回到调用它的函数中:一是执行到函数的最后一条语句后自然返回到调用它的函数中的调用处,这种情况一般不要求函数返回值;一是在函数执行过程中遇到返回语句时,将终止后续语句的执行,直接返回到调用它的函数中的调用处,这种情况一般都要求被调用函数返回一个值; C语言中的返回语句为 return语句。
return 语句的一般形式为:
return 表达式;
或 return (表达式 );
函数返回值
26
int divisibleCheck(int num)
{
if (num % 5 == 0)
return 1;
else
return 0;
}
C语言中的函数至多可以返回一个值,不能返回多个值;
返回值的数据类型必须与函数原型中返回值的数据类型匹配;
当遇到 return 语句时,函数执行将终止。
程序控制流将立即返回调用函数;
函数返回值
C 语言中的 return 语句用于向调用函数返回值,
语法如下:
return (<表达式 >);
说明:
( 1)函数的返回值是通过函数中的 return语句获得的。
一个函数中可以有一个以上的 return语句,执行到哪一个 return语句,哪一个语句起作用。
return语句后面的括弧也可以不要例如,,return z ;” 等价于,return ( z ) ;,
return后面的值可以是一个表达式 。
例如,max ( int x,int y )
{ return( x >y? x,y ) ; }
( 2)函数的返回值应当属于某一个确定的类型,在定义函数时指定函数返回值的类型。
例如,下面是 3个函数的首行:
int max( float x,float y ) /* 函数值为整型 */
char letter( char c1,char c2) /* 函数值为字符型 */
double min( int x,int y ) /* 函数值为双精度型 */
注意:
凡不加类型说明的函数,自动按整型处理。
( 3)在定义函数时指定的函数类型一般应该和 return
语句中的表达式类型一致。
如果函数值的类型和 return语句中表达式的值不一致,则以函数类型为准。
对数值型数据,可以自动进行类型转换。即函数类型决定返回值的类型。
( 4)对于不带回值的函数,应当用,void” 定义函数为“无类型”(或称“空类型”)。此时在函数体中不得出现 return语句。
例 返回值类型与函数类型不同
# include <stdio.h>
void main()
{ int max( float x,float y) ;
float a,b;
int c;
scanf("%f,%f,",&a,&b);
c=max(a,b);
printf("Max is %d\n",c);

int max( float x,float y)
{ float z; /* z为实型变量 */
z=x>y?x ∶ y;
return(z);

运行情况如下:
1.5,2.5 ↙
Max is 2
6.2.1 函数的嵌套调用嵌套调用就是在调用一个函数时,其函数体内又包含对另一个函数的调用 。
该图表示了两层嵌套的情形,图中的标号给出了嵌套调用程序的走向。其执行过程是,main函数中执行调用 a函数的语句时,即转去执行 a函数,
在 a函数中执行调用 b函数语句时,又转去执行 b
函数,b函数执行完毕返回 a函数的调用处继续执行,a函数执行完毕返回 main函数的调用处继续执行 main函数的剩余部分,直到结束。
33
void reverse()
{
::::::::::
::::::::::
}
#include<stdio.h>
void main()
{
:::::::::
palindrome();
::::::::
::::::::
}
void palindrome()
{
::::::::
reverse();
::::::::
}
6.2.1 函数嵌套调用从一个函数调用另一个函数称为函数的嵌套调用编程计算 s=1!+2! +3! +……+10!
程序代码如下:
#include <stdio.h>
long f2(int n) /*定义求 n的阶乘函数 */
{
long t=1;
int i;
for(i=1;i<=n;i++) t=t*i;
return t;
}
long f1(int p) /*定义求 n个数的和函数 */
{
int i;
long sum=0;
for(i=1;i<=p;i++)
sum+=f2(i); /*调用求阶乘函数累加到和 */
return sum;
}
main( )
{
long s;
s=f1(10); /*调用求和函数 */
printf("s=%ld\n",s);
}
分析:函数 f2的主要功能是求 n!(n=1,2,…,10)。 函数 f1
的主要功能是调用 f2函数求 n个阶乘的和 。 由于阶乘的值较大,所以函数 f1和 f2及一些变量的类型都说明为长整型,否则会造成计算错误 。
函数的递归调用在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用 。 C语言的特点之一就在于允许函数的递归调用 。
例如:
int f ( int x )
{ int y,z;
z= f ( y ) ;
return( 2 *z ) ;

递归调用的两种形式:
39
/* 此函数用于计算 a 的阶乘 */
int factorial(int a)
{
if (a == 1)
return 1;
else
{
a = a * factorial(a-1);
return a;
}
}
函数递归调用在一个函数体内调用自身称为函数的递归调用有5个人坐在一起,问第5个人多少岁?他说比第4
个人大2岁。问第4个人岁数,他说比第3个人大2
岁。问第3个人,又说比第2个人大2岁。问第2个人,说比第1个人大2岁。最后问第1个人,他说是
10岁。请问第5个人多大。
age( 5) = age ( 4) +2
age( 4) = age ( 3) +2
age( 3) = age ( 2) +2
age( 2) = age ( 1) +2
age( 1) = 10
用数学公式表述如下:
age( n) = 10 (n=1)
age( n-1) +2 (n >1)
可以用一个函数来描述上述递归过程:
int age( int n) / * */
{ int c; / * c用作存放函数的返回值的变量 */
if(n==1) c=10;
else c=age(n-1) +2;
return(c);

运行结果如下:
18
用一个主函数调用 age函数,求得第 5人的年龄 。
#include <stdio.h>
void main( )
{ printf( ″% d ″,age( 5 )) ;

用递归方法求n!
求n!也可以用递归方法,即5!等于4! × 5,
而4!=3! × 4? 1!=1。
可用下面的递归公式表示:
n!=1 (n=0)
n · (n-1)! (n> 0)
#include <stdio.h>
long fac(int n) /*定义求 n的阶乘的递归函数 */
{
long f;
if(n<0) printf("n<0,input error");
else if(n==0) f=1;
else f=n*fac(n-1);
return(f);
}
main( )
{
int n;
long y;
printf("\ninput a inteager number:\n");
scanf("%d",&n);
y=fac(n);
printf("%d!=%ld",n,y);
}
分析:函数 fac是一个递归函数,由主函数调用 fac。 进入 fac后,如果 n<0,出错返回,如果 n==0,返回 1并结束函数的执行,否则就递归调用 fac函数自身 。 由于每次递归调用的实参为 n-1,即把 n-1的值赋予形参 n,最后当 n-1的值为 1时将使递归终止 。 然后可逐层退回 。
46
总结 2-1
函数是程序中的一个相对独立的单元或模块,程序在需要时可以任意多次地调用函数来完成特定功能
使用函数带来的好处,程序更清晰、易维护、分模块方便设计与开发、提高代码的重用性
C语言提供了极为丰富的内置函数,这些内置函数分门别类地放在不同的头文件中,要使用这些内置函数,只要在程序前包含相应的头文件即可
47
总结 2-2
自定义函数是用户在程序中根据需要而编写的函数
函数的结构包括:返回值类型、函数名、参数列表、
函数体
函数原型说明以便向编译器指出该函数使用什么样的格式和语法
函数调用时程序控制流将转向被调函数,被调函数执行结束时,控制流返回主调函数
return 语句用于向调用函数返回值