Содержит ли этот код "Язык программирования C 2nd Edition" ошибку? - PullRequest
0 голосов
/ 02 января 2012
#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. Списки аргументов переменной длины

Обычно эта программа должна выводить aaaaaaa и останавливаться, но вместо этого печатает aaaaaaa dddd.http://ideone.com/d3Akk

Это действительно ошибка.

Спасибо.

Ответы [ 3 ]

2 голосов
/ 02 января 2012

Дело в том, что вы игнорируете нулевой терминатор, если ему предшествует % (в операторе switch).Это может быть или не быть ошибкой, но это, безусловно, нестандартное поведение для функций Си.Однако, в вашем случае, это не вызывает неопределенного поведения и в значительной степени делает то, что говорит.

1 голос
/ 02 января 2012

Ваша проблема в том, что из-за условия for *p вы ожидаете, что он остановится на первом NULL, только это не так?

Итак, ваш вопрос: «почему он не останавливается на первом NULL?». Ответ: из-за постинкремента в операторе switch(). Сначала он оценивает блок переключателей, затем увеличивает указатель. Так что в вашем конкретном случае происходит то, что когда функция видит знак процента, она попадает в оператор switch. Поскольку NULL не является допустимым спецификатором формата, блок коммутатора по умолчанию выводит его. Затем из-за постинкремента указатель перемещается на один символ вперед, что составляет d. Следовательно, *p работает как d, что не равно 0, поэтому условие в цикле for определено как истинное.

РЕДАКТИРОВАТЬ: Там есть ошибка, IMO, но это на самом деле не эта: это факт, что неправильные спецификаторы формата молча отбрасываются конструкцией по умолчанию. Кроме того, может быть крайний случай, если вы сделаете что-то вроде minprintf("whoopsie%");, где цикл for будет пытаться выполнить итерации за концом строки!

1 голос
/ 02 января 2012

Функция, вызываемая со строкой формата, такой как «aaa%», вызовет UB, нарушая принцип наименьшего удивления.Это ошибка в моей книге.

...