[英]How does this printf code work without using va_args?
有问题的代码来自grub 。 通常在printf实现中,您会看到stdarg
和va_start
, va_list
, va_end
和va_arg
,但是它们似乎在这里进行了一些转换。 我的猜测是,他们依靠某种奇怪的技巧来获取参数,并且(int *)
类似于va_arg(parameter, int)
。 这是可移植的代码吗? 它是如何工作的?
/* Format a string and print it on the screen, just like the libc
function printf. */
void
printf(const char *format, ...) {
char **arg = (char **) &format;
int c;
char buf[20];
arg++;
while ((c = *format++) != 0) {
if (c != '%')
putchar(c);
else {
char *p;
c = *format++;
switch (c) {
case 'd':
case 'u':
case 'x':
itoa(buf, c, *((int *) arg++));
p = buf;
goto string;
break;
case 's':
p = *arg++;
if (!p)
strcpy(p, "(null)");
string:
while (*p)
putchar(*p++);
break;
default:
putchar(*((int *) arg++));
break;
}
}
}
}
这不是可移植的代码。 它假定va_list
本质上只是一个参数数组,并且对所使用的调用约定进行了许多危险的假设。
它仅适用于在堆栈上传递每个参数的调用约定,即使这样也不能保证它起作用:例如,填充可用于对齐超宽参数。 此外,不能保证&format
甚至获得堆栈上参数列表的起始地址(在函数启动时, &format
可能是局部临时变量)。
对于在寄存器中传递某些参数的调用约定(ARM EABI,x86 __fastcall,x86_64和其他几个ABI),这将完全中断。
不,这不是可移植的代码,并且不能在任何现代编译器上远程工作。 它假定函数的参数到达类型为char *[N]
的数组,该数组的开头与局部变量format
重叠。 这当然是错误的,但是与某些编译器过去选择在某些ABI (主要是i386)上定位format
的方式相匹配。
该代码对参数传递给函数的顺序进行了某些假设。
特别是,它假定参数的地址是连续的。
它不是可移植的,因为一旦违反此假设,它将立即中断。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.