最近,遇到一个c语言的不定参数问题。其实,对于c语言的不定参数问题,只需要三个函数就可以搞定了。这三个函数的头文件是<stdarg.h>,其实下面的三个函数都是一个宏定义(macro)。
    这三个函数是:
    void va_start(va_list ap, last);
    type va_arg(va_list ap, type);
    void va_end(va_list ap);
    如果需要进行其他的一些操作,可以查看一下man手册进行查询。
    在这三个函数解释之前,先看一个变量va_list,这个变量的类型是什么呢?通过查看内核源代码,一直追踪下去,才发现它的类型是void *类型的。
    对于va_start(va_list ap, last)函数,这个函数是用来初始化指针变量ap(va_list类型)的,以后处理参数就是默认从ap处开始处理。last一般为char *传过来参数列表的第一个参数。
    对于va_arg(va_list ap, type)函数来说,就是将ap指针按照type类型向后移动,然后取出ap指针所指的那个参数。
    对于va_end(va_list ap)一般和va_start(va_list ap, last)配套使用,做一些善后处理的事情。
    这里有一个问题,当我们取参数的时候,如何判断我们要取的参数已经取完了?开始我是这么想的,通过va_arg的返回值进行判断,通过查阅资料,都是这么说的,看来我的猜想是对的。当我把程序写出来进行测试的时候,发现不是这样的:
#include <stdio.h>
#include <stdarg.h>
int sum(const int , ...);
int main(void)
{
    printf("The result is:%d/n", sum(10, 9, 8));
    return 0;
}
int sum(const int first, ...)
{
    va_list argp;
    int sum = 0;
    int tmp;
    va_start(argp, first);
    sum += first;
    printf("%d/n", first);
    while((tmp = va_arg(argp, int)) != 0) {
        printf("%d/n", tmp);
        sum += tmp;
    }
    va_end(argp);
    return sum;
}
这个程序的运行结果是:
10
9
8
6676468
134513824
The result is:141190319
    这个结果说明,通过va_arg的返回值进行参数是否取完来判断是有问题的。
    会不会是通过argp的值来判断的呢?让我们来做个测试:
#include <stdio.h>
#include <stdarg.h>
int sum(const int , ...);
int main(void)
{
    printf("The result is:%d/n", sum(10, 9, 8));
    return 0;
}
int sum(const int first, ...)
{
    va_list argp;
    int sum = 0;
    int tmp;
    va_start(argp, first);
    sum += first;
    printf("%d/n", first);
    while(argp) {
        tmp = va_arg(argp, int);
        printf("%d/n", tmp);
        sum += tmp;
    }
    va_end(argp);
    return sum;
}
    这个程序的执行结果出乎我的意料,出现了段错误。
    至于如何修改这个程序把不定参数取出来,我还是没有找到解决方法。后来,我想到了printf()函数,我查看了它的源代码,其中主要是调用了vsprintf()函数,至于为什么调用vsprintf()函数,我想可能是为了实现类似于fprintf()之类的函数调用的方便,这样也提高了函数的利用率。printf()函数的主要代码:
328 va_start(args, fmt);
329 n = vsprintf(sprint_buf, fmt, args);
330 va_end(args);
    我继续查看了vsprintf()函数,结果发现,在这个函数当中,它好像是通过判断字符串当中“%”号的多少来决定后面参数的个数的。想到这里,我断定,在想调用不定参数这样的函数的时候,其实是需要指出参数的个数的,只是是通过间接的方式。比如我们最熟悉的printf()函数,其实我们在第一个参数当中,通过%号已经指出了参数的个数,不是吗?
    想到这里,我想到了之前看到man手册中给出的例子为什么是这样的:
#include <stdio.h>
       #include <stdarg.h>
       void
       foo(char *fmt, ...)
       {
           va_list ap;
           int d;
           char c, *s;
           va_start(ap, fmt);
           while (*fmt)
               switch (*fmt++) {
               case 's': /* string */
                   s = va_arg(ap, char *);
                   printf("string %s/n", s);
                   break;
               case 'd': /* int */
                   d = va_arg(ap, int);
                   printf("int %d/n", d);
                   break;
               case 'c': /* char */
                   /* need a cast here since va_arg only
                      takes fully promoted types */
                   c = (char) va_arg(ap, int);
                   printf("char %c/n", c);
                   break;
               }
           va_end(ap);
       }
    这里的话,不是就通过第一个参数指定之后才读取的吗?其实我觉得是间接的告诉了参数的个数。
    通过上面的分析,下面做了一个简单的不定参数的应用。
    问题描述:给定一些字符串,求出它们的最长开始字串。
    实验代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
void fun(char *fmt, ...);
int main()
{
    fun("sss", "fanabcd", "fanfanfanfan", "fanyyyyyyyyyyyy");//sss 表示了不定参数的个数
    return 0;    
}
void fun(char *fmt, ...)
{
    va_list argp;
    char * str, res[20] = {0};
    int i;
    va_start(argp, fmt);
    if(*fmt == 's') {
        str = va_arg(argp, char *);
        strcpy(res, str);
    }
    fmt++;
    while(*fmt) {
        if(*fmt++ == 's') {
            str = va_arg(argp, char *);
            i = 0;
            while(res[i] != '/0') {
                if(res[i] != str[i]) {
                    res[i] = 0;
                    break;
                }
                i++;
            }
        }
    }
    va_end(argp);
    printf("The result is:%s/n", res);
}
    程序的执行结果是:
The result is:fan
    通过这样的折腾,就把c语言的不定参数简单地应用起来了

C语言不定参数的更多相关文章

  1. 关于c语言不定参数的研究

    一. 学习过程 编写程序如下: 编译连接并用debug加载,观察main函数的内容: Showchar函数的内容: 观察发现,main函数要传递两个参数‘a’和2,在汇编代码中是先将2赋给ax,再将a ...

  2. C语言函数不定参数实现方式

    函数如何实现不定参数: 由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦,即使采用C++,如果参数个数不能确定,也很难采用函数重载.对这种情况,提出了指针参数来解决问题. (1)va_ ...

  3. go语言基础之不定参数的传递

    1.不定参数的传递 示例1: package main //必须有一个main包 import "fmt" func myfunc(tmp ...int) { for _, dat ...

  4. golang中不定参数与数组切片的区别

    package main import "fmt" func main() { myfunc1(, , , ) //传递不定数量的参数 myfunc2([], , , }) //传 ...

  5. C技巧:结构体参数转成不定参数

    下面这段程序是一个C语言的小技巧,其展示了如何把一个参数为结构体的函数转成一个可变参数的函数,其中用到了宏和内建宏"__VA_ARGS__",下面这段程序可以在GCC下正常编译通过 ...

  6. printf不定参数

    title: printf不定参数 tags: C ARM date: 2018-10-21 12:14:58 --- 不定参数的传递 函数调用时参数传递是使用堆栈来实现的,参数入栈顺序是从右向左,在 ...

  7. c++不定参数函数

    不定参数当年做为C/C++语言一个特长被很多人推崇,但是实际上这种技术并没有应用很多.除了格式化输出之外,我实在没看到多少应用.主要原因是这种技术比较麻烦,副作用也比较多,而一般情况下重载函数也足以替 ...

  8. Go语言 可变参数

    最近与同事讨论时,提到Go语言的可变参数,之前没有总结过相关知识点,今天我们介绍一下Go语言的可变参数. 可变参数(Variable Parameters):参数数量可变的函数称之为可变参数函数,主要 ...

  9. 转:C语言 可变参数

    C语言 可变参数 堆栈一般是怎么压栈处理的 /* * stack space: * *        参数3   |    up *        参数2   | *        参数1   v   ...

随机推荐

  1. Adsafe 导致win10 中窗口错位

    域账号使用,出现上述情况,干掉后一切恢复正常... 还好家里的本地管理员账号使用一切正常,不然又被广告占领了

  2. Codeforces Round #345 (Div. 1) D - Zip-line 带单点修改的LIS 主席树 | 离线树状数组

    D - Zip-line #include<bits/stdc++.h> #define LL long long #define fi first #define se second # ...

  3. email 校验

    email 校验: javascript: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/ java: ^([a- ...

  4. CodeForces 803F Coprime Subsequences

    $dp$. 记$dp[i]$表示$gcd$为$i$的倍数的子序列的方案数.然后倒着推一遍减去倍数的方案数就可以得到想要的答案了. #include <iostream> #include ...

  5. jquery DOM操作(一)

    上一篇文章是记录了jquery中选择器的作用,这里只要记录下jquery中的DOM操作,还是按照上篇的格式来. 下面是测试用的html代码,接下来DOM的操作会在下面的代码下进行. <body& ...

  6. Python开发基础-Day29多线程

    概念 进程:进程就是一个程序在一个数据集上的一次动态执行过程 程序:代码 数据集:程序执行过程中需要的资源 进程控制块:完成状态保存的单元 线程:线程是寄托在进程之上,为了提高系统的并发性 线程是进程 ...

  7. FastReport.Net使用:[17]线(Line)控件使用

    FastReport中,线(Line)控件怎么用?怎么画一条美观的线? 认识Line控件 1.线(Line)控件包含于形状(Shape)控件中,有5个可选项,一个标准线和四个对角线,其实都是同一种线, ...

  8. CUDA学习笔记4:CUDA(英伟达显卡统一计算架构)代码运行时间测试

    CUDA内核运行时间的测量函数 cudaEvent_t start1; cudaEventCreate(&start1); cudaEvent_t stop1; cudaEventCreate ...

  9. 平衡树之treap luoguP3369

    今天又复习了一遍treap,这题有前驱后继排名排位添加和删除等操作. 非常好写,虽然代码颇长但逻辑性很强. #include<bits/stdc++.h> using namespace ...

  10. Codeforces 160 D. Edges in MST

    \(>Codeforces \space 160 D. Edges in MST<\) 题目大意 : 给出一张带权无向图,求对于这张图上的每一条边,其是必然存在于每一种最小生成树中,还是至 ...