精华内容
下载资源
问答
  • 1、va_start函数 原型:void va_start(va_list ap, last); ap:va_list 类型的变量,指向参数的指针 last:最后一个显式声明的参数,以用来获取第一个变长参数的位置 2、va_arg函数 原型:type va_arg(va_list ap, ...

    本文主要介绍可变参数 函数的实现方法。

    头文件:#include <stdarg.h>

    一、函数系列介绍
    1、va_start函数
    原型:void va_start(va_list ap, last);
    ap:va_list 类型的变量,指向参数的指针
    last:最后一个显式声明的参数,以用来获取第一个变长参数的位置
    2、va_arg函数
    原型:type va_arg(va_list ap, type);
    ap:va_list 类型的变量,指向参数的指针
    type:指要获取的参数的类型
    3、va_end函数
    原型:void va_end(va_list ap);
    ap:va_list 类型的变量,指向参数的指针
    4、va_copy函数
    原型:void va_copy(va_list dest, va_list src);

    二、函数详细介绍

    typedef char *  va_list;
    #define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
    #define ADDRESSOF(v)   ( &reinterpret_cast<const char &>(v) )
    
    #define va_start _crt_va_start
    #define va_arg _crt_va_arg
    #define va_end _crt_va_end
    
    #define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
    #define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
    #define _crt_va_end(ap)      ( ap = (va_list)0 )
    

    三、使用步骤介绍
    1)定义一个va_list类型的变量,变量是指向参数的指针;
    2)va_start初始化刚定义的变量,第二个参数是最后一个显式声明的参数;
    3)va_arg返回变长参数的值,第二个参数是该变长参数的类型;
    4)va_end将a)定义的变量重置为NULL。

    四、示例
    示例1:

    void test_printf2(int num,...)
    {
        va_list args;
        int i = 0;
        va_start(args,num);
        for(i; i < num; i++)
            printf("%d\n", va_arg(args, int));
        va_end(args);
    }
    test_printf2(3,1,2,3);
    

    示例2:

    void test_printf6(char *headData, char *format,...)
    {
        char buff[4096];
        va_list args;
    
        memcpy(buff, headData, strlen(headData));
        va_start(args, format);
        vsnprintf(buff+strlen(headData), 1024, format, args);
        va_end(args);
    
        printf("%s\n", buff);
    }
    
    test_printf6("requestData", "len is %d, data is %s", 10, "helloworld");
    
    

    示例3:

    void test_printf7(char *format,...)
    {
        int tmpLen; 
        char *buff, *buf, *ptr;
        va_list args;
    
        buff = (char *) malloc(sizeof(char)*1024);
        buf = buff; 
        va_start(args, format);
        for (ptr = format; *ptr; ptr++) {
            if(*ptr == '%') {
                switch (*(++ptr)) {
                    case 'd': 
                    *buff++ = va_arg(args, int) + 0x30;
                    break;
                    case 's':
                    tmpLen = va_arg(args, int); 
                    memcpy(buff, va_arg(args, char *), tmpLen);
                    buff += tmpLen;
                    break;
                    case 'c': 
                    *buff++ = va_arg(args, int);
                    break;
                }
            } else{
                printf("%c\n", *ptr);
            }
        }   
        *buff = '\0';
        printf("%s\n", buf);//此处需注意打印要从头指针开始
        va_end(args);
        free(buf);//此处需注意释放的是头指针,否则会出现下面的错误
        buf = NULL;
    }
    
    test_printf7("%d%d%s%c", 1,2,4,"helloWorld", 'E');
    
    

    *** Error in `./a.out’: munmap_chunk(): invalid pointer: 0x0000000000e00047 ***
    ======= Backtrace: =========
    /lib/x86_64-linux-gnu/libc.so.6(+0x77725)[0x7f982c4fa725]
    /lib/x86_64-linux-gnu/libc.so.6(cfree+0x1a8)[0x7f982c506c18]
    ./a.out[0x401575]
    ./a.out[0x4015c8]
    /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f982c4a3830]
    ./a.out[0x4006d9]
    ======= Memory map: ========
    00400000-00402000 r-xp 00000000 08:01 919832 /home/zx/test/a.out
    00601000-00602000 r–p 00001000 08:01 919832 /home/zx/test/a.out
    00602000-00603000 rw-p 00002000 08:01 919832 /home/zx/test/a.out
    00e00000-00e21000 rw-p 00000000 00:00 0 [heap]
    7f982c26d000-7f982c283000 r-xp 00000000 08:01 792176 /lib/x86_64-linux-gnu/libgcc_s.so.1
    7f982c283000-7f982c482000 —p 00016000 08:01 792176 /lib/x86_64-linux-gnu/libgcc_s.so.1
    7f982c482000-7f982c483000 rw-p 00015000 08:01 792176 /lib/x86_64-linux-gnu/libgcc_s.so.1
    7f982c483000-7f982c643000 r-xp 00000000 08:01 792138 /lib/x86_64-linux-gnu/libc-2.23.so
    7f982c643000-7f982c842000 —p 001c0000 08:01 792138 /lib/x86_64-linux-gnu/libc-2.23.so
    7f982c842000-7f982c846000 r–p 001bf000 08:01 792138 /lib/x86_64-linux-gnu/libc-2.23.so
    7f982c846000-7f982c848000 rw-p 001c3000 08:01 792138 /lib/x86_64-linux-gnu/libc-2.23.so
    7f982c848000-7f982c84c000 rw-p 00000000 00:00 0
    7f982c84c000-7f982c872000 r-xp 00000000 08:01 792110 /lib/x86_64-linux-gnu/ld-2.23.so
    7f982ca56000-7f982ca59000 rw-p 00000000 00:00 0
    7f982ca6e000-7f982ca71000 rw-p 00000000 00:00 0
    7f982ca71000-7f982ca72000 r–p 00025000 08:01 792110 /lib/x86_64-linux-gnu/ld-2.23.so
    7f982ca72000-7f982ca73000 rw-p 00026000 08:01 792110 /lib/x86_64-linux-gnu/ld-2.23.so
    7f982ca73000-7f982ca74000 rw-p 00000000 00:00 0
    7ffe93f65000-7ffe93f86000 rw-p 00000000 00:00 0 [stack]
    7ffe93ff0000-7ffe93ff2000 r–p 00000000 00:00 0 [vvar]
    7ffe93ff2000-7ffe93ff4000 r-xp 00000000 00:00 0 [vdso]
    ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
    Aborted (core dumped)

    示例4:

    void test_printf5(char *data, char *format,...)
    {
        char buff[1024];
        char *tmpBuff, *ptr;
        va_list args;
        int tmpLen = 0;
    
        tmpBuff = buff; 
        memset(tmpBuff, 0x00, sizeof(buff));
        memcpy(tmpBuff, data, strlen(data));
        tmpBuff += strlen(data);
        va_start(args, format); 
        for(ptr = format; *ptr; ptr++) {
            switch (*ptr) {
                case 'd':
                    *tmpBuff++ = va_arg(args, int) + 0x30;
                    break;   
                case 's':    
                    tmpLen = va_arg(args, int);
                    *tmpBuff++ = tmpLen + 0x30;
                    memcpy(tmpBuff, va_arg(args, char *), tmpLen);
                    tmpBuff += tmpLen;
                    break;
                case 'c':
                    *tmpBuff++ = va_arg(args, int);
                    break;
            }
        } 
        va_end(args);
        *tmpBuff = '\0';    
        printf("%s\n", buff);
        return ;
    }
    
    test_printf5("reqeustData", "dcdsdsc",2,'A',3,4,"buffer",5,3,"heloo",'C');
    
    展开全文
  • C语言函数之可变参数原理:va_start、va_arg及va_end 说到C语言函数可变参数,我们最先想到的可能就是printf、scanf、printk了。在Linux-2.6.24.7内核源码里,printk函数原型如下: asmlinkageintprintk(const...

    C语言函数之可变参数原理:va_start、va_arg及va_end

     

        说到C语言函数可变参数,我们最先想到的可能就是printf、scanf、printk了。在Linux-2.6.24.7内核源码里,printk函数原型如下:

    asmlinkage int printk(const char *fmt, ...)

     

        asmlinkage表示通过堆栈传递参数。gcc编译器在汇编过程中调用c语言函数时传递参数有两种方法:一种是通过堆栈,另一种是通过寄存器。缺省时采用寄存器,假如你要在你的汇编过程中调用c语言函数,并且想通过堆栈传递参数,你定义的c函数时要在函数前加上宏asmlinkage。

        从printk函数原型可知,printk除了接收一个固定参数fmt外,后面的参数用...表示。在C/C++语言中,...表示可以接收定数量的参数(0或0以上个参数)。那么printk到底是怎么支持可变参数的呢?

        我们先来看几个宏:va_list、va_start、va_arg及va_end(va的意思应该是variable),在Linux-2.6.24.7内核源码里,其定义(内核里的定义与C语言库的定义是类似的)如下

     

    /*
      * Use local definitions of C library macros and functions
      * NOTE: The function implementations may not be as efficient
      * as an inline or assembly code implementation provided by a
      * native C library.
      */


    #ifndef va_arg

    #ifndef _VALIST
    #define _VALIST
    typedef char *va_list;
    #endif                /* _VALIST */

    /*
     * Storage alignment properties
     */

    #define _AUPBND (sizeof (acpi_native_int) - 1)
    #define _ADNBND (sizeof (acpi_native_int) - 1)

    /*
     * Variable argument list macro definitions
     */

    #define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))
    #define va_arg(ap, T) (*(*)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
    #define va_end(ap) (void) 0
    #define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))

    #endif                /* va_arg */

    1、va_list

        va_list表示可变参数列表类型,实际上就是一个char指针

    2、va_start

        va_start用于获取函数参数列表中可变参数的首指针(获取函数可变参数列表)
      * 输出参数ap(类型为va_list): 用于保存函数参数列表中可变参数的首指针(即,可变参数列表)
      * 输入参数A: 为函数参数列表中最后一个固定参数

    3、va_arg

        va_arg用于获取当前ap所指的可变参数并将并将ap指针移向下一可变参数
      * 输入参数ap(类型为va_list): 可变参数列表,指向当前正要处理的可变参数
      * 输入参数T: 正要处理的可变参数的类型
      * 返回值: 当前可变参数的值

     

        在C/C++中,默认调用方式_cdecl是由调用者管理参数入栈操作,且入栈顺序为从右至左,入栈方向为从高地址到低地址。因此,第1个到第n个参数被放在地址递增的堆栈里。所以,函数参数列表中最后一个固定参数的地址加上第一个可变参数对其的偏移量就是函数的可变参数列表了(va_start的实现);当前可变参数的地址加上下一可变参数对其的偏移量的就是下一可变参数的地址了(va_arg的实现)。这里提到的偏移量并不一定等于参数所占的字节数,而是为参数所占的字节数再扩展为机器字长(acpi_native_int)倍数后所占的字节数(因为入栈操作针对的是一个机器字),这也就是为什么_bnd那么定义的原因。

     

    4、va_end

        va_end用于结束对可变参数的处理。实际上,va_end被定义为空.它只是为实现与va_start配对(实现代码对称和"代码自注释"功能)

     

        对可变参数列表的处理过程一般为:

    1、用va_list定义一个可变参数列表

    2、用va_start获取函数可变参数列表

    3、用va_arg循环处理可变参数列表中的各个可变参数

    4、用va_end结束对可变参数列表的处理
     

    下面是一个例子:

    #include <stdio.h>
    #include <stdarg.h>   /* 使用va_list、va_start等必须包含的头文件 */
    #include <string.h>

    /* linux C没有itoa函数,所以要自己写 */
    char *itoa(int i, char *str)
    {
        int mod, div = fabs(i), index = 0;
        char *start, *end, temp;

        do
        {
            mod = div % 10;
            str[index++] = '0' + mod;
            div = div / 10;
        }while(div != 0);

        if (< 0)
            str[index++] = '-';

        str[index] = '\0';

        for (start = str, end = str + strlen(str) - 1;
            start < end; start++, end--)
        {
            temp    = *start;
            *start    = *end;
            *end    = temp;
        }
        
        return str;
    }

    void print(const char *fmt, ...)
    {
        char str[100];
        unsigned int len, i, index;
        int iTemp;
        char *strTemp;
        va_list args;

        va_start(args, fmt);
        len = strlen(fmt);
        for (i=0, index=0; i<len; i++)
        {
            if (fmt[i] != '%')    /* 非格式化参数 */
            {
                str[index++] = fmt[i];
            }
            else                /* 格式化参数 */
            {
                switch(fmt[i+1])
                {
                case 'd':        /* 整型 */
                case 'D':
                    iTemp = va_arg(args, int);
                    strTemp = itoa(iTemp, str+index);
                    index += strlen(strTemp);
                    i++;
                    break;
                case 's':        /* 字符串 */
                case 'S':
                    strTemp = va_arg(args, char*);
                    strcpy(str + index, strTemp);
                    index += strlen(strTemp);
                    i++;
                    break;
                default:
                    str[index++] = fmt[i];
                }
            }
        }
        str[index] = '\0';
        va_end(args);

        printf(str);        
    }

    int main()
    {
        print("Version: %d; Modifier: %s\n", -958, "lingd");
        return 0;
    }

        另一个比较实用的例子:printk只能在内核初始化完控制台(console_init)后才能使用。因此,在Linux内核初始化控制台前,只能使用其它函数来打印内核消息。这些函数有:

    void printascii(const char *); 
    void printhex(unsigned long value, int nbytes); 
    void printhex2(unsigned char value); 
    void printhex4(unsigned short value); 
    void printhex8(unsigned long value);

        这些函数都是用汇编实现的,并直接从低层操作s3c2410的串口,并显示信息。因此不需要等到console_init后就可以显示信息。
        为了使用这些函数,需要特性的内核选项支持:make menuconfig 
          Kernel hack ->
           [*]Kernel low-level debugging functions 
           [*]Kernel low-level debugging messages via S3C UART
           (0)S3C UART to use for low-level debug

        但是,这些函数并没有printk功能那么强大,支持可变参数,支持格式化字符串。为了在Linux内核初始化控制台前,能使用了一个类似于printk的函数,我们将printascii函数封装到新函数debug里:

    extern void printascii(const char *);

    static void debug(const char *fmt, ...)
    {
        va_list va;                
        char buff[256];

        va_start(va, fmt);
        
        /* 函数vsprintf:用于输出格式化字符串到缓冲区
         * 输出参数buff:用于保存结果的缓冲区
         * 输入参数fmt: 格式化字符串
         * 输入参数va:  可变参数列表
         * 返回值:      实际输出到缓冲区的字符数
         */

        vsprintf(buff, fmt, va);
        va_end(va);

        printascii(buff);
    }

    转自:http://blog.chinaunix.net/uid-23089249-id-34493.html

    展开全文
  • 函数原型:int vsnprintf(char *str, size_tsize, const char *format, va_listap); 函数说明:将可变参数格式化输出到一个字符数组 参数: str输出到的数组,size指定大小,防止越界,format格式化参数,ap可变参数...

    vsnprintf函数
    头文件:#include  <stdarg.h>
    函数原型:int vsnprintf(char *str, size_t size, const char *format, va_list ap);
    函数说明:将可变参数格式化输出到一个字符数组
    参数:
    str输出到的数组,size指定大小,防止越界,format格式化参数,ap可变参数列表函数用法
    #include <stdio.h>
    #include <stdarg.h>
     
    void SYSTEM(const char * format, ...)
    {
        char buff[4069];
        va_list list;
        va_start(list, format);
        vsnprintf(buff, 4069, format, list);
        va_end(list);
        printf("%s\n", buff);
    }
     
    void main()
    {
        SYSTEM("%d_%s", 6, "abc");
    }
    我们了解下可变参数,先看下面几个定义
    typedef char *  va_list;
    #define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )  
    #define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )   //得到可变参数中第一个参数的首地址  
    #define va_arg(ap,type)    ( *(type *)((ap += _INTSIZEOF(type)) - _INTSIZEOF(type)) )    //将参数转换成需要的类型,并使ap指向下一个参数  
     #define va_end(ap)      ( ap = (va_list)0 )  
    函数参数保存到栈中,根据va_start(ap,v)宏定义,可以看出此函数获得可变参数受地址,接着通过va_arg取得各可变参数,最后调用va_end函数把ap指向空。写面例子分别打印出指定数目的可变参数
     #include <stdio.h>
    #include <stdarg.h>
     
    void SYSTEM(int count, ...)
    {
        va_list ap;
        va_start(ap, count);
        int i = 0;
        for(i; i < count; i++)
        {
            printf("%d\n",va_arg(ap, int));
        }
        
        va_end(ap);
    }
     
    void main()
    {
        SYSTEM(3, 1, 2, 3);
    }
    结果打印出
    1
    2
    3

     

    展开全文
  • va_start,va_end, va_arg

    2013-07-25 17:19:43
    在我们编程的过程中,有时可能会需要一个参数个数不确定的函数。比如说我们想求不确定个数字的和:int sum(...),但是我们...这几个宏的原型及涉及宏的原型:#define va_start _crt_va_start #define va_arg _crt_va_arg

      在我们编程的过程中,有时可能会需要一个参数个数不确定的函数。比如说我们想求不确定个数字的和:int sum(...),但是我们怎么来确定函数里面的参数呢,怎么取得几个数字相加呢,要定位参数的位置,va_start,va_end,va_arg就派上用场了。这几个宏的原型及涉及宏的原型:

    #define va_start _crt_va_start
    #define va_arg _crt_va_arg
    #define va_end _crt_va_end
    #define _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
    #define _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
    #define _crt_va_end(ap)      ( ap = (va_list)0 )
    #define _ADDRESSOF(v)   ( &reinterpret_cast<const char &>(v) )
    第一个宏也就是找到第二个参数的位置,现在把函数写具体一点:

    int add_ser(int num, ...)
    {
    	int nSum = 0;
    	va_list arg;
    	va_start(arg, num);
    	cout << *(int*)arg << endl;
    	for(int i=0; i<num; ++i){
    		printf("%p ", arg);
    		nSum += va_arg(arg,int);
    		cout << endl;
    	}
    	va_end(arg);
    	cout << nSum <<endl;
    
    	return nSum;
    }
    现在arg指向第二个参数,如果参数列表是这个add_ser(4,1,2,3,4);那么arg现在指向1;

    第二个宏是意思是使arg指向下一个数,返回当前的数,通过打印arg便可知。注意:(ap += _INTSIZEOF(t)) - _INTSIZEOF(t)这句意思是ap指向下一个数了,但是返回的是当前数,下一次则返回下一个数,而ap指向下下个数。当va_start(arg,num)完成的时候arg指向1,第一次循环va_arg(arg,int)之后arg已经指向2了,但是va_arg(arg,int)返回的是1。当循环完成后arg指向最后一个数的下一个数

    第三个宏将ap置零

    展开全文
  • 1. 什么是可变参数列表 除了参数列表中有固定参数外, 还有类型与个数不确定的参数... C语言中定义了宏来处理可变列表, va_start, va_arg, va_end这三个宏和va_list这个指针类型。 它们都包含在 stdarg.h头文件中。
  • 1、函数原型: int vsnprintf(char *str, size_t size, const char *format, va_list ap); 某度百科: _vsnprintf是C语言库函数之一,属于可变参数。用于向字符串中打印数据、数据格式用户自定义。 头文件是#...
  • 我们在C语言编程中有时会遇到一些参数个数可变的... 它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的(用三个点“…”做参数占位符) 在程序中将用到以下这些宏: void va_start( va_list arg_ptr,...
  • 我们常用的打印函数printf就是一个可变参数的函数,参数个数并不是固定的...那么该如何编写自己的变参函数呢,这就要用到处理变参的三个方法:va_start, va_arg, va_end , 这三个方法包含在头文件 stdarg.h 中,这三个
  • 如printf()函数,其原型为:int printf( const char* format, …); 它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的,例如我们可以有以下不同的调用方法: printf( “%d “,i); printf( “%...
  • va_start、va_end、va_list的使用 1:当无法列出传递函数的所有实参的类型和数目时,可用省略号指定参数表 void foo(...); void  foo(parm_list,...);   2:函数参数的传递原理 函数参数是以数据结构:栈的...
  • 关于C、C++中可变参数的简介——(va_list,va_start,va_arg,va_end)  由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦,即使采用C++,如果参数个数不能确定,也很难采用函数重载。对这种...
  • 原型:int printf(const char * format, ...); 参数format表示如何来格式字符串的指令,… 表示可选参数,调用时传递给"..."的参数可有可无,根据实际情况而定。 系统提供了vprintf系列格式化字符串的函数,用于编程...
  • c 的 printf 的函数原型,你清楚不? int printf(const char *fmt, ...); // 逗号,后面,即第二个参数是三个点 vsprintf 的函数原型: int vsprintf(char *s, const char *fmt, va_list arg); 你见过va_list类
  •  在C语言编程中有时会遇到一些参数个数可变的函数,例如printf(),scanf()函数,其函数原型为:  int printf(const char* format,…),int scanf(const char *format,…);它除了有一个参数format固定以外,后面...
  • 在Linux-2.6.24.7内核源码里,printf函数原型如下: asmlinkage int printk(const char *fmt, ...)    asmlinkage表示通过堆栈传递参数。gcc编译器在汇编过程中调用c语言函数时传递参数有两种方法...
  • va_list、va_start、va_end

    千次阅读 2011-12-20 15:47:08
    va(variable argument)函数,函数参数的个数可变。C/C++中提供给我们的va函数比较少,scanf()/printf()系列函数,exec()系列。...printf的函数原型为: int printf(const char * szFormat,...);szFormat表
  • va_start 变参

    2018-09-13 10:29:58
    我们常用的打印函数printf就是一个可变参数的函数,参数个数并不是固定的,函数...那么该如何编写自己的变参函数呢,这就要用到处理变参的三个方法:va_start, va_arg, va_end , 这三个方法包含在头文件 stdarg.h ...
  • 由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦,... 如printf()函数,其原型为: int printf( const char* format, ...);它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的,...
  • va_start,va_arg,va_end概述

    2012-02-20 11:05:00
    说实话,第一次接触这三个新的函数确实... 相信大多数人接触这3个函数应该是在数据结构的数组和广义表这一章节中,大家发现了没有在对数组基本操作函数原型说明中很多的变量都用"..."给省略了如下 status Initarra...
  • 相信大多数人接触这3个函数应该是在数据结构的数组和广义表这一章节中,大家发现了没有在对数组基本操作函数原型说明中很多的变量都用"..."给省略了如下  status Initarray(array &a,int din,...);//若维数din和...
  • 在Linux-2.6.24.7内核源码里,printk函数原型如下: asmlinkage int printk(const char *fmt, ...)    asmlinkage表示通过堆栈传递参数。gcc编译器在汇编过程中调用c语言函数时传

空空如也

空空如也

1 2 3 4 5
收藏数 97
精华内容 38
关键字:

va_start函数原型