📄 第8章 函 数.txt
字号:
C语言编程常见问题解答
发表日期:2003年10月2日 已经有2085位读者读过此文
第8章 函 数
函数是C语言的基本构件,要成为一个优秀的程序员,必须很好地掌握函数的编写方法和使用方法。本章将集中讨论与函数有关的问题,例如什么时候说明函数,怎样说明函数,使用函数的种种技巧,等等。
在阅读本章时,请回忆你曾编写过的函数,看看你是否已尽可能提高了这些函数的效率;如果没有,请应用本章所介绍的一些技术,以提高你的程序的速度和效率。此外,请注意本章所介绍的一些实用编程技巧,其中的一些例子能有效地帮助你提高编写函数的技能。
8. 1 什么时候说明函数?
只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件。例如,如果函数stat_func()只在源文件stat.c中使用,应该这样说明:
/* stat.c */
# include <atdio.h>
atatic int atat_func(int,int); /* atatic declaration of atat-funcO */
void main (void);
viod main (void)
{
......
rc=stat_func(1,2);
......
}
/* definition (body) of stat-funcO */
static int stat-funcdnt argl,int arg2)
{
return rc;
}
在上例中,函数stat_func()只在源文件stat.c中使用,因此它的原型(或说明)在源文件stat.c以外是不可见的,为了避免与其它源文件中可能出现的同名函数发生冲突,应该将其说明为内部函数。
在下例中,函数glob_func()在源文件global.c中定义和使用,并且还要在源文件extern,c中使用,因此应该在一个头文件(本例中为proto.h)中说明,而源文件global.c和extern.c
中都应包含这个头文件。
File: proto.h
/* proto.h */
int glob_func(int,int); /* declaration of the glob-funcO function * /
File: global. c
/* global. c */
# include <stdio.h>
# include "proto. h" /*include this file for the declaration of
glob_func() */
viod main(void);
viod main (void)
{
rc_glob_func(l,2);
}
/* deHnition (body) of the glob-funcO function */
int glob_func(int argl,int arg2)
{
return rc;
}
File extern. c
/* extin.c */
# include <atdio.h>
# include "proto. h" /*include thia file for the declaration of
glob_func() */
void ext_func(void);
void ext_func(void)
{
/* call glob_func(), which ia deHncd in the global, c source file * /
rc=glob_func(10,20);
}
在上例中,在头文件proto.h中说明了函数glob_func(),因此,只要任意一个源文件包含了该头文件,该源文件就包含了对函数glob_func()的说明,这样编译程序就能检查在该源文件中glob_func()函数的参数和返回值是否符合要求。请注意,包含头文件的语句总是出现在源文件中第一条说明函数的语句之前。
请参见;
8.2 为什么要说明函数原型?
8.3 一个函数可以有多少个参数?
8.4 什么是内部函数?
8.2 为什么要说明函数原型?
函数原型能告诉编译程序一个函数将接受什么样的参数,将返回什么样的返回值,这样编译程序就能检查对函数的调用是否正确,是否存在错误的类型转换。例如,现有以下函数原型;
int some_func(int,char·,long);
编译程序就会检查所有对该函数的引用(包括该函数的定义)是否使用了三个参数并且返回一个int类型的值。如果编译程序发现函数的调用或定义与函数原型不匹配,编译程序就会报告出错或警告消息。例如,对上述函数原型来说,当编译程序检查以下语句时,就会报告出错或警告消息:
x = some_func(l); /* not enough arguments passed */
x = somc_funcC*HELLOl", 1, "DUDE:"); /* wrong type of arguments used */
x = aome_funcd, sir, 2879, "T"); /* too many arguments passed */
下例中的函数调用同样是不正确的,因为函数some_func()的返回值不是一个long*类型的值。
lValue=some_func(1,str,2879); /*some_rune()returns anint,not a long* */
同样,编译程序还能检查函数的定义(或函数体)是否与函数原型匹配。例如,当编译程序检查以下函数定义时,就会报告出错或警告消息:
int some_func(char *string,longlValue,int iValue) /* wrong order Of
{ parameters */
......
}
总之,在源文件中说明函数原型提供了一种检查函数是否被正确引用的机制。目前许多流行的编译程序都会检查被引用的函数的原型是否已在源文件中说明过,如果没有,就会发出警告消息。
请参见:
8.1什么时候说明函数?
8.3一个函数可以有多少个参数?
8.4什么是内部函数?
8.3 一个函数可以有多少个参数?
一个函数的参数的数目没有明确的限制,但是参数过多(例如超过8个)显然是一种不可取的编程风格。参数的数目直接影响调用函数的速度,参数越多,调用函数就越慢。另一方面,参数的数目少,程序就显得精练、简洁,这有助于检查和发现程序中的错误。因此,通常应该尽可能减少参数的数目,如果一个函数的参数超过4个,你就应该考虑一下函数是否编写得当。
如果一个函数不得不使用很多参数,你可以定义一个结构来容纳这些参数,这是一种非常好的解决方法。在下例中,函数print_report()需要使用10个参数,然而在它的说明中并没有列出这些参数,而是通过一个RPT_PARMS结构得到这些参数。
# include <atdio. h>
typedef struct
(
int orientation ;
char rpt_name[25];
char rpt_path[40];
int destination;
char output_file[25];
int starting_page;
int ending_page;
char db_name[25];
char db_path[40];
int draft_quality;
)RPT_PARMS;
void main (void);
int print_report (RPT_PARMS* );
void main (void)
{
RPT_PARMS rpt_parm; /*define the report parameter
structure variable * /
/* set up the report parameter structure variable to pass to the
print_report 0 function */
rpt_parm. orientation = ORIENT_LANDSCAPE;
rpt_parm.rpt_name = "QSALES.RPT";
rpt_parm. rpt_path = "Ci\REPORTS"
rpt_parm. destination == DEST_FILE;
rpt_parm. output_file = "QSALES. TXT" ;
rpt_parm. starting_page = 1;
rpt_pann. ending_page = RPT_END;
rpt_pann.db_name = "SALES. DB";
rpt_parm.db_path = "Ci\DATA";
rpt_pann. draft_quality = TRUE;
/*call the print_report 0 function; paaaing it a pointer to the
parameteM inatead of paMing it a long liat of 10 aeparate
parameteM. * /
ret_code = print_report(cu*pt_parm);
}
int print_report(RPT_PARMS*p)
{
int rc;
/*acccM the report parametcra paaaed to the print_report()
function */
oricnt_printcr(p->orientation);
Kt_printer_quality((p->draft_quality == TRUE) ? DRAFT ; NORMAL);
return rc;
}
上例唯一的不足是编译程序无法检查引用print_report()函数时RPT_PARMS结构的10个成员是否符合要求。
请参见:
8.1 什么时候说明函数?
8.2 为什么要说明函数原型?
8.3 什么是内部函数?
8.4 什么是内部函数?
内部函数(用static关键字说明)是作用域只限于说明它的源文件的函数。作用域指的是函数或变量的可见性。如果一个函数或变量在说明它的源文件以外也是可见的,那么就称它具有全局或外部作用域;如果一个函数或变量只在说明它的源文件中是可见的,那么就称它具有局部或内部作用域。
内部函数只能在说明它的源文件中使用。如果你知道或希望一个函数不会在说明它的源文件以外被使用,你就应该将它说明为内部函数,这是一种好的编程习惯,因为这样可以避免与其它源文件中可能出现的同名函数发生冲突。
请看下例:
#include <stdio.h>
int open_customer_table(void); /*global function, callable from
any module * /
static int open_customer_indexes(void); /*local function, used only in
this module * /
int open_customer_table(void)
{
int ret_code;
/* open the customer table * /
......
if (ret_code == OK)
{
ret_code = opcn_customer_indexes();
}
return ret_code;
}
static int open_customer_indexes(void)
{
int ret_code;
/* open the index files used for this table * /
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -