如何实现函数来处理可变参数

来源:互联网 发布:新日铁住金软件 薪资 编辑:程序博客网 时间:2024/05/20 18:16

原文地址::http://blog.csdn.net/sunny_ss12/article/details/51042378



假如我需要实现一个log函数,该函数可传入类似于printf的格式化字符串做参数来提供日志提示信息,并且可根据当前errno输出实际的错误原因,如何实现它呢?这可以模仿printf的原型int printf( const char* format, ...) 用... 传入可变参数,然后使用va宏获取可变参数的数据。

1. va宏介绍

由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦,即使采用C++,如果参数个数不能确定,也很难采用函数重载。对这种情况,提出了指针参数来解决问题。如printf()函数,其原型为:

<code class="hljs r has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">int   printf(   const   char*   format,   <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的,例如我们可以有以下不同的调用方法:

<code class="hljs perl has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">printf</span>( <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">%d</span> "</span>,i);   <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">printf</span>( <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">%s</span> "</span>,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">s</span>);   <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">printf</span>( <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"the   number   is   <span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">%d</span>   ,string   is:<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">%s</span> "</span>,   i,   <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">s</span>);  </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>

如何实现其功能?我们需要以下几个宏定义: 
(1) va_list 
va_list是一个指针类型,用于以后指向可变的参数. 
(2)va_start(arg_ptr, argN) 
arg_ptr就是前面定义的va_list类型的变量,当调用va_start后arg_ptr指向第一个可变参数。argN表示最后一个固定参数。比如有一va函数的声明是void va_test(char a, char b, char c, …), 则它的固定参数依次是a,b,c, 最后一个固定参数argN为c, 因此就是va_start(arg_ptr, c). 
(3)va_arg(arg_ptr, type) 
每调用一次va_arg(arg_ptr,type),va_arg就会返回可变参数列表的下一个参数,并且下一个参数类型为type。不断调用va_arg,可以获取所有的可变参数。 
(4)va_end(arg_ptr) 
清空参数列表, 并置参数指针arg_ptr无效. 
(注:va在这里是variable-argument(可变参数)的意思) 
举个例子:定义这么一个函数,函数的第一个参数是固定的,其余参数是可变的。定义为:void simple_va_fun(int i,...); 其代码为:

<code class="hljs cpp has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include <iostream></span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include <stdarg.h></span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">using</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">namespace</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">std</span>;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> simple_va_fun(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i,...);<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> main(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> argc,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> *argv[]){    simple_va_fun(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">100</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">200</span>,<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'a'</span>);    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;}<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> simple_va_fun(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i,...)   {       va_list   arg_ptr;     <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//定义可变参数指针 </span>    va_start(arg_ptr,i);   <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// i为最后一个固定参数,这里i为100</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> j=va_arg(arg_ptr,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>);   <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//返回第一个可变参数,类型为int。这里返回200</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> c=va_arg(arg_ptr,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>);   <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//返回第二个可变参数,类型为char。这里返回'a'</span>    va_end(arg_ptr);        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//  清空参数指针</span>    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">printf</span>( <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%d %d %c\n"</span>,i,j,c);   <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//输出为100 200 a</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>;   }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li></ul>

代码运行解释: 
(1)首先在函数里定义一个va_list型的变量arg_ptr 
(2)然后用va_start宏初始化变量arg_ptr,使它指向第一个可变参数。这个宏的第二个参数是第一个可变参数的前一个参数,也就是最后一个固定参数. 
(3)然后用va_arg返回第一个可变的参数,并赋值给整数j。va_arg的第二个参数是你要返回的参数的类型,这里是int型,返回第一个可变参数后arg_ptr指向第二个可变参数。当第二次调用va_arg后,返回第二个可变参数并赋值给c,类型为char类型。 
(4)最后用va_end宏结束可变参数的获取。

下面这种图对va_startva_arg 进行了解释:

下面这种图对va_start va_arg 进行了解释:

2. 获取格式化字符串和错误字符串

从1可知,使用va宏我们可以获取可变参数,那怎么获取传入参数对应的格式化字符串呢?可以调用vsprintf 或者 vsnprintf ,他们的原型为:

<code class="hljs rust has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> vsprintf(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>* <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">str</span>,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>* format,va_list ap);<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> vsnprintf(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>* <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">str</span>,size_t size,<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>* format,va_list ap);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>

这里formatap、 size 都是传入参数,str 是传入参数,表示格式化后的字符串。

调用strerror 可以获取实际的错误字符串。

3. 简易log函数实现

<code class="hljs r has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">//代码来自于unpv13void err_sys(const char *fmt, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>){    va_list     ap;    va_start(ap, fmt);    err_doit(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>, LOG_ERR, fmt, ap);    va_end(ap);    exit(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>);} void err_msg(const char *fmt, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>){    va_list     ap;    va_start(ap, fmt);    err_doit(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, LOG_INFO, fmt, ap);    va_end(ap);    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>;}void err_doit(int errnoflag, int level, const char *fmt, va_list ap){    int     errno_save, n;    char    buf[MAXLINE + <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>];    errno_save = errno;     /* value caller might want printed */    vsnprintf(buf, MAXLINE, fmt, ap);    n = strlen(buf);    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (errnoflag)        snprintf(buf + n, MAXLINE - n, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">": %s\n"</span>, strerror(errno_save));     <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (daemon_proc) {        syslog(level, buf);    } <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span> {        fflush(stdout);     /* <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">in</span> case stdout and stderr are the same */        fputs(buf, stderr);        fflush(stderr);    }    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>;}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li></ul>

4. 对va宏的解释

我们来看看在vs2008中,这四个宏是怎么定义的:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"><span class="hljs-xmlDocTag" style="box-sizing: border-box;">///</span>stdarg.h</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> va_start _crt_va_start</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> va_arg _crt_va_arg</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> va_end _crt_va_end</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;"><span class="hljs-xmlDocTag" style="box-sizing: border-box;">///</span>vadefs.h</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> _ADDRESSOF(v)   ( &reinterpret_cast<const char &>(v) )</span>typedef <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> *  va_list;<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> _crt_va_start(ap,v)  ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> _crt_va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> _crt_va_end(ap)      ( ap = (va_list)0 )</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul>

先看看va_start的实现:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">define</span> _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

定义_INTSIZEOF(n)主要是为了需要内存的对齐的系统,_crt_va_start(ap,v)表示将ap设为 v的地址+sizeof(v)且与4对齐的数值,这样就将ap指向第一个可变参数。 
为什么呢?因为C语言的函数参数是从右向左压入堆栈的,而堆栈地址是从高地址到低地址分配的。当调用一个函数时,参数 函数返回地址 函数代码在堆栈的分布情况为:

<code class="hljs r has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;">高地址————————————最后一个参数 倒数第二个参数<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>第一个参数函数返回值函数代码————————————低地址</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>

所以对于函数参数而言,右边参数所在的地址 >这里写图片描述 左边参数所在的地址。所以( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )表示将ap指向v变量的下一个变量,即第一个可变参数。 
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )表示将ap指向下一个类型为t变量,并且返回类型为t的前一个数值。这样ap不断指向下一个变量,就可以通过va_arg不断获取下一个可变参数的值 
va_end(ap) 将声明的ap指针置为空。

