1 复习函数
函数(function)是完成特定任务的独立程序代码单元。首先,使用函数可以省去编写重复代码的苦差。其次,即使程序只完成某项任务一次,也值得使用函数。因为函数让程序更加模块化,从而提高了程序代码的可读性,更方便后期修改、完善。
1.1 创建并使用简单函数
这里我们创建一个在一行打印40个星号的函数,并在一个打印表头的程序中使用该函数。如程序清单9.1所示,该程序由mian和startbar组成。
#include <stdio.h>
#define NAME "GIGATHINK, INC."
#define ADDRESS "101 Megabuck Plaza"
#define PLACE "Megapolis, CA 94904"
#define WIDTH 40
void starbar(void);
int main(void){
starbar();
printf("%s\n", NAME);
printf("%s\n", ADDRESS);
printf("%s\n", PLACE);
starbar();
return 0;
}
void starbar(void){
int count;
for (count = 1; count <= WIDTH; count++)
putchar('*');
putchar('\n');
}
该程序的输出如下:
****************************************
GIGATHINK, INC.
101 Megabuck Plaza
Megapolis, CA 94904
****************************************
1.2 分析程序
使用该程序时注意以下几点:
- 程序共有3处使用了starbar标识符:函数原型(function prototype)告诉编译器函数starbar的类型;函数调用(function call)表明在此处执行函数;函数定义(function definition)明确地指定了函数要做什么。
- 函数和变量不一样,有多种类型。任何程序在使用函数之前都要声明该函数的类型。因此,在main函数定义的前面出现了下面的ANSI C风格的函数原型:
void starbar(void); 圆括号表明starbar是一个函数名。第一个void是函数类型,表明函数没有返回值。第二个void(在圆括号中)表明该函数不带参数。分号表明这是在声明函数,不是定义函数。也就是说,这行声明了程序将使用一个名为starbar,没有返回值,没有参数的函数,并告诉编译器在别处查找该函数的定义。对于不识别ANSI C风格原型的编译器,只需声明函数的类型,如下所示: void starbar(); 注意,一些老版本的编译器甚至连void都识别不了。如果使用这种编译器,就要把没有返回值的函数声明为int类型。当然,最好还是换一个新的编译器。 - 一般而言,函数原型指明了函数的返回值类型和函数接受的参数类型。这些信息称为该函数的签名(signature)。对于starbar函数而言,其签名是该函数没有返回值,没有参数。
- 程序把starbar原型置于main的前面,当然也可以放在main里面的声明变量处。
- 在main中,执行到下面的语句时调用了starbar函数:
starbar(); 这是调用void类型函数的一种形式。当计算机执行到starbar(); 语句时,会找到该函数的定义并执行其中的内容。执行完starbar中的代码后,计算机返回主调函数(calling function)继续执行下一行(本例中,主调函数是mian),如图9.1所示 - 程序中starbar和main的定义形式相同。首先函数头包括函数类型、函数名和圆括号,接着是左花括号、变量声明、函数表达式语句,最后以右花括号(如图9.2)结束。
- main中的右花括号告诉编译器main函数结束的位置,后面的starbar函数头告诉编译器starbar是一个函数。
- starbar函数中的变量count是局部变量(local variable)。我们可以在程序中的其他地方(包括main中)使用count,这不会引起名称冲突,它们是同名的不同变量。
1.3 函数参数
程序9.2 lethead2.c
#include <stdio.h>
#include <string.h>
#define NAME "GIGATHINK, INC."
#define ADDRESS "101 Megabuck Plaza"
#define PLACE "Megapolis, CA 94904"
#define WIDTH 40
#define SPACE ' '
void show_n_char(char ch, int num);
int main(void){
int spaces;
show_n_char('*', WIDTH);
putchar('\n');
show_n_char(SPACE, 12);
printf("%s\n", NAME);
spaces = (WIDTH - strlen(ADDRESS)) / 2;
show_n_char(SPACE, spaces);
printf("%s\n", ADDRESS);
show_n_char(SPACE, (WIDTH - strlen(PLACE)) / 2);
printf("%s\n", PLACE);
show_n_char('*', WIDTH);
putchar('\n');
return 0;
}
void show_n_char(char ch, int num){
int count;
for (count = 1; count <= num; count++)
putchar(ch);
}
函数运行结果如下:
****************************************
GIGATHINK, INC.
101 Megabuck Plaza
Megapolis, CA 94904
****************************************
1.4 定义带形式参数的函数
1.5 声明带形式参数函数的原型
1.6 调用带实际参数的函数
待补充 270
2 ANSI C 函数原型
2.1 问题所在
2.2 ANSI的解决方案
针对参数不匹配的问题,ANSI C标准要求在函数声明时还要声明变量的类型,即使用函数原型(function prototype)来声明函数的返回类型、参数的数量和每个参数的类型。以下是两种函数原型:
int imax(int, int);
int imax(int a, int b);
第一种形式使用以逗号分隔的类型列表,第二种形式在类型后面添加了变量名。注意,这里的变量名是假名,不必与函数定义的形式参数一致。
有了这些信息后,编译器可以检查函数调用是否与函数原型匹配,函数的数量是否正确,参数的类型是否匹配。以imax函数为例,如果两个参数都是数字,但是类型不匹配,编译器会把实际参数的类型转换成形式参数的类型。例如,imax(3.0, 5.0)会被转换成imax(3, 5)。用函数原型替换程序9.4 中的函数声明,如程序9.5所示:
程序9.5 proto.c
#include <stdio.h>
int imax(int, int);
int main(void){
printf("The maximum of %d and %d is %d.\n", 3, 5, imax(3));
printf("The maximum of %d and %d is %d.\n", 3, 5, imax(3.0, 5.0));
return 0;
}
int imax(int n, int m){
return (n > m ? n : m);
}
编译程序清单9.5时,编译器会给出调用的imax函数参数太少的错误信息。
如果类型不匹配又会怎样,为了探究这个问题,我们使用imax(3, 5)替换imax(3),然后再次编译该程序。这次编译器没有给出任何错误信息,程序的输出如下:
The maximum of 3 and 5 is 5.
The maximum of 3 and 5 is 5.
2.3 无参数和未指定参数
2.4 函数原型的优点
|