可变参数列表源码的剖析

先剖析printf函数

#include <stdio.h>

int
main(int argc, char const *argv[])
{
        printf("hello c\n"); 
        printf("%s\n""hello world\n""%d\n", argv[0], argc);

	return 0;
}

printf库函数接收的参数可以是多个,这里的多个是不缺定的数字,可能只有一个参数,或者两个甚至更多。但是最少有一个参数。

库函数printf的声明:


[cpp]  view plain  copy
  1. int printf (__const char *__restrict __format, ...);  

从声明中可以看出,printf中第一个参数是固定的(一个格式化的字符串常量),而后面的参数列表没有给定。printf库函数的实现运用了C语言中的一个特性,支持可变参数列表。

举例实现:

实现一个函数可以求任意个参数的平均值。

#include <stdio.h>
#include <stdarg.h>
int average(int n, ...)
{
va_list arg;
int i = 0;
int sum = 0;
va_start(arg, n);
for(i=0; i<n; i++)
{
sum += va_arg(arg, int);
}
return sum/n;
va_end(arg);
}
int main()
{
int a = 1;
int b = 2;
int c = 3;
int avg1 = average(2, a, c);
int avg2 = average(3, a, b, c);
printf("avg1 = %d\n", avg1);
printf("avg2 = %d\n", avg2);
return 0;
}

声明一个 va_list 类型的变量 arg ,它用于访问参数列表的未确定部分。这个变量是调用 va_start 来初始化的。它的第一个参数是 va_list 的变量名,第2个参数是省略号前最后一个有名字的参数。初始化过程把 arg 变量设置为指向可变参数部分的第一个参数。为了访问参数,需要使用 va_arg ,这个宏接受两个参数: va_list 变量和参数列表中下一个参数的类型。在这个例子中所有的可变参数都是整型。va_arg返回这个参数的值,并使用 va_arg指向下一个可变参数。最后,当访问完毕最后一个可变参数之后,我们需要调用 va_end 。

可变参数的限制
注意:
可变参数必须从头到尾逐个访问。如果你在访问了几个可变参数之后想半途终止,这是可以的,但是,如果你想一开始就访问参数列表中间的参数,那是不行的。参数列表中至少有一个命名参数。如果连一个命名参数都没有,就无法使用va_start。这些宏是无法直接判断实际存在参数的数量。这些宏无法判断每个参数的是类型。如果在va_arg中指定了错误的类型,那么其后果是不可预测的。

如果有兴趣我们可以研究一下可变参数实现的细节:
在VS的源码中我们可以得知
可变参数的实现过程是使用宏的封装。

printf函数的模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>  
#include <assert.h>  
#include <stdarg.h>  
#include <string.h>  
 
int m_printf(const char *str, ...)
{
char *eos = (char *)str;
int ret = 0;
 
va_list ap;
va_start(ap, str);
 
assert(str);
 
while (*eos)
{
if ('%' == *eos)
{
int d = 0;
char c = 0;
char *s = NULL;
 
eos++;
 
switch (*eos)
{
case 's':
s = va_arg(ap, char*);
while (*s)
{
putchar(*s);
s++;
ret++;
}
break;
case 'c':
//  c = va_arg(ap, char);  
//  printf("%c", c);  
putchar(va_arg(ap, char));
ret++;
break;
case 'd':
d = va_arg(ap, int);
printf("%d", d);
while (d)
{
ret++;
d /= 10;
}
break;
case '\0':
putchar('\0');
default:
;
break;
}
}
else
{
putchar(*eos);
ret++;
}
 
eos++;
}
 
va_end(ap);
 
return (ret);
}
 
int main()
{
char str1[] = "Hello";
char str2[] = "bit";
int num = 200;
 
int ret = m_printf("第%d行 : %s %c%c%c\n", 100, "hello", 'b', 'i', 't');
m_printf("ret = %d\n", ret);
system("pause");
return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值