如何实现函数来处理可变参数
来源:互联网 发布:新日铁住金软件 薪资 编辑:程序博客网 时间: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_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>
这里format
、ap
、 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. 引用
- 关于C/C++中可变参数的详细介绍(va_list,va_start,va_arg,va_end)
- va_list/va_start/va_arg/va_end深入分析
- 可变参数的宏定义
- 如何定义不定参数的宏
- 如何实现函数来处理可变参数
- 如何实现函数来处理可变参数
- 如何实现可变参数函数
- 如何实现可变参数函数
- 如何实现可变参数函数
- 如何实现可变参数函数
- 如何实现参数个数可变的函数
- JavaScript中如何实现函数参数可变
- 如何实现参数个数可变的函数
- 函数可变参数处理
- 可变参数函数实现
- 可变参数函数实现
- Matlab中使用varargin来实现参数可变的函数
- Matlab中使用varargin来实现参数可变的函数
- Matlab中使用varargin来实现参数可变的函数
- Matlab中使用varargin来实现参数可变的函数
- Matlab中使用varargin来实现参数可变的函数
- JavaScript如何处理可变参数
- Eclipse常用快捷键
- [kuangbin带你飞]专题六 最小生成树 A POJ 1251
- 学习vitamio
- Android TextView ClickableSpan 分段点击实现
- 20 个最棒的 JavaScript 图表库
- 如何实现函数来处理可变参数
- 第二本第七章上机1北大青鸟网站导航
- HTML5+开发移动app教程2-原理介绍
- 每周一书-《鸟哥的Linux私房菜基础学习篇(第四版)》台湾原版,你想要吗?
- wireshark抓rtmp协议包
- Delphi TreeView 基础与技巧(详解)
- 浅谈CSRF攻击方式
- Kali Linux Web 渗透测试秘籍 翻译完成!
- AndroidStudio报错AAPT: libpng error: Not a PNG file