繁体   English   中英

不使用va_args,此printf代码如何工作?

[英]How does this printf code work without using va_args?

有问题的代码来自grub 通常在printf实现中,您会看到stdargva_startva_listva_endva_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM