参考: 里科《C和指针》
int *func();
这个既可以看作是一个旧式风格的声明(只给出func的返回类型),也可以看作是一个没有参数的函数的新风格原型。这个声明必须被解释为旧式风格的声明,目的是保持与ANSI标准前的程序的兼容性。一个没有参数的函数的原型应该写成:
int *func( void );
函数的原型中,参数名不是必须的,但是写上可以给用户提供更多的信息。
应该将所有原型写在一个头文件里,然后用include包含在需要使用的文件中。这样如果需要修改某个原型只需要修改一次,也避免了多处书写原型造成的冲突。
如果程序调用一个无法见到原型的函数时,编译器会认为该函数返回一个整数值。同时,传递给函数的实参会进行缺省参数提升,即char/short转为int,float转为double
float f = xyz()
// 如果在函数调用前,编译器没看到xyz()的原型,就会认为它返回一个整型,但实际它返回的是浮点值
// 那么编译器会将二进制的浮点数以整型解释,再将其转换为float,再赋值给f
- 传递给函数的标量都是传值调用;
- 传递给函数的数组参数在行为上就像它们是通过传地址调用的那样
// 无效交换,因为是传值,所以x和y在函数内修改了并不会返回出来
void
swap(int x, int y)
{
int temp;
temp = x;
x = y;
y = temp;
}
// 有效交换
void
swap(int* x, int* y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
swap(&x, &y);
声明数组参数时可以不指定长度,因为函数不会为数组元素分配内存,是通过间接访问去找数组元素的。但是函数同样无法判断数组参数的长度,所以如果需要的话必须作为参数显式传递。
void
clear_array( int array[], int n_elements)
{
while (n_elements > 0)
array[--n_elements] = 0;
}
递归的两个特性:1)存在限制条件:当符合条件时停止;2)每次递归调用后越来越接近限制条件
递归的优点是代码简单清晰,如果这一优点可以补偿效率开销的话,可用。
尾递归tail recursion:递归调用是函数所执行的最后一个任务。尾递归可以改写成循环,一般能更快。
long
fac( int n )
{
if( n <= 0 )
return 1;
else
return n * fac( n - 1 );
}
可变参数:var_arg必须指定正确的类型,因为可变参数实际传递给函数时,会经历缺省参数提升,如果还想保持正确的类型,就需要指定
#include <stdarg.h>
float
average(int n_values, ...) {
va_list var_arg;
int count;
float sum = 0;
//准备访问可变参数
va_start( var_arg, n_values );
for (count = 0; count < n_values; count++) {
// 添加取自可变参数列表的值
sum += va_arg(var_arg, int);
}
// 完成处理可变参数
va_end( var_arg );
return sum / n_values;
}
average(3, 2, 4, 6)