5. 使用宏处理可变参数

(1)C99中规定宏可以像函数一样带有可变参数,比如

<code class="hljs lasso has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">#define</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">LOG</span>(format, <span class="hljs-attribute" style="box-sizing: border-box;">...</span>) fprintf(stdout, format, __VA_ARGS__)</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

其中,...表示参数可变,__VA_ARGS__在宏开展时会被实际的参数集所替换 
所以如果在实际写代码时,调用LOG(“log:message is %s,n=%d”,str,n),在预编译时会被fprintf(stdout,”log:message is %s,n=%d”,str,n)所替换,其中…表示可变参数str,n,在宏开展时_VA_ARGS_被str,n代替。 
(2)GCC中同时支持如下的形式

<code class="hljs lasso has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">#define</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">LOG</span>(format, args<span class="hljs-attribute" style="box-sizing: border-box;">...</span>) fprintf(stdout, format, args)</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

其用法和上面的基本一致,只是参数符号有变化 
(3)写定义可变参数宏时也可以不用写format,只在()中用…表示可变参数。比如:

<code class="hljs r has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#define printf_debug(...) printf_uart(__VA_ARGS__)</span>void printf_uart(const char *format, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>){    va_list arg;    char tmp[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">255</span>];    va_start(arg, format);     vsprintf(tmp, format, arg);     va_end(arg);     uart_write(DATA_PORT, tmp, strlen(tmp));}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>

当实际调用printf_debug(“debug:message is %s,n=%d”,str,n)时,在预处理时该语句会用printf_uart(debug:message is %s,n=%d”,str,n)替换。其中…表示debug:message is %s,n=%d”,str,n这三个参数,而__VA_ARGS__ 用debug:message is %s,n=%d”,str,n替换。 
另一个例子:

<code class="hljs r has-numbering" style="display: block; padding: 0px; background: transparent; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#define CORK_PARSE_ERROR               0x95dfd3c8</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#define CORK_REDEFINED                 0x171629cb</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#define cork_parse_error(...) \</span>    cork_error_set_printf(CORK_PARSE_ERROR, __VA_ARGS__)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">#define cork_redefined(...) \</span>    cork_error_set_printf(CORK_REDEFINED, __VA_ARGS__)voidcork_error_set_printf(cork_error code, const char *format, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>){    va_list arg;    char tmp[MAXLINE];    va_start(arg, format);     vsnprintf(tmp, MAXLINE, format, arg);        va_end(arg);     <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (code)        snprintf(buf + n, MAXLINE - n, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">": %s\n"</span>, strerror(errno_save));     fputs(tmp,stdout);   fflush(stdout);}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li></ul><div class="save_code tracking-ad" data-mod="popu_249" style="box-sizing: border-box; position: absolute; height: 60px; right: 30px; top: 5px; color: rgb(255, 255, 255); cursor: pointer; z-index: 2;"><a target=_blank target="_blank" style="box-sizing: border-box; color: rgb(12, 137, 207);"><img src="http://static.blog.csdn.net/images/save_snippets.png" style="border: none; box-sizing: border-box;" alt="" /></a></div><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right: 1px solid rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li></ul>

6. 引用

  1. 关于C/C++中可变参数的详细介绍(va_list,va_start,va_arg,va_end)
  2. va_list/va_start/va_arg/va_end深入分析
  3. 可变参数的宏定义
  4. 如何定义不定参数的宏

0 0