Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1901116
  • 博文数量: 217
  • 博客积分: 4362
  • 博客等级: 上校
  • 技术积分: 4180
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-20 09:31
文章分类

全部博文(217)

文章存档

2017年(1)

2015年(2)

2014年(2)

2013年(6)

2012年(42)

2011年(119)

2010年(28)

2009年(17)

分类: C/C++

2011-06-20 21:28:03

    最近,遇到一个c语言的不定参数问题。其实,对于c语言的不定参数问题,只需要三个函数就可以搞定了。这三个函数的头文件是,其实下面的三个函数都是一个宏定义(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的返回值进行判断,通过查阅资料,都是这么说的,看来我的猜想是对的。当我把程序写出来进行测试的时候,发现不是这样的:
  1. #include <stdio.h>
  2. #include <stdarg.h>

  3. int sum(const int , ...);

  4. int main(void)
  5. {
  6.     printf("The result is:%d\n", sum(10, 9, 8));
  7.     return 0;
  8. }

  9. int sum(const int first, ...)
  10. {
  11.     va_list argp;
  12.     int sum = 0;
  13.     int tmp;

  14.     va_start(argp, first);
  15.     sum += first;
  16.     printf("%d\n", first);
  17.     while((tmp = va_arg(argp, int)) != 0) {
  18.         printf("%d\n", tmp);
  19.         sum += tmp;
  20.     }
  21.     va_end(argp);
  22.     return sum;
  23. }
这个程序的运行结果是:
10
9
8
6676468
134513824
The result is:141190319

    这个结果说明,通过va_arg的返回值进行参数是否取完来判断是有问题的。
    会不会是通过argp的值来判断的呢?让我们来做个测试:
  1. #include <stdio.h>
  2. #include <stdarg.h>

  3. int sum(const int , ...);

  4. int main(void)
  5. {
  6.     printf("The result is:%d\n", sum(10, 9, 8));
  7.     return 0;
  8. }

  9. int sum(const int first, ...)
  10. {
  11.     va_list argp;
  12.     int sum = 0;
  13.     int tmp;

  14.     va_start(argp, first);
  15.     sum += first;
  16.     printf("%d\n", first);
  17.     while(argp) {
  18.         tmp = va_arg(argp, int);
  19.         printf("%d\n", tmp);
  20.         sum += tmp;
  21.     }
  22.     va_end(argp);
  23.     return sum;
  24. }
    这个程序的执行结果出乎我的意料,出现了段错误。
    至于如何修改这个程序把不定参数取出来,我还是没有找到解决方法。后来,我想到了printf()函数,我查看了它的源代码,其中主要是调用了vsprintf()函数,至于为什么调用vsprintf()函数,我想可能是为了实现类似于fprintf()之类的函数调用的方便,这样也提高了函数的利用率。printf()函数的主要代码:
  1. 328 va_start(args, fmt);
  2. 329 n = vsprintf(sprint_buf, fmt, args);
  3. 330 va_end(args);
    我继续查看了vsprintf()函数,结果发现,在这个函数当中,它好像是通过判断字符串当中“%”号的多少来决定后面参数的个数的。想到这里,我断定,在想调用不定参数这样的函数的时候,其实是需要指出参数的个数的,只是是通过间接的方式。比如我们最熟悉的printf()函数,其实我们在第一个参数当中,通过%号已经指出了参数的个数,不是吗?
    想到这里,我想到了之前看到man手册中给出的例子为什么是这样的:
  1. #include <stdio.h>
  2.        #include <stdarg.h>

  3.        void
  4.        foo(char *fmt, ...)
  5.        {
  6.            va_list ap;
  7.            int d;
  8.            char c, *s;

  9.            va_start(ap, fmt);
  10.            while (*fmt)
  11.                switch (*fmt++) {
  12.                case 's': /* string */
  13.                    s = va_arg(ap, char *);
  14.                    printf("string %s\n", s);
  15.                    break;
  16.                case 'd': /* int */
  17.                    d = va_arg(ap, int);
  18.                    printf("int %d\n", d);
  19.                    break;
  20.                case 'c': /* char */
  21.                    /* need a cast here since va_arg only
  22.                       takes fully promoted types */
  23.                    c = (char) va_arg(ap, int);
  24.                    printf("char %c\n", c);
  25.                    break;
  26.                }
  27.            va_end(ap);
  28.        }
    这里的话,不是就通过第一个参数指定之后才读取的吗?其实我觉得是间接的告诉了参数的个数。
    通过上面的分析,下面做了一个简单的不定参数的应用。
    问题描述:给定一些字符串,求出它们的最长开始字串。
    实验代码如下:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <stdarg.h>

  5. void fun(char *fmt, ...);

  6. int main()
  7. {
  8.     fun("sss", "fanabcd", "fanfanfanfan", "fanyyyyyyyyyyyy");
  9.     return 0;    
  10. }

  11. void fun(char *fmt, ...)
  12. {
  13.     va_list argp;
  14.     char * str, res[20] = {0};
  15.     int i;

  16.     va_start(argp, fmt);
  17.     if(*fmt == 's') {
  18.         str = va_arg(argp, char *);
  19.         strcpy(res, str);
  20.     }
  21.     fmt++;
  22.     while(*fmt) {
  23.         if(*fmt++ == 's') {
  24.             str = va_arg(argp, char *);
  25.             i = 0;
  26.             while(res[i] != '\0') {
  27.                 if(res[i] != str[i]) {
  28.                     res[i] = 0;
  29.                     break;
  30.                 }
  31.                 i++;
  32.             }
  33.         }
  34.     }
  35.     va_end(argp);
  36.     printf("The result is:%s\n", res);
  37. }
    程序的执行结果是:
The result is:fan
    通过这样的折腾,就把c语言的不定参数简单地应用起来了。
阅读(4543) | 评论(6) | 转发(6) |
给主人留下些什么吧!~~

sunjiangang-ok2011-06-22 15:28:42

fera: 不好意思,我搞错了。其实是需要参数个数的。。。
对于误导我表示抱歉。.....

fera2011-06-21 21:53:59

sunjiangang-ok: 我改写了你的程序,为什么一执行总是段错误?能帮忙看一下吗?

#include <stdio.h>
#include <stdarg.h>
int sum(int a, ...)
{
        va_list.....
不好意思,我搞错了。其实是需要参数个数的。。。
对于误导我表示抱歉。

azfa1232011-06-21 19:31:11

printf("%d%d",i);printf("%d",i,j);也是可以的。根据格式化字符串有需求就从栈上拿,不关心到底参数是不是够或者不够。

void va_start(va_list ap, last);除了找出首个参数,有时候需要把寄存器里的参数压栈(以Arm为例)。

sunjiangang-ok2011-06-21 17:56:30

fera: 扯淡。根本不需要参数个数。看这个:
int sum(int a, ...)
{
        va_list argp;
        int tmp;
        va_start(argp, a);
        while(argp != 0)
.....
我改写了你的程序,为什么一执行总是段错误?能帮忙看一下吗?

#include <stdio.h>
#include <stdarg.h>
int sum(int a, ...)
{

fera2011-06-21 16:24:32

c语言函数调用的约定