0
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
void minprintf(char *fmt, ...)
{
    va_list ap;
    char *p, *sval;
    int ival;
    double dval;

    va_start(ap, fmt);
    for (p = fmt; *p; p++) {
        if (*p != '%') {
            putchar(*p);
            continue;
        }
        switch (*p++) {
        case 'd':
            ival = va_arg(ap, int);
            printf("%d", ival);
            break;
        case 'f':
            dval = va_arg(ap, double);
            printf("%f", dval);
            break;
        case 's':
            for (sval = va_arg(ap, char *); *sval; sval++)
                putchar(*sval);
            break;
        default:
            putchar(*p);
            break;
        }
    }
    va_end(ap);
}

int main(void)
{
    minprintf("aaaaaaa%\0dddd");
    return 0;
}

此代码来自 C 编程语言第二版 7.3 Variable-length Argument Lists

通常这个程序应该输出 aaaaaaa 并停止,但它会打印 aaaaaaa dddd。 http://ideone.com/d3Akk

真的是bug吗。

谢谢你。

4

3 回答 3

2

问题是,如果它前面是%(在 switch 语句中),则忽略空终止符。这可能是也可能不是错误,但肯定是 C 函数的非标准行为。但是,在您的情况下,它不会导致任何未定义的行为,并且几乎可以做到它所说的。

于 2012-01-02T19:05:02.497 回答
1

用“aaa%”这样的格式字符串调用的函数会导致UB,打破了最小意外原则。这是我书中的一个错误。

于 2012-01-02T19:19:44.780 回答
1

您的问题是,由于您希望它在第一个 NULL 处停止的for条件*p,但它没有?

所以你的问题是:“为什么它不在第一个 NULL 处停止?”。答:因为switch()语句中的后增量。它首先评估开关块,然后增加指针。因此,在您的特定情况下会发生什么,当函数看到百分号时,它会进入 switch 语句。因为 NULL 不是有效的格式说明符,所以 switch 块默认输出它。然后,由于后增量,指针向前移动了一个字符,即d. 因此,计算*pd,它不为 0,因此 for 循环中的条件被定义为真。

编辑:那里有一个错误,IMO,但实际上不是这个:错误的格式说明符被默认构造默默地丢弃是事实。此外,如果您执行类似minprintf("whoopsie%");for 循环将尝试遍历字符串末尾的操作,则可能存在边缘情况!

于 2012-01-02T19:20:21.503 回答