函数

函数声明与定义

定义

无参函数:

返回值类型 函数名()
{
    语句;
}

有参函数:

返回值类型 函数名(形式参数)
{
    语句;
}

声明

声明仅包含函数头,不包含函数体。

参数传递

首先,我们要区分清楚形式参数与实际参数的区别。简单来说,我们传递给函数的参数是实际参数,然后在函数内部执行时,使用的是形式参数。

当我们调用一个函数时,会给形参分配响应的内存单元,同时将实参的值复制给形参,在执行过程中不会影响实参,执行完后,再将形参释放。特殊的,要注意指针、数组、结构体作为参数的情况:

  1. 指针作为参数时,传递的是“地址”,也就是说,实际存储的东西都是同一个。所以对指针形参操作会影响所指向的变量。
  2. 数组名实际上是第一个元素的地址,所以函数内对数组的操作会影响外部的数组。
  3. 结构体名本身也是第一个元素的地址,所以同2.

多个返回值

一个 return 只能返回一个值,如果要返回多个值,可以用如下方法:

  1. 用全局变量(不建议)
  2. 传递数组指针:在参数中传递数组
  3. 传递结构体指针:在参数中传递结构体
#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

解释如下:

  1. stdarg.h 是用于处理可变参数的头文件
  2. 可变参数相当于一串数组
  3. 先定义参数数组 va_list arg_ptr,然后用 va_start(arg_ptr, start) 初始化,让它指向最后一个固定参数
  4. 然后用 va_arg 依次返回可变参数,并解释按照指定类型解释
  5. 设置结束条件,这里的结束条件是最后一个可变参数是 -1

要注意的点:

  1. 至少要有一个固定参数
  2. 在可变长参数中,应用的是加宽原则:char、short 加宽为 int,float 加宽为 double。所以获取 flaot 类型的参数,需要用 va_arg(argp, double)