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\n", s);
break;
case 'd': /* int */
d = va_arg(ap, int);
printf("int %d\n", 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\n", 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\n", "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 可变参函数

va_start、va_arg、va_end、va_copy 可变参函数的更多相关文章

  1. va_start,va_arg,va_end的使用

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

  2. 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 ...

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

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

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

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

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

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

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

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

  7. va_list、va_start和va_end使用

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

  8. va_start和va_end使用详解

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

  9. [转载]va_start和va_end使用详解

    va_start和va_end使用详解 原文地址:http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html 本文主要介绍va_s ...

随机推荐

  1. MongoDB使用中的一些问题

    1.count统计结果错误 这是由于分布式集群正在迁移数据,它导致count结果值错误,需要使用aggregate pipeline来得到正确统计结果,例如: db.collection.aggreg ...

  2. cordova linux 安装并编出第一个demo-android

    cordova可以做到一次编写到处运行各个平台(android.ios.wp.bb.firefoxos.web等几乎所有平台) 手上只有一个android手机 ,安装的时候没有那么顺利,第一大问题就是 ...

  3. 20145303 《Java程序设计》第7周学习总结

    20145303 <Java程序设计>第7周学习总结 教材学习内容总结 时间的度量 格林威治标准时间(GMT),现已不作为标准时间使用,即使标注为GMT(格林威治时间),实际上谈到的的是U ...

  4. Spring MVC工作流程图

    图一   图二    Spring工作流程描述       1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获:       2. Disp ...

  5. Mac开发配置手册

    系统设置 在任何的操作系统中,首先你需要做一件事就是更新系统,点击窗口左上角的  > 关于本机 > 软件更新.此外,如果这是一部新的电脑,你还需要到系统设置进行一些适当调整.如何调整,取 ...

  6. [BZOJ1044木棍分割]

    Description 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连 接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段 ...

  7. jsp中获取list长度

    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@ tag ...

  8. Spring IOC 源码解析(持续)

    如何查看源码 Spring源码下载https://github.com/spring-projects/spring-framework/tags?after=v3.1.0.RC1 eclipse关联 ...

  9. Graph_Master(连通分量_H_Trajan+拓扑序dp)

    Graph_Master_连通分量_H 题目描述: 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条 ...

  10. 【cs231n】神经网络笔记笔记2

    ) # 对数据进行零中心化(重要) cov = np.dot(X.T, X) / X.shape[0] # 得到数据的协方差矩阵 数据协方差矩阵的第(i, j)个元素是数据第i个和第j个维度的协方差. ...