在“最直观的--函数可变参数数量的实现讲解(一)”中,我们站在上帝视角,实现了int printf_test (const char *__format, ...),现在我们接着玩。。。
打开printf_test和printf两个函数,他们的对应关系,大概是这个样子:
那么,运行程序的机器没有上帝的视角,怎么知道我们传递的参数个数和类型呢?
其实,这也是我们设计好的:
int printf (const char *__format, ...)中,第一个参数__format是固定的,要使用字符串指针(const char *),机器通过我们这个字符串指针指向的字符串内容,一步步解析,推测我们后面传递的参数。
例如printf ("%s, %2d, Hello, World, %d!", __func__, __LINE__, data);
1.我们传递的字串内容是"%s, %2d, Hello, World, %d!";
2.机器先检查到%s,就知道,我们第二个参数,要传递字符串,我这里传了函数名进去;
3.然后机器继续解析,遇到%2d,机器就知道,我们第三个参数要传整形,我这里传来行号进去;
4.机器再解析,遇到%d,同样知道我们最后一个参数也是整形,我这里传data的值进去了;
5.字符串"%s, %2d, Hello, World, %d!"解析完毕,printf的工作也就基本完成了。
这个实现过程蛮复杂的,我就不操作了。。。。
接下来,我们看头文件<stdarg.h>中的3个宏va_start、va_arg和va_end
va_start、va_arg和va_end三个宏在项目中比较实用的。现在,我们用他们实现printf_test功能:
#include <stdio.h>
#include <stdarg.h>
static int printf_test (const char *__format, ...)
{
va_list p_args; // 声明va_list(const char *)变量
va_start(p_args, __format); // __format一定是...前的第一个参数
const char * func = va_arg(p_args, const char *); // 得到__func__值
int line = va_arg(p_args, int); // 得到__LINE__值
int data = va_arg(p_args, int); // 得到data值
va_end(p_args); // 结束
printf("\n%s, %d\n", __func__, __LINE__);
printf("%s, %s, %d, %d", __format, func, line, data);
printf("\n\n");
return 0;
}
int main (void * arg)
{
int data = 1;
printf ("%s, %2d, Hello, World, %d!", __func__, __LINE__, data);
printf ("\n");
printf_test ("%s, %2d, Hello, World, %d!", __func__, __LINE__, data);
printf ("\n");
return 0;
}
运行结果:
main, 23, Hello, World, 1!
printf_test, 13
%s, %2d, Hello, World, %d!, main, 25, 1
接下来,我们对printf_test函数继续改进,先介绍一下vprintf函数
原型:int vprintf (const char *__format, __builtin_va_list __local_argv)
它是printf的双胞胎函数,里面用__builtin_va_list替换了…
#include <stdio.h>
#include <stdarg.h>
static int printf_test (const char *__format, ...)
{
va_list p_args; // 声明va_list(const char *)变量
va_start(p_args, __format); // __format一定是...前的第一个参数
printf("\n");
vprintf(__format, p_args);
va_end(p_args);
printf("\n");
return 0;
}
int main (void * arg)
{
int data = 1;
printf ("%s, %2d, Hello, World, %d!", __func__, __LINE__, data);
printf ("\n");
printf_test ("%s, %2d, Hello, World, %d!", __func__, __LINE__, data);
printf ("\n");
return 0;
}
运行结果
main, 19, Hello, World, 1!
main, 21, Hello, World, 1!
这样,printf_test就实现了真正意义上的可变参数数量;