相关代码来自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;
}
}
}
}
答案 0 :(得分:2)
这是不可移植代码。它假设va_list
本质上只是一个参数数组,并对使用的调用约定做了许多危险的假设。
它只能用于调用每个参数在堆栈上传递的约定,即使这样也不能保证工作:例如,padding可用于对齐超宽参数。此外,不保证&format
甚至可以获得堆栈上参数列表的起始地址(&format
在函数启动时可能是本地临时变量)。
对于在寄存器中传递一些参数的调用约定(ARM EABI,x86 __fastcall,x86_64和其他几个ABI),这会完全破坏。
答案 1 :(得分:1)
不,这不是可移植代码,并且它不能在任何现代编译器上远程工作。它假定函数的参数到达类型char *[N]
的数组,其开头与局部变量format
重叠。这当然是错误的,但与某些编译器过去可能选择将format
定位在某些ABI (主要是i386)上的方式相匹配。< / p>
答案 2 :(得分:0)
此代码对参数传递给函数的顺序做出了某些假设。
特别是,它假定参数的地址是连续的。
它不可移植,因为一旦违反此假设,它就会中断。