可变参数列别解析
前言:很多朋友都不清楚c语言中关于函数还有一个很有意思的东西——“可变参数列表”。那么本主今天就借助模拟实现printf()
函数的例子给大家说道说道关于“函数可变参数列表”的事情!
例码:print()函数
#include<stdio.h>
#include<stdarg.h>
void display(int n)//打印n的每一位,例如:n = 123 将打印出 1 2 3
{
if (n > 9)
{
display(n/10);
}
putchar((n % 10) + '0');
}
void print(char *format, ...)
{
va_list arg;
va_start(arg, format);
char tmp;
while ((tmp = *format ) != '\0')
{
format++;
switch (tmp)
{
case 's'://如果字符串内字符为 'S',将执行字符串打印工作
{
char *ret = va_arg(arg, char *);
while (*ret != '\0')//字符串打印工作
{
putchar(*ret);
ret++;
}
}
break;
case 'c'://如果字符串内字符为 'C',将执行字符打印工作
{
char ret = va_arg(arg, char);
putchar(ret);//字符打印工作
}
break;
case 'd'://如果字符串内字符为 'd',将执行整形数值(十进制)打印工作
{
int ret = va_arg(arg, int);
display(ret); //执行整形数值(十进制)打印工作
}
break;
default:
putchar(tmp);//否则,原样输出
break;
}
}
}
int main()
{
print("s ccc d%.\n", "hello", 'b', 'i', 't',100);
return 0;
}
说明:
- 我们必须要清楚函数调用时形参实例化的过程,参数是“从右向左”依次入栈的。如下图:
void print(char *format, ...)
参数列表后面的“ …”表示参数个数不固定。- 可变参数必须从头到尾依次访问,不可跳转访问,当然,如果你在访问几个参数后不想继续访问后边的参数也是可以的。
- 可变参数列表前面必须至少有一个命名参数,否则,将不能使用
va_start
进行初始化。- 函数实现中使用的
va_*
系列宏无法直接判断可变参数个数、每个参数类型。- 在使用
va_arg
时,如果指定了错误的类型,那么后果将非常严重“一步失足,满盘皆输”。
正文:
调用
print("s ccc d%.\n", "hello", 'b', 'i', 't',100);
参数实例化过程如下图:
详解:
_INTSIZEOF(n)
详解:
va_*
系列宏
详解:
print()函数
总结:
- 函数的可变参数列表的实现是借助参数“命名参数部分”的最后一个命名参数的地址偏移进而找到“可变参数部分第一个参数的地址”进而往后访问其它可变参数部分。