精华内容
下载资源
问答
  • 1:当无法列出传递函数的所有实参的类型和数目时,可用省略号指定参数表void foo(...);void foo(parm_list,...);2:函数参数的传递原理函数参数是以数据结构:栈的形式存取,从右至左入栈.eg:先介绍一下可变参数表的调用...

    1:当无法列出传递函数的所有实参的类型和数目时,可用省略号指定参数表

    void foo(...);

    void foo(parm_list,...);

    2:函数参数的传递原理

    函数参数是以数据结构:栈的形式存取,从右至左入栈.eg:

    先介绍一下可变参数表的调用形式以及原理:

    首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:

    void func(int x, float y, char z);

    那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。

    下面是 里面重要的几个宏定义如下:

    typedef char* va_list;

    void va_start ( va_list ap, prev_param ); /* ANSI version */

    type va_arg ( va_list ap, type );

    void va_end ( va_list ap );

    va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。

    在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);

    然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;

    然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;

    获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。

    例如 int max(int n, ...); 其函数内部应该如此实现:

    int max(int n, ...) {                // 定参 n 表示后面变参数量,定界用,输入时切勿搞错

    va_list ap;                         // 定义一个 va_list 指针来访问参数表

    va_start(ap, n);                       // 初始化 ap,让它指向第一个变参,n之后的参数

    int maximum = -0x7FFFFFFF;          // 这是一个最小的整数

    int temp;

    for(int i = 0; i < n; i++) {

    temp = va_arg(ap, int);          // 获取一个 int 型参数,并且 ap 指向下一个参数

    if(maximum < temp) maximum = temp;

    }

    va_end(ap);                         // 善后工作,关闭 ap

    return max;

    }

    // 在主函数中测试 max 函数的行为(C++ 格式)

    int main() {

    cout << max(3, 10, 20, 30) << endl;

    cout << max(6, 20, 40, 10, 50, 30, 40) << endl;

    }

    基本用法阐述至此,可以看到,这个方法存在两处极严重的漏洞:其一,输入参数的类型随意性,使得参数很容易以一个不正确的类型获取一个值(譬如输入一个float,却以int型去获取他),这样做会出现莫名其妙的运行结果;其二,变参表的大小并不能在运行时获取,这样就存在一个访问越界的可能性,导致后果严重的 RUNTIME ERROR。

    #include

    void fun(int a, ...)

    {

    int *temp = &a;

    temp++;

    for (int i = 0; i < a; ++i)

    {

    cout << *temp << endl;

    temp++;

    }

    }

    int main()

    {

    int a = 1;

    int b = 2;

    int c = 3;

    int d = 4;

    fun(4, a, b, c, d);

    system("pause");

    return 0;

    }

    Output::

    1

    2

    3

    4

    3:获取省略号指定的参数

    在函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束。像这段代码:

    void TestFun(char* pszDest, int DestLen, const char* pszFormat, ...)

    {

    va_list args;

    va_start(args, pszFormat); //一定要“...”之前的那个参数

    _vsnprintf(pszDest, DestLen, pszFormat, args);

    va_end(args);

    }

    4.va_start使argp指向第一个可选参数。va_arg返回参数列表中的当前参数并使argp指向参数列表中的下一个参数。va_end把argp指针清为NULL。函数体内可以多次遍历这些参数,但是都必须以va_start开始,并以va_end结尾。

    1).演示如何使用参数个数可变的函数,采用ANSI标准形式

    #include 〈stdio.h〉

    #include 〈string.h〉

    #include 〈stdarg.h〉

    /*函数原型声明,至少需要一个确定的参数,注意括号内的省略号*/

    int demo( char, ... );

    void main( void )

    {

    demo("DEMO", "This", "is", "a", "demo!", "");

    }

    /*ANSI标准形式的声明方式,括号内的省略号表示可选参数*/

    int demo( char msg, ... )

    {

    /*定义保存函数参数的结构*/

    va_list argp;

    int argno = 0;

    char para;

    /*argp指向传入的第一个可选参数,msg是最后一个确定的参数*/

    va_start( argp, msg );

    while (1)

    {

    para = va_arg( argp, char);

    if ( strcmp( para, "") == 0 )

    break;

    printf("Parameter #%d is: %s\n", argno, para);

    argno++;

    }

    va_end( argp );

    /*将argp置为NULL*/

    return 0;

    }

    2)//示例代码1:可变参数函数的使用

    #include "stdio.h"

    #include "stdarg.h"

    void simple_va_fun(int start, ...)

    {

    va_list arg_ptr;

    int nArgValue =start;

    int nArgCout=0;     //可变参数的数目

    va_start(arg_ptr,start); //以固定参数的地址为起点确定变参的内存起始地址。

    do

    {

    ++nArgCout;

    printf("the %d th arg: %d\n",nArgCout,nArgValue);     //输出各参数的值

    nArgValue = va_arg(arg_ptr,int);                      //得到下一个可变参数的值

    } while(nArgValue != -1);

    return;

    }

    int main(int argc, char* argv[])

    {

    simple_va_fun(100,-1);

    simple_va_fun(100,200,-1);

    return 0;

    }

    3)//示例代码2:扩展——自己实现简单的可变参数的函数。

    下面是一个简单的printf函数的实现,参考了中的例子

    #include "stdio.h"

    #include "stdlib.h"

    void myprintf(char* fmt, ...)        //一个简单的类似于printf的实现,//参数必须都是int 类型

    {

    char* pArg=NULL;               //等价于原来的va_list

    char c;

    pArg = (char*) &fmt;          //注意不要写成p = fmt !!因为这里要对//参数取址,而不是取值

    pArg += sizeof(fmt);         //等价于原来的va_start

    do     {         c =*fmt;         if (c != '%')         {             putchar(c);            //照原样输出字符         }         else         {            //按格式字符输出数据            switch(*++fmt)            {             case'd':                 printf("%d",*((int*)pArg));                           break;             case'x':                 printf("%#x",*((int*)pArg));                 break;             default:                 break;             }             pArg += sizeof(int);               //等价于原来的va_arg         }         ++fmt;     }while (*fmt != '\0');     pArg = NULL;                               //等价于va_end     return; } int main(int argc, char* argv[]) {     int i = 1234;     int j = 5678;         myprintf("the first test:i=%d\n",i,j);     myprintf("the secend test:i=%d; %x;j=%d;\n",i,0xabcd,j);     system("pause");     return 0; }

    展开全文
  •   在C中,当我们无法确定函数的所有实参的类型和数目时,可以用省略号指定参数表来处理,如: void foo(...); void foo(parm_list,...);   具体怎么使用呢,我们首先需要把函数参数的传递原理说清楚。 函数参数...

    引出

      在C中,当我们无法确定函数的所有实参的类型和数目时,可以用省略号指定参数表来处理,如:

    void foo(...);
    void foo(parm_list,...);
    

      具体怎么使用呢,我们首先需要把函数参数的传递原理说清楚。

    函数参数的传递原理

      函数参数传递的时候是从右至左依次入栈的,然后调用的时候再依次出栈。先入栈的在栈底,后入栈的在栈顶。在内存中,栈底是高地址,栈顶是低地址。
      举个例子如下:

    void func(int x, float y, char z);   
    

      调用此函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,就可以顺藤摸瓜找到其他的输入变量。

    使用方法

      首先需要包含头文件<stdarg.h>,下面是 <stdarg.h> 里面重要的几个宏定义如下:

    typedef char* va_list;
    void va_start ( va_list ap, prev_param ); /* ANSI version */
    type va_arg ( va_list ap, type );
    void va_end ( va_list ap );
    

    va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
    <Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
    <Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 函数来实现的,该函数的第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“…”之前的那个参数;
    <Step 3> 然后是获取参数,调用va_arg函数,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;
    <Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end函数,他把输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。

    使用例子1

    #include <stdarg.h>
    #include <stdio.h>
    
    int sum(int, ...);
    
    int main(void)
    {
       printf("10、20 和 30 的和 = %d\n",  sum(3, 10, 20, 30) );
       printf("4、20、25 和 30 的和 = %d\n",  sum(4, 4, 20, 25, 30) );
    
       return 0;
    }
    
    int sum(int num_args, ...)
    {
       int val = 0;
       va_list ap;
       int i;
    
       va_start(ap, num_args);
       for(i = 0; i < num_args; i++)
       {
          val += va_arg(ap, int);
       }
       va_end(ap);
     
       return val;
    }
    

      当需要对多个数进行和运算,但是数的个数不确定,就可以使用参数表。

    使用例子2

    #include <stdio.h> 
    #include <string.h>
    #include <stdarg.h>  
    
    /*函数原型声明,至少需要一个确定的参数,注意括号内的省略号*/
    int demo(char *fmt, ...);  
    
    void main()   
    {   
       demo("DEMO", "This", "is", "a", "demo!", "");   
    }   
    
    int demo( char *fmt, ... )   
    {   
       va_list argp;   
       int argno = 0;    
       char *para;           /*argp指向传入的第一个可选参数,msg是最后一个确定的参数*/   
    
       va_start(argp, fmt);   
    
       while (1)   
       {   
          para = va_arg(argp, char *);   
          if (strcmp( para, "") == 0)
    		break;
    
          printf("Parameter #%d is: %s \r\n", argno, para);   
    	  
          argno++;   
       }   
       va_end( argp );        /*将argp置为NULL*/
       return 0;   
    }  
    
    展开全文
  • 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_startva_arg及va_end 说到C语言函数可变参数,...在Linux-2.6.24.7内核源码里,printk函数原型如下: asmlinkageintprintk(constchar*fmt,...) asmlinkage表示通过堆...

    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

    展开全文
  • 一、什么是可变参数我们在C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为:int printf( const char* format, ...);它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的...
  • va_start 用法

    2020-11-10 14:41:14
    1. 在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表 void foo(...); void foo(parm_list,...); 这种方式和我们以前认识的不大一样,但我们要记住这是C中一种传参的形式,在后面我们...
  • FROM:...在以前的一篇帖子Format MessageBox详解中曾使用到va_startva_end这两个宏,但对它们也只是泛泛的了解。介绍这两个宏之前先看一下C中传递函数的参数时的用法和原理:引言:在C中,如果...
  • VA_START()和VA_END()函数的应用

    千次阅读 2017-08-18 15:27:48
    原理解释: VA_LIST 是在C语言中解决变参问题的一组宏,在... (2)然后用VA_START宏初始化变量刚定义的VA_LIST变量,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数。  (3)然后用VA_AR
  • (3)因为编译器对可变参数的函数原型检查不够严格,对编程查错不利.不利于我们写出高质量的代码/ int main(){ Outputinfo(); Outputinfo(“你好,哈尔冰”); outPutInfo(2,“Beijing”,2022,33,“chunjie”); ...
  • 形如 int func(int , ...) {} //第一个参数代表参数个数,第二个代表参数。。。就像printf一样。  函数参数是以数据... 那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内...
  • 一、什么是可变参数在C语言编程中有时会遇到一些参数个数可变的函数,例如printf(),scanf()函数,其函数原型为:int printf(const char* format,…),int scanf(const char *format,…);它除了有一个参数format固定...
  • 头文件 #include "stdarg.h" //TI的BLE代码中包含下面这个头文件 //#include "printf-stdarg.h" ...#define va_start(list,param1) ( list = (va_list)¶m1+ sizeof(param1) )va_start宏,获取可变参数
  • 原理解释: VA_LIST 是在C语言中解决变参问题的一组宏,在头文件... (2)然后用VA_START宏初始化变量刚定义的VA_LIST变量,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数。  (3)然后用VA_AR
  • 我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的,由于1、硬件平台的不同 2、编译器的不同,所以定义的宏也有所不同。 在ANSI C中,这些宏的定义位于stdarg.h中,典型的实现如下: typedef char *va_list...
  • 我们常用的打印函数printf就是一个可变参数的函数,参数个数并不是固定的,函数原型为: int printf(const char *format, ...); 这就是一个典型的变参函数,其中的变参用三个点 ... 表示。 那么该如何编写自己的...
  • 本文主要介绍va_startva_end的使用及原理。  在以前的一篇帖子Format MessageBox 详解中曾使用到va_startva_end这两个宏,但对它们也只是泛泛的了解。  介绍这两个宏之前先看一下C中传递函数的参
  • 在C/C++中,我们经常会用到可变参数的函数(比如printf/snprintf等),本篇笔记旨讲解通过va_start/va_arg/va_end这簇宏来实现可变参数函数的原理,并在文末给出简单的实例。  备注:本文的分析适用于Linux/Windows...
  •  本文主要介绍va_startva_end的使用及原理。  在以前的一篇帖子Format MessageBox 详解中曾使用到va_startva_end这两个宏,但对它们也只是泛泛的了解。  介绍这两个宏之前先看一下C中传递函数的参数时的...
  • C语言 va_startva_end使用详解

    千次阅读 2017-03-15 17:48:52
    本文主要介绍va_startva_end的使用及原理。  介绍这两个宏之前先看一下C中传递函数的参数时的用法和原理:  1.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表 void foo(.....
  • 可变参数函数就是函数的参数有好多个,我们用…省略,我们如果要用到这些函数我们一定会用到va_list,va_startva_arg和va_end这些函数,我们的printf和scanf的函数也是这样定义的,首先我们看可变参数的函数怎么写...
  • 我们在C语言编程中会遇到一些参数个数可变的函数,例如printf()这个函数,它的定义是这样的: int printf( const char* format, ...); 除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的,例如我们可以有...
  • C语言可变参简介 ... 我们在C语言编程中会遇到一些参数个数可变的函数,例如printf()这个函数,它的定义是这样的:  int printf( const char* format, ...);  它除了有一个参数format固定以外
  • va_list,va_start,va_arg,va_end详解

    千次阅读 2013-09-29 14:24:47
    由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦;即使采用C++,如果参数个数不能确定,也很难采用函数重载.对这种情况,有些人采用指针参数来解决问题.下面就c语言中处理不定参数数目的问题进行讨论. ...
  • va_startva_end的使用

    2016-07-25 17:00:04
    本文主要介绍va_startva_end的使用及原理。    介绍这两个宏之前先看一下C中传递函数的参数时的用法和原理:  1.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表 ...
  • va_list函数

    2020-02-02 11:12:38
    本篇文章转载自“小雨滴答”的学习笔记,欲看原文请移步 函数的参数个数不确定时,就可以...2)然后用va_start宏初始化变量刚定义的va_list变量,使其指向第一个可变参数的地址。 3)然后va_arg返回可变参数,va_a...
  • 原型:int printf(const char * format, ...); 参数format表示如何来格式字符串的指令,… 表示可选参数,调用时传递给"..."的参数可有可无,根据实际情况而定。 系统提供了vprintf系列格式化字符串的函数,用于编程...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,763
精华内容 2,305
关键字:

va_start函数原型

友情链接: dsgdg.rar