zoukankan      html  css  js  c++  java
  • va_start、va_arg、va_end、va_copy 可变参函数


    1、应用与原理

            在C语言中,有时我们无法给出一个函数参数的列表,比如:
      int printf(const char *format, ...);
      int fprintf(FILE *stream, const char *format, ...);

        这时我们使用到了可以变参数,也就是使用...代表0个或多个参数。
        那么编译器如何获取/使用这些参数。这涉及到参数的传递原理:
    参数传递原理:
        在内存中,函数的参数以栈的方式存取,从右到左入栈。这些参数存放的地址是连续的。这样,我们就可以通过获取第一个参数的地址,以及各个参数的地址偏移量,就可以获取每个参数的地址,从而得到每一个参数。

    2、va_start、va_arg、va_end、va_copy介绍

           #include <stdarg.h>
           void va_start(va_list ap, last);
           type va_arg(va_list ap, type);
           void va_end(va_list ap);
           void va_copy(va_list dest, va_list src);

    va_list是一个指向参数首地址的指针,
    typedef struct {
    char *a0; /* pointer to first homed integer argument */
    int offset; /* byte offset of next parameter */
    } va_list;

    va_start     
            对ap进行了一系列的初始化,之后ap会被va_arg和va_end使用到。last是可变参数...的前一个参数,如fun(char *fmt, ...)   的fmt。通过va_start初始化ap,我们就获得了可变参数前一个参数fmt的地址。
    va_arg   
            va_arg用于获取可变参数...的每一个参数。如函数fun(char *fmt, ...)的一次调用fun(fmt, arg1, arg2, arg3)。在使用va_start()进行ap的初始化后,我们调用一次va_arg(ap,type)就获得了参数arg1,在调用一次就获得arg2,……从而,得到每一个参数的值。
    va_end 
            va_end用于清理ap的值,与va_start()配对使用。  
    va_copy 
            不常用,暂不介绍。

    3、Example  

        先给一个man手册里的例子
    //foo.c
    #include <stdarg.h>
    #include <stdio.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
    ", s);
               break;
           case 'd':              /* int */
               d = va_arg(ap, int);
               printf("int %d
    ", d);
               break;
           case 'c':              /* char */
               /* need a cast here since va_arg only
               /* need a cast here since va_arg only
                  takes fully promoted types */
               c = (char) va_arg(ap, int);
               printf("char %c
    ", c);
               break;
           }
       va_end(ap);
    }

    //main.c
    #include <stdio.h>
    #include <stdarg.h>
    #include "foo.h"
    int
    main(void)
    {
            foo("%s %d %c %d %c", "Hello", 4, 'x', 3, 'y');
            return 0;
    }


    执行结果:
    windeal@ubuntu:~/Windeal/apue$ ./exe 
    string Hello
    int 4
    char x
    int 3
    char y
    windeal@ubuntu:~/Windeal/apue$


    使用vsprintf的例子
    //foo.c
    #include <stdarg.h>
    #include <stdio.h>
    void
    foo(char *fmt, ...)
    {
            va_list ap;
            int len = 0;
            char buf[64];
            va_start(ap, fmt);
            len = vsnprintf(buf, 128, fmt, ap);
            va_end(ap);
            int i = 0;
            for(i = 0; i < len; i++)
            {
                    putchar(buf[i]);
            }
            return ;
    }

    // main.c
    #include <stdio.h>
    #include <stdarg.h>
    #include "foo.h"
    int
    main(void)
    {
            foo("Test:%s %d %c %d %c
    ", "Hello", 4, 'x', 3, 'y');
            return 0;
    }


    执行结果:
    windeal@ubuntu:~/Windeal/apue$ ./exe 
    Test:Hello 4 x 3 y

































    va_start、va_arg、va_end、va_copy 可变参函数va_start、va_arg、va_end、va_copy 可变参函数

  • 相关阅读:
    Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权(二)
    Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权(一)
    在Ocelot中使用自定义的中间件(二)
    在Ocelot中使用自定义的中间件(一)
    基于Angular 8和Bootstrap 4实现动态主题切换
    ASP.NET Core 2.0 Web API项目升级到ASP.NET Core 3.0概要笔记
    使用Helm将ASP.NET Core应用程序部署到Kubernetes容器集群
    使用Rancher在Microsoft Azure上搭建Kubernetes集群
    容器化单页面应用中Nginx反向代理与Kubernetes部署
    在C#中使用Irony实现SQL语句的解析
  • 原文地址:https://www.cnblogs.com/Windeal/p/4284636.html
Copyright © 2011-2022 走看看