函数的定义
函数的定义如下:
类型 函数名(形式参数)
{
代码块
}
代码块包含了局部变量的声明和函数调用所要执行的语句。
return语句
return 语句的语法如下:
return expression;
当执行流执行到return语句时函数被返回到被调用的地方,return语句允许在任何地方返回,并不要一定在函数的末尾。
表达式expression是可选的,如果函数不需要向调用程序返回一个值,就可以被省略,这种没有返回值的函数可以在声明的时候将其函数的类型置为void。
真函数是从内部表达式调用的,它必须返回一个值,用于表达式的求值,这类函数的return语句必须包含一个表达式,通常函数的类型就是表达式的类型。
只有当编译器可以通过通常的算数转化把表达式的类型转化为正确的类型时,才允许返回值的类型与函数的类型不一致
函数的声明
如果没有函数的声明信息,编译器便假定这个函数的调用时的参数类型和数量是正确的,它同时假定函数的返回值类型是整型。
原型
向编译器提供一些关于函数的信息显然是很安全的,我们可以通过两种方法来实现:
- 如果同一源文件的前面已经出现过该函数的定义,编译器会记住它的参数数量和类型,以及函数的返回值类型。接着编译器会检查该函数的后续调用,确保它们是正确的。
- 向编译器提供函数的原型,使用原型最安全的方法是把原型置于一个单独的文件,当其它文件需要原型时,就使用#include指令包含改文件。
原型的定义如下:
int *find_int(int key,int array[],int len);
函数原型的使用如下:
#include "func.h"
void a()
{
func(&a,b);
}
文件func.h包含了函数func的声明
int func(int *value,int len);
函数的原型同时也被#include包含到定义函数的文件中,编译器就可以确认函数的定义与函数的原型必须匹配。
区分函数原型和函数定义的是后面那个分号(;)。
函数的缺省认定
当函数调用一个无法见到原型的韩束时,编译器认为该函数返回一个整型值。对于那些并不是返回值为整型值的函数调用,这可能引起错误。
值的类型并不是值得内在本质(固有属性),而是取决于它使用的方式。
函数的参数
两个概念:
1.传值调用:传递给被调用函数的是实参的一份拷贝,函数可以放心的修改这个拷贝值,而不用担心会修改调用程序实际传递给它的参数。
2.传址调用:传递给被调用函数的是指针的一份拷贝,假如传递的是数组名,并且在函数中使用下标引用该数组的值,那么在函数中对数组元素的修改其实修改的是调用程序中的数组元素,数组并不会复制。
数组参数的这种行为似乎与传值调用相悖,其实不然,数组名其实就是一个指针,传递函数的就是该指针的一份拷贝,下标应用也是间接访问的一种,所以这份拷贝进行间接访问操作访问的是原先的数组。
总结
- 传递给函数的标量都是传值调用;
- 传递给函数的数组参数在行为上就像他们是通过传址调用一样。
可变参数列表
让一个函数在不同的时刻接受不同数目的参数。
stdarg宏
可变参数列表是通过宏来实现的,这些宏位于stdarg.h,这个头文件声明了一个类型va_list和三个宏——va_start,va_arg,va_end。
可变参数列表实现的步骤
- 用类型va_list声明一个变量var_arg,它用于访问参数列表的未定部分;
- var_arg变量用va_start来初始化,它的第一个参数是va_list变量,第二个参数是省略号前最后一个有名字的参数,初始化过程把var_arg变量设置为指向可变参数部分的第一个参数;
- 用va_arg访问参数,这个宏接受两个参数,va_list变量和参数列表中下一个参数的类型;
- 当访问完参数后,调用va_end;
实例演示:求平均数
#include<stdarg.h>
float average(int n_values,...)
{
valist var_arg; //声明va_list类型的变量
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;
}