C语言可变参数函数 —— Linux 编程

一、可变参数介绍

C 语言允许定义参数数量可变的函数,这称为可变参数函数(variadic function)。这种函数需要固定数量的强制参数(mandatory argument),后面是数量可变的可选参数(optional argument)

这种函数必须至少有一个强制参数。可选参数的类型可以变化。可选参数的数量由强制参数的值决定,或由用来定义可选参数列表的特殊值决定。C 语言中最常用的可变参数函数例子是 printf()和 scanf()。这两个函数都有一个强制参数,即格式化字符串。格式化字符串中的转换修饰符决定了可选参数的数量和类型

对于每一个强制参数来说,函数头部都会显示一个适当的参数,像普通函数声明一样。参数列表的格式是强制性参数在前,后面跟着一个逗号和省略号(...),这个省略号代表可选参数。

可变参数函数要获取可选参数时,必须通过一个类型为 va_list 的对象,它包含了参数信息。这种类型的对象也称为参数指针(argument pointer),它包含了栈中至少一个参数的位置。可以使用这个参数指针从一个可选参数移动到下一个可选参数,由此,函数就可以获取所有的可选参数。va_list 类型被定义在头文件 stdarg.h 中。

二 、API介绍

当编写支持参数数量可变的函数时,必须用 va_list 类型定义参数指针,以获取可选参数。在下面的讨论中,va_list 对象被命名为 argptr。可以用 4 个宏来处理该参数指针,这些宏都定义在头文件 stdarg.h 中:

1. void va_start(va_list argptr, lastparam);

宏 va_start 使用第一个可选参数的位置来初始化 argptr 参数指针。该宏的第二个参数必须是该函数最后一个有名称参数的名称。必须先调用该宏,才可以开始使用可选参数。

2. type va_arg(va_list argptr, type);

展开宏 va_arg 会得到当前 argptr 所引用的可选参数,也会将 argptr 移动到列表中的下一个参数。宏 va_arg 的第二个参数是刚刚被读入的参数的类型。

3. void va_end(va_list argptr);

当不再需要使用参数指针时,必须调用宏 va_end。如果想使用宏 va_start 或者宏 va_copy 来重新初始化一个之前用过的参数指针,也必须先调用宏 va_end。

4. void va_copy(va_list dest, va_list src);

宏 va_copy 使用当前的 src 值来初始化参数指针 dest。然后就可以使用 dest 中的备份获取可选参数列表,从 src 所引用的位置开始。

三、举例说明

例 1 中的函数展示了这些宏的使用方法。

【例1】函数 simple()

#include<stdio.h>
#include<stdarg.h>
int simple(int num,...)
{
    int i, result=0;
    va_list vl;                         //va_list指针,用于va_start取可变参数,为char*
    va_start(vl,num);                   //取得可变参数列表中的第一个值
    printf("num:%d, vl:%d\n",num,*vl);
    for (i = 0; i < (num-1); i++)       //这里num表示可变参数列表中有多少个参数  
    {
                //这里把vl往后跳过4个字节(sizeof(int)大小)指向下一个参数,返回的是当前参数(而非下 一个参数)  
                result = va_arg(vl, int);
                //这里打印下,可以看出,vl总是指向result后面的那个参数  
                printf("in for  result:%d,  *vl:%d\n", result, *vl);
    }
    va_end(vl);                                                 //结束标志  

    return result;
}

int main(int argc, char **argv)
{
    int num = argc;
    int i = 0;
    simple(5,1,2,3,4,5);

    return 1;
}

执行效果: 

[root@localhost tmp]# ./a.out 
num:5, vl:1
in for  result:1,  *vl:2
in for  result:2,  *vl:3
in for  result:3,  *vl:4
in for  result:4,  *vl:5

【例2】使用vsnprintf 

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
char *make_message(const char *fmt, ...) 
{
    /* 初始时假设我们只需要不超过100字节大小的空间 */
    int n, size = 100;
    char *p;
    va_list ap;
    if ( (p = (char *) malloc(size*sizeof(char))) == NULL)
    return NULL;
    while (1) 
    {
            /* 尝试在申请的空间中进行打印操作 */
            va_start(ap, fmt);
            n = vsnprintf (p, size, fmt, ap);
            va_end(ap);
            /* 如果vsnprintf调用成功,返回该字符串 */
            if (n > -1 && n < size)
            return p;
            /* vsnprintf调用失败(n<0),或者p的空间不足够容纳size大小的字符串(n>=size),尝试申请更大的空间*/
            size *= 2; /* 两倍原来大小的空间 */
            if ((p = (char *)realloc(p, size*sizeof(char))) == NULL)
            return NULL;
        }
}
int main() 
{
    /* 调用上面的函数 */
    char* str = make_message("%d,%f,%s,%c",5,2.5,"test",'B');
    printf("%s\n",str);
    free(str);
    /* we allocate the memory in the make_message function, so we should release it by caller(main function). */
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u011285208/article/details/86506875
今日推荐