C和指针——函数

函数的定义:函数的定义就是函数体的实现。函数体就是一个代码块,它在函数被调用时执行。语法如下:

one

形式参数列表包括变量名和它们的类型声明。代码块包含了局部变量的声明和函数调用时需要执行的语句。

在K&C 中,形式参数的类型以单独的列表进行声明,并出现在参数列表和函数体的左花括号之间,如下所示:

two

return语句:该语句允许从函数体的任何位置返回,并不一定要在函数体的末尾。语法如下:

return expression;

expression是可选的。如果函数无需向调用程序返回一个值,它就被省略。这种没有返回值的函数在声明时应该给把函数的类型声明为void。

在C中,子程序不论是否存在返回值,均被称为函数。调用一个真函数(即返回一个值的函数)但不在任何表达式中使用这个返回值是完全可能的。在这种情况下,返回值被丢弃。但是,从表达式内部调用一个过程类型的函数(无返回值)是一个严重的错误,因为这样一来在表达式的求值过程中会使用一个不可预测的值(垃圾)。

原型:第一种 K&R C:如果函数是以旧式风格定义的,也就是用一个单独的列表给出参数的类型,那么编译器就只记住函数的返回值类型,但不保存函数的参数数量和类型方面的信息。第二种使用函数原型。函数原型总结了函数定义的起始部分的声明,向编译器提供有关该函数如何调用的完整信息。分号“ :” 它区分了函数原型和函数定义的起始部分。

关键字void提示没有任何参数,而不是表示它有一个类型为void的参数。

函数的缺省认定:当程序调用一个无法见到原型的函数时,编译器便认为该函数返回一个整型值。

函数的参数:C函数的所有参数均以”传值调用”方式进行传递,这意味着函数将获得参数值的一份拷贝。但是,如果被传递的参数是一个数组名,并且在函数中使用下标引用该数组的参数,那么在函数中对数组元素进行修改实际上修改的是调用程序中的数组元素。函数将访问调用程序的数组元素,数组并不会复制,这个行为称为“传址调用”。数组参数的这种行为似乎与传值调用规则相悖。但是,并不是这样——数组名的值实际上是一个指针,传递给函数的就是这个指针的一份拷贝。但是因为是指针的缘故所以在这份拷贝上执行间接访问操作所访问的是原先的数组。

ADT和黑盒:C可以用于设计和实现抽象数据类(ADT,abstract data type),因为它可以限制函数和数据定义的作用域。这个技巧也被称为黑盒设计。

递归:C通过运行时堆栈支持递归函数的实现。递归函数就是直接或间接调用自身的函数。

stdarg宏:可变参数列表是通过宏来实现的,这些宏定义于stdarg.h头文件,他是标准库的一部分。这个头文件声明一个类型va_list和三个宏——va_start、va_arg和va_end。

可变参数的限制:注意可变参数必须从头到尾按照顺序逐个访问。如果在访问了几个可变参数后想中途终止,这是可以的。但是,如果像一个开始就访问参数列表中间的参数,那是不行的。另外,由于参数列表中的可变参数部分并没原型,所以,所有作为可变参数传递给函数的值都将执行缺省参数类型提升。

如果函数体内没有任何语句,那么该函数就称为存根,它在测试不完整的程序时非常有用。

如果一个递归函数内部所执行的最后一条语句就是调用自身时,那么它就被称为尾部递归。尾部递归可以很容易地改写为循环的形式,它的效率通常更高一些。