函数声明与定义
定义
无参函数:
返回值类型 函数名()
{
语句;
}
有参函数:
返回值类型 函数名(形式参数)
{
语句;
}
声明
声明仅包含函数头,不包含函数体。
参数传递
首先,我们要区分清楚形式参数与实际参数的区别。简单来说,我们传递给函数的参数是实际参数,然后在函数内部执行时,使用的是形式参数。
当我们调用一个函数时,会给形参分配响应的内存单元,同时将实参的值复制给形参,在执行过程中不会影响实参,执行完后,再将形参释放。特殊的,要注意指针、数组、结构体作为参数的情况:
- 指针作为参数时,传递的是“地址”,也就是说,实际存储的东西都是同一个。所以对指针形参操作会影响所指向的变量。
- 数组名实际上是第一个元素的地址,所以函数内对数组的操作会影响外部的数组。
- 结构体名本身也是第一个元素的地址,所以同2.
多个返回值
一个 return 只能返回一个值,如果要返回多个值,可以用如下方法:
- 用全局变量(不建议)
- 传递数组指针:在参数中传递数组
- 传递结构体指针:在参数中传递结构体
#include<stdio.h>
int* test(int p[2])
{
p[0]++;
p[1]++;
return p;
}
int main(void)
{
int p[2]={1,2};
int *q=test(p);
printf("%d %d\n",q[0],q[1]);
return 0;
}
注意,不能返回局部指针。因为它指向的地址会随着函数结束而释放。
回调函数
回调函数就是把函数当作参数传入另一个函数。这是通过函数指针实现的。比如下面这个例子:我们想在链表中查找特定值,这个值可能是整数,也可能是浮点数。我们通过传入 compare,使得这个函数对浮点数和整数都适用。
Node *search_List(Node *node, int (*compare)(void const *, void const *))
{
while(node != Null)
{
if compare((node->value_address), desired_value) ==0
{
break;
}
node = node->next;
}
return node;
}
typedef struct list
{
void *value_address;
struct list *next;
} Node;
int int_compare(void const *a, void const *b)
{
if (*(int *)a == *(int *)b)
{
return 0;
}
else
{
return 1;
}
}
desired_node=search_list(root, int_compare, &desired_int_value);
如果想通过统一接口实现不同功能,就要使用回调函数。
变参函数
某些函数的参数是可变的,比如 printf()
,它的函数原型是 int printf(const char*, ...);
,可以这样调用:printf('%d',i)
,也可以这样调用 printf("%d%s",i,s)
要实现可变参数,可以这样做:
#include<stdio.h>
#include<stdarg.h>
void func(int start, ...)
{
va_list arg_ptr;
int nArgValue = start;
int nArgCout=0;
va_start(arg_ptr,start);
do
{
++nArgCout;
printf("the %dth arg: %d\n", nArgCout, nArgValue);
nArgValue = va_arg(arg_ptr,int);
}while(nArgValue != -1);
return;
}
int main(void)
{
func(100,-1);
func(100,200,-1);
}
//输出
the 1th arg: 100
the 1th arg: 100
the 2th arg: 200
解释如下:
stdarg.h
是用于处理可变参数的头文件- 可变参数相当于一串数组
- 先定义参数数组
va_list arg_ptr
,然后用va_start(arg_ptr, start)
初始化,让它指向最后一个固定参数 - 然后用
va_arg
依次返回可变参数,并解释按照指定类型解释 - 设置结束条件,这里的结束条件是最后一个可变参数是
-1
要注意的点:
- 至少要有一个固定参数
- 在可变长参数中,应用的是加宽原则:char、short 加宽为 int,float 加宽为 double。所以获取 flaot 类型的参数,需要用
va_arg(argp, double)