转自:https://blog.csdn.net/f110300641/article/details/83822290

在ANSI C中,这些宏的定义位于stdarg.h中:

typedef char *va_list;

va_start宏,获取可变参数列表的第一个参数的地址(list是类型为va_list的指针,param1是可变参数最左边的参数):

#define va_start(list,param1)   ( list = (va_list)&param1+ sizeof(param1) )

va_arg宏,获取可变参数的当前参数,返回指定类型并将指针指向下一参数(mode参数描述了当前参数的类型):

#define va_arg(list,mode)   ( (mode *) ( list += sizeof(mode) ) )[-1]

va_end宏,清空va_list可变参数列表:

#define va_end(list) ( list = (va_list)0 )

注:以上sizeof()只是为了说明工作原理,实际实现中,增加的字节数需保证为为int的整数倍

如:#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

为了理解这些宏的作用,我们必须先搞清楚:C语言中函数参数的内存布局。首先,函数参数是存储在栈中的,函数参数从右往左依次入栈。

以下面函数为讨论对象:

void test(char *para1,char *param2,char *param3, char *param4) { va_list list; ...... return; }

在linux中,栈由高地址往低地址生长,调用test函数时,其参数入栈情况如下:

当调用va_start(list,param1) 时:list指针指向情况对应下图:

最复杂的宏是va_arg。

  1.  
    #include <stdio.h>
  2.  
    #include <stdarg.h>
  3.  
     
  4.  
    void var_test(char *format, ...)
  5.  
    {
  6.  
    va_list list;
  7.  
    va_start(list,format);
  8.  
     
  9.  
    char *ch;
  10.  
    while(1)
  11.  
    {
  12.  
    ch = va_arg(list, char *);
  13.  
     
  14.  
    if(strcmp(ch,"") == 0)
  15.  
    {
  16.  
    printf("\n");
  17.  
    break;
  18.  
    }
  19.  
    printf("%s ",ch);
  20.  
    }
  21.  
    va_end(list);
  22.  
    }
  23.  
     
  24.  
    int main()
  25.  
    {
  26.  
    var_test("test","this","is","a","test","");
  27.  
    return 0;
  28.  
    }

附:可变参数应用实例

1.printf实现

  1.  
    #include <stdarg.h>
  2.  
     
  3.  
    int printf(char *format, ...)
  4.  
    {
  5.  
    va_list ap;
  6.  
    int n;
  7.  
     
  8.  
    va_start(ap, format);
  9.  
    n = vprintf(format, ap);
  10.  
    va_end(ap);
  11.  
    return n;
  12.  
    }

2.定制错误打印函数error

  1.  
    #include <stdio.h>
  2.  
    #include <stdarg.h>
  3.  
     
  4.  
    void error(char *format, ...)
  5.  
    {
  6.  
    va_list ap;
  7.  
    va_start(ap, format);
  8.  
    fprintf(stderr, "Error: ");
  9.  
    vfprintf(stderr, format, ap);
  10.  
    va_end(ap);
  11.  
    fprintf(stderr, "\n");
  12.  
    return;
  13.  

va_list 、va_start、 va_arg、 va_end 使用说明【转】的更多相关文章

  1. va_list/va_start/va_arg/va_end深入分析【转】

    转自:http://www.cnblogs.com/justinzhang/archive/2011/09/29/2195969.html va_list/va_start/va_arg/va_end ...

  2. C++省略参数(va_list va_start va_arg va_end)的简单应用

    原文参考自:http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html #include <iostream> #in ...

  3. va_list/va_start/va_arg/va_end深入分析

    http://www.cnblogs.com/justinzhang/archive/2011/09/29/2195969.html

  4. va_start,va_arg,va_end的使用

    一.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表. void fun(...); void fun(parm_list,...); #include <stdi ...

  5. va_list、va_start和va_end使用

    我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的,由于1.硬件平台的不同 2.编译器的不同,所以定义的宏也有所不同. 在ANSI C中,这些宏的定义位于stdar ...

  6. va_start(),va_end()函数应用【转】

    转自:http://www.cnblogs.com/gogly/articles/2416833.html 原理解释: VA_LIST 是在C语言中解决变参问题的一组宏,在<stdarg.h&g ...

  7. va_start(),va_end()函数应用

    原理解释: VA_LIST 是在C语言中解决变参问题的一组宏,在<stdarg.h>头文件下. VA_LIST的用法:            (1)首先在函数里定义一具VA_LIST型的变 ...

  8. 深入C语言可变参数(va_arg,va_list,va_start,va_end,_INTSIZEOF)

    一.什么是可变参数 在C语言编程中有时会遇到一些参数个数可变的函数,例如printf(),scanf()函数,其函数原型为: int printf(const char* format,…),int ...

  9. OC可变參数的函数实现va_start、va_end、va_list的使用

    一.简单介绍 我们常常在编程的时候看见类似这种代码,如图1.1 图1.1 或者是这种可变參数,如图1.2 图1.2 二.基本知识介绍 在学习怎样写这样的格式的函数前,先简介几个经常使用的宏: 下面摘自 ...

随机推荐

  1. 如何在VIM中保存编辑的只读文件

    我们经常碰到这样的情景:在VIM中编辑了一个系统配置文件,当需要保存时才发现当前的用户对该文件没有写入的权限, 这时候怎么办呢? 当需要保存时,输入以下的命令: :w !sudo tee %

  2. Jupyter notebook and Octave kernel installation

    Jupyter notebook 安装 为了更加方便地写 Python 代码,还需要安装 Jupyter notebook. 利用 pip 安装 Jupyter notebook. 为什么要使用 Ju ...

  3. CentOS7 安装 anaconda

    Anaconda是一个开源的Python发行版本,可以帮助我们更方便地配置Python环境. 如果只需要某些包,或者需要节省带宽或存储空间,也可以使用Miniconda这个较小的发行版 0. 开始前 ...

  4. mysql数据库的批量数据导入与导出,性能提升。

    少量数据批量导入:1. 先从数据库把唯一键的值查询出来,放在列表2. 将导入的数据遍历取出,看是否存在列表中,若不在,说明数据库没有.3. 定义两个空列表,一个做为插入数据,一个做为更新数据4. 步骤 ...

  5. Dockerfil

    Dockerfile简介 dockerfile 是一个文本格式的配置文件, 用户可以使用 Dockerfile 来快速创建自定义的镜像,另外,使用Dockerfile去构建镜像好比使用pom去构建ma ...

  6. P1验证性内容

    #include<stdio.h> int main() { printf("201983270555"); return 0; } #include<stdio ...

  7. Qt所有滚动条的样式

    const QString QSS_VerticalScrollBar = "" "QScrollBar:vertical{" //垂直滑块整体 "m ...

  8. FFmpeg 常用API

    一.通用API 1.1 av_register_all() 初始化 libavformat 和注册所有的复用器.解复用器和协议处理器.如果不调用这个函数,可以调用下面的三个函数来选择支持的格式. 注册 ...

  9. vue 移动端上传图片结合localResizeIMG插件进行图片压缩

    localResizeIMG插件的功能是将图片进行压缩,然后转换成base64传给后台. 首先, npm i lrz -save 然后,再main.js里面引入lrz import lrz from ...

  10. vue+element 动态表单验证

    公司最近的项目有个添加动态表单的需求,总结一下我在表单验证上遇到的一些坑. 如图是功能的需求,这个功能挺好实现的,但是表单验证真是耗费了我一些功夫. vue+element在表单验证上有一些限制,必须 ...