对标题和序号稍加修改。
9.1 复习函数
- 函数(function)是C程序的基本模块。
- 函数原型(prototype),告知编译器在程序中使用该函数,指明函数和参数的类型。
- 函数调用(function call),函数表达式语句,执行函数体部分。
- 函数定义(function declaration),包含完整的函数头和函数体,是函数本身的源代码。
- 签名(signature)是函数的返回值和参数类型信息。
- 函数由函数头和函数体组成。
- 主调函数(calling funciton)通过函数表达式语句调用被调函数(called function)。
9.1.2 实际参数
实际参数(actual argument)是主调函数拷贝给被调函数的具体值,可以是常量、变量或表达式。主调函数将实参放入栈(stack)中,被调函数从栈中读取。
9.1.3 形式参数
如果函数不接受任何参数,函数头()
中加关键字void;若有参数,参数名遵循C语言命名规则。声明参数就创建了形式参数(formal parameter)变量,是函数私有的局部变量(local variable),不会和函数外其他相同名称发生冲突。
9.1.4 特殊的函数头
ANSI C之前的特殊的函数定义:
void fun1(ch,num)
char ch;
int num;
{...}
void fun2(x,y,z)
int x,y,z;
{...}
9.1.5 函数类型
函数声明、原型中必须声明函数类型,且与返回值类型一致。无返回值声明为void类型。示例:
void fun1(...)
{...}
int fun2(...)
{...}
double fun3(...)
{...}
9.1.6 return语句
return语句终止被调函数的运行,控制权返回给主调函数,return语句后的语句不再执行。形式:return expression;
。expression的值就是函数的返回值。主调函数实际得到的值相当于把函数中指定返回的值赋给与函数类型相同的变量所得到的值。
void函数中还可以用return;
形式。
9.2 ANSI C 函数原型
ANSI C之前函数声明只需类型和函数名,不需要参数,因此会导致参数个数与类型不匹配的问题。这种声明也会导致变量类型间的自动转换,使数据有偏差。
函数原型(function prototype)指明了函数的类型和参数列表的信息,编译器可以利用这些信息检查参数类型、数量是否匹配,如果类型不匹配,编译器会将实参转换成形参的类型。编译器会根据函数原型在文件中查找函数定义。
注意:
- 原型可以放在主函数前或主函数内部。
- 原型中的参数名是假名,可以与定义中的参数名不一致。
- 原型中变量声明并未创建存储空间。
- 原型中可以只有类型没有参数名,定义中必须有类型和参数名。
- 原型以
;
结尾。 - 如果函数不接收参数就在原型与定义的参数列表用void。
示例程序:
#include<stdio.h>
void helloworld(void); /*无参函数原型*/
int fun_1(int,int,int); /*无参数名函数原型*/
int main(void)
{
int fun_2(int x,int y);/*与定义参数名不一致的函数原型*/
int num1,num2,num3,max;
helloworld(); /*函数调用*/
num1=num2=4;num3=5;
/*函数调用*/
printf("4+4+5 is %d.\n",fun_1(num1,num2,num3));
max=fun_2(num1,num3); /*函数调用*/
printf("The largest number of 4 and 5 is %d.\n",max);
return 0;
}
/*函数定义*/
void helloworld(void)
{printf("HelloWorld!\n");return;}
int fun_1(int x,int y,int z)
{return x+y+z;}
int fun_2(int a, int b)
{return a>b?a:b;}
输出结果:
HelloWorld!
4+4+5 is 13.
The largest number of 4 and 5 is 5.
9.3 递归
函数自己调用自己就是递归(recursion),递归没有循环效率高,但是简洁。示例程序:
/*简单的求阶乘的递归程序*/
#include<stdio.h>
long fact(int n); /*函数原型*/
int main(void)
{
int num=5;
long result;
result=fact(num);
printf("%d factorial = %d.\n",num,result);
return 0;
}
/*使用递归的函数定义*/
long fact(int n)
{
long result;
if(n>0) result=n*fact(n-1);/*函数调用*/
else result=1;
return result;
}
输出结果:
5 factorial = 120.
这种形式的递归成为尾递归,位于函数末尾,return语句之前,是最简单的递归形式。
9.3.1 递归的基本要点
- 每次函数调用都有自己的变量。
- 每次函数调用完后控制权返回给上一级递归。
- 递归函数中位于递归调用之前的语句均按被调函数的顺序执行。
- 递归函数中位于递归调用之后的语句均按被调函数相反顺序执行。
- 每级递归的变量都是私有的、独立的,但是并没有拷贝函数代码。
- 一定要有满足结束递归的条件。
9.3.2 递归和倒序计算
使用递归和倒叙计算的方式将十进制转换成二进制形式:
#include<stdio.h>
#include<stdlib.h>
void to_binary(unsigned long number); /*函数原型*/
int main(void)
{
unsigned long number; /*存储待转换的十进制值的变量*/
/*文字说明*/
printf("Please enter an integer(enter 'q' to qiut):\n");
while (scanf("%d",&number)==1) /*提示用户重复输入直到输入'q'结束循环*/
{
printf("Binary equivalent: "); /*文字说明*/
to_binary(number); /*函数表达式语句,调用递归函数*/
/*文字说明*/
printf("\nNow enter another integer(enter 'q' to qiut):\n");
}
printf("Done!\n");
system("pause");
return 0;
}
void to_binary(unsigned long number) /*递归函数定义*/
{
int remainder; /*存储余数,即二进制数的每一位,非0即1*/
remainder=number%2; /*求余*/
if (number>=2) /*如果本次递归中被除数大于或等于2说明还有二进制位*/
{
to_binary(number/2); /*函数表达式语句,递归调用*/
}
putchar(remainder==0?'0':'1'); /*输出二进制位的数,非0即1,或为printf("%d",remainder);*/
}
/*递归函数方法二
void to_binary(unsigned long number)
{
int remainder,quotient; //quotient存储本次递归中的商
remainder=number%2;
quotient=number/2; //求商
if (quotient>=1) //如果商大于或等于1说明还有二进制位
{
to_binary(quotient); //函数表达式语句,递归调用
}
putchar(remainder==0?'0':'1');
}*/
输出结果:
Please enter an integer(enter 'q' to qiut):
9
Binary equivalent: 1001
Now enter another integer(enter 'q' to qiut):
255
Binary equivalent: 11111111
Now enter another integer(enter 'q' to qiut):
1024
Binary equivalent: 10000000000
Now enter another integer(enter 'q' to qiut):
q
Done!
9.3.3 递归的优缺点
**优点:**简洁,为特定的问题提供简单的解决方案。
**缺点:**相比循环,效率低,易快速消耗计算机内存且不易读。
9.4 编译多代码文件的程序
使用多源代码文件和添加头文件的方式可以将主函数和被调函数等代码分开,方便修改和维护。编译时除含main()
函数的文件外再加上被调函数的定义文件即可。示例程序:
/*hotel.c--酒店管理函数*/
#include <stdio.h>
#include "hotel.h" //使用“hotle.h”头文件
int showmenu(void) //显示选择列表
{
int code, status; //分别存储选项以及输入状态
printf("\n%s%s\n", STARS, STARS);
printf("Enter the number of the diserd hotle:\n");
printf("1) Fairfield Arms 2) Hotle Olympic\n");
printf("3) Chertowth Plaza 4) The Stocken\n");
printf("5) quit\n");
printf("%s%s\n", STARS, STARS);
while ((status = scanf("%d", &code)) != 1 || (code < 1 || code > 5))
{
if (status != 1)
{
scanf("%*s");
}
printf("Enter an integer from 1 to 5, please.\n");
}
return code;
}
int getnights(void) //返回预定天数
{
int nights; //存储预定天数
printf("How many nights are needed?\n");
while (scanf("%d", &nights) != 1)
{
scanf("%*s");
printf("Please enter an integer.\n");
}
return nights;
}
void showprice(double rate, int nights) //根据费率、入住天数计算费用
{
int n;
double total = 0.0;
double factor = 1.0;
for (n = 1; n <= nights; n++, factor *= DISCOUNT)
{
total += rate * factor;
}
printf("The total cost will be $%0.2f\n", total);
}
/*hotel.h--声明符号常量以及函数原型*/
#define QUIT 5
#define HOTLE1 180.00
#define HOTLE2 225.00
#define HOTLE3 255.00
#define HOTLE4 355.00
#define DISCOUNT 0.95
#define STARS "*********************"
//显示选择列表
int showmenu(void);
//返回预定天数
int getnights(void);
//根据费率、预定天数计算费用并显示结果
void showprice(double rate, int nights);
/*usehotel.c--计算房间费用程序*/
#include <stdio.h>
#include "hotel.h" //使用“hotle.h”头文件
int main(void)
{
int nights, code;
double hotle_rate;
while ((code = showmenu()) != QUIT)
{
switch (code)
{
case 1:
hotle_rate = HOTLE1;
break;
case 2:
hotle_rate = HOTLE2;
break;
case 3:
hotle_rate = HOTLE3;
break;
case 4:
hotle_rate = HOTLE4;
break;
default:
hotle_rate = 0.0;
printf("oops!\n");
break;
}
nights = getnights();
showprice(hotle_rate, nights);
}
printf("Thank you and goodbye!\n");
return 0;
}
编译运行usehotel.c后输出结果:
******************************************
Enter the number of the diserd hotle:
1) Fairfield Arms 2) Hotle Olympic
3) Chertowth Plaza 4) The Stocken
5) quit
******************************************
3
How many nights are needed?
1
The total cost will be $255.00
******************************************
Enter the number of the diserd hotle:
1) Fairfield Arms 2) Hotle Olympic
3) Chertowth Plaza 4) The Stocken
5) quit
******************************************
4
How many nights are needed?
3
The total cost will be $1012.64
******************************************
Enter the number of the diserd hotle:
1) Fairfield Arms 2) Hotle Olympic
3) Chertowth Plaza 4) The Stocken
5) quit
******************************************
5
Thank you and goodbye!