从printf谈可变参数函数的实现
2008-08-09 19:25:55 来源:WEB开发网void fun1(int num, ...)
如果后续参数的类型是变化而且是未知的,则必须通过一个参数中设定模式来匹配后续参数的个数和类型,就像printf一样,当然我们可以定义自己的模式,如可以用i表示int参数,d表示double参数,为了简单,我们用一个字符表示一个参数,并由该字符的名称决定参数的类型而字符的出现的顺序也表示后续参数的顺序。 我们可以这样定义字符和参数类型的映射表,
{
double *p = (double *)((&num)+1);
double Param1 = *p;
double Param2 = *(p+1);
...
double Paramn *(p+num);
}i---int
"ild"模式用于表示后续有三个参数,按顺序分别为int, long, double类型的三个参数那么这样我们可以定义自己版本的printf 如下
s---signed short
l---long
c---charvoid printf(char *fmt, ...)
也可以这样定义我们的Max函数,它返回多个输入整型参数的最大值
{
char s[80] = "";
int paramCount = strlen(fmt);
write(stdout, "paramCount = " , strlen(paramCount = ));
itoa(paramCount,s,10);
write(stdout, s, strlen(s));
char *p = (char *)(&fmt) + sizeof(char *);
int *pi = (int *)p;
for (int i=0; i<paramCount; i++)
{
char line[80] = "";
strcpy(line, "param");
itoa(i+1, s, 10);
strcat(line, s);
strcat(line, "=");
switch(fmt[i])
{
case 'i':
case 's':
itoa((*pi),s,10);
strcat(line, s);
pi++;
break;
case 'c':
{
int len = strlen(line);
line[len] = (char)(*pi);
line[len+1] = '';
}
break;
case 'l':
ltoa((*(long *)pi),s,10);
strcat(line, s);
pi++;
break;
default:
break;
}
}
}int Max(int n, ...)
可以这样调用, 后续参数的个数由第一个参数指定
{
int *p = &n + 1;
int ret = *p;
for (int i=0; i<n; i++)
{
if (ret < *(p + i))
ret = *(p + i);
}
return ret;
}int m = Max(3, 45, 12, 56);
结论
int m = Max(1, 3);
int m = Max(2, 23, 45);
int first = 34, second = 45, third=5;
int m = Max(5, first, second, third, 100, 4);
对于可变参数函数的调用有一点需要注意,实际的可变参数的个数必须比前面模式指定的个数要多,或者不小于, 也即后续参数多一点不要紧,但不能少, 如果少了则会访问到函数参数以外的堆栈区域,这可能会把程序搞崩掉。前面模式的类型和后面实际参数的类型不匹配也有可能造成把程序搞崩溃,只要模式指定的数据长度大于后续参数长度,则这种情况就会发生。如:
printf("%.3f, %.3f, %.6e", 1, 2, 3, 4);
参数1,2,3,4的默认类型为整型,而模式指定的需要为double型,其数据长度比int大,这种情况就有可能访问函数参数堆栈以外的区域,从而造成危险。但是printf("%d, %d, %d", 1.0, 20., 3.0);这种情况虽然结果可能不正确,但是确不会造成灾难性后果。因为实际指定的参数长度比要求的参数长度长,堆栈不会越界。
更多精彩
赞助商链接