函数
出处:按学科分类—工业技术 企业管理出版社《工程师手册》第855页(2821字)
所有C语言程序都是由一个或多个函数组成,即使最先执行的程序部分也是一个称为main()的函数。因而C语言与其它高级语言不同的是,“主”程序与其它被主程序调用的程序之间没有区别,被调用的程序有时也称为子程序。C的函数可以返回一个结果值,也可以不返回任何结果,因此C语言中也不存在象FORTRAN那样的子程序与函数间的区别。C的所有函数都是相互平等的。任一函数都可调用其它函数(甚至可以调用自己),或被其它函数调用,因此C的函数与Pascal的过程有所不同。后者可以嵌套,还应该指的是,C语言总是按值传递函数参数,而不象FORTRAN或别的语言那样按引用传递参数。由于C语言按值传递参数,当一个函数需要修改调用者的某一变量时,相应的参数必须设置为指向该变量在存贮器中的起始地址的指针,
1.函数的定义与声明
一个函数通过函数类型,函数名,一对包含可选形参表的小括号。以及一对包含可选执行语句的大括号组成。函数定义的一般形式为
type name(formal argument list)
argument declarations
{
function body
}
其中type确定函数返回值的类型,而不是参数的类型。如type被略,则认为函数返回int型值。如果函数返回值,则应声明为void类型。
2.存贮类,私用性及作用域
除了类型之外,变量和函数还有一个称为存贮类的性质。C语言共有四种存贮类,标记分别为:auto代表存贮于栈中的自动变量;extern代表存贮于当前模块之外的外部变量;static代表只有当前模块和边的静态变量;register代表存贮目标计算器寄存器中的临时变量。每种存贮类都定义了变量或函数的作用域或私用程度。标记存贮类的关键字(auto,extern,static,register)必须位于变量声明中类型之前。变量或函数的私用性是它模块或函数不能访问该变量或调用该函数的程序,作用域在某种意义上正好与私用性相对,因为变量的作用域表明有哪些模块或函数可以访问该变量。
自动变量只能在函数内部声明,它随函数的调用而创建,随函数的停止运行而消亡。自动变量的作用域限于声明它的函数,函数运行一次结束后自动变量的值不能保持到函数的下一次运行。由于自动变量分配在一堆栈中,仅使用自动变量的函数就可以递归地调用自己,关键字auto在C程序中很少使用,因为在函数中声明的变量,其缺省存贮类就是自动存贮类。
必须注意的是自动变量只有包含声明的控制结构中有定义,也就是说自动变量的作用域不超出包含其声明的最近一对大括号范围,例如,下面的简单程序会产生编译错误,因为j在for循环之外没有定义:
寄存器变量的作用域与自动变量相同,它存贮于目标计算机的寄存器中,如果目标计算机没有空闲的寄存器,则寄存器变量退化为自动变量。由于多数微处理器中都有大量寄存器,其访问速度远快于主存,所以利用寄存器变量可以显着提高程序执行速度。大多数编译器将寄存器变量的使用限于指针,整数和字符,因为目标机器一般没有能力用寄存器进行浮点或双精度操作。
外部变量具有最大作用域,它可被一个模块中的所有函数访问,甚至可以在模块之外,说明并访问。外部变量寄存器在自己单独的数据区中,必须注意不要调用自己,或调用其它也访问同一外部变量的函数。因为外部变量在函数进入或结束远行时都保持其值。在函数外声明的变量的函数自身的缺省存贮类均为外部贮类,因此,没有特别声明的函数都可以被本模块或其它模块中的任意函数调用。
静态变量与外部变量的唯一不同是作用域在函数之外声明的静态变量只对本模块中的函数有效,在函数内部声明的静态变量只对所在函数有效。与自动变量不同,静态变量在函数的两次相邻调用之间可以保持其值。函数也可以声明为静态,这时函数只能被同一模块中的其它函数调用。因此,静态函数可以阻止其它模块的调用。
3.函数模板
虽然函数模板不属于C语言的基本定义,它却已成为标准的C编译器特征。一个函数模板就是一个对函数进行描述的语句(必须以分号结尾)。它告诉编译器函数的类型,(即函数返回的类型)和形参表中每个参数的类型。如果一个函数不在包含其模板的模中定义,则该函数的模板必须声明为外部,所以C编译器都提供一系列头文件,头文件中包含了所有标准C函数的函数模板。例如,3.3.1节中stats函数的模块为
extern void stats(float*,int,float*,float*);
这个模块表明stats(定义在另一模块中)不返结果,有四个参数。第一参数是浮点指数(这里指向被统计的数组),第二个参数是整数(这里给出数组的大小)。后两个变量是浮点指针,分别返回均值和方差。
对一个程序使用的所有函数,使用其函数模板可以使编译器知道每个函数参数类型。这一信息有不同的利用方式,有的编译器将调用程序得提供的实参类型转换为函数模板中说明的类型。并给出一个已进行类型转换的警告。另一些编译器仅仅警告说参数类型不符,并让程序员处理,支持按压缩形式另声明形参表的编译器利用函数带类型的参量表作为该函数在当前模块中的模板,例如将函数stats的前面五行改为
void stats(folat*array,int size,float*ave,float*var)
则该语句也可在包含stats的模块中作为函数模极。函数声明的压缩形式还允许对每个形参使用哑量,这时函数模板与函数声明之间的唯一区别是函数模板末尾的分号。