ungetc: количество байтов pushback

ungetc гарантированно принимает только один байт pushback. С другой стороны, я протестировал его на Windows и Linux, и он работает с двумя байтами.

Существуют ли какие-либо платформы (например, какие-либо текущие системы Unix), на которых он фактически занимает всего один байт?

3 ответа

Решение

Стандарт C99 (и стандарт C89 до этого) однозначно сказал:

Один символ отталкивания гарантирован. Если ungetc функция вызывается слишком много раз в одном и том же потоке без промежуточной операции чтения или позиционирования файла в этом потоке, операция может завершиться ошибкой.

Таким образом, чтобы быть переносимым, вы не предполагаете более одного символа откатывания назад.

При этом на MacOS X 10.7.2 (Lion) и RHEL 5 (Linux, x86/64) я попытался:

#include <stdio.h>
int main(void)
{
    int i;
    for (i = 0; i < 4096; i++)
    {
        int c = i % 16 + 64;
        if (ungetc(c, stdin) != c)
        {
            fprintf(stderr, "Error at count = %d\n", i);
            return(1);
        }
    }
    printf("No error up to count = %d\n", i-1);
    return(0);
}

Я не получил ошибки ни на одной платформе. Напротив, в Solaris 10 (SPARC) я получил сообщение об ошибке "count = 4". Хуже того, в HP-UX 11.00 (PA-RISC) и HP-UX 11.23 (Itanium) я получил ошибку при 'count = 1', опровергающую теорию о том, что 2 безопасно. Точно так же AIX 6.0 выдал ошибку при 'count = 1'.

Резюме

  • Linux: большой (4 КиБ)
  • MaxOS X: большой (4 КиБ)
  • Солярис: 4
  • HP-UX: 1
  • AIX: 1

Таким образом, AIX и HP-UX допускают только один символ возврата во входной файл, для которого не было прочитано никаких данных. Это неприятный случай; они могут обеспечить гораздо большую емкость отката после считывания некоторых данных из файла (но простой тест на AIX, добавляющий getchar() до того, как цикл не изменил емкость отжима).

Реализации, которые поддерживают 2 символа pushback, вероятно, делают это для того, чтобы scanf можешь использовать ungetc для его отдачи, а не требует второго почти идентичного механизма. Это означает, что для вас, как для программиста приложения, даже при вызове ungetc дважды, кажется, работает, это может быть не надежно во всех ситуациях - например, если последняя операция в потоке была fscanf и он должен был использовать pushback, вы можете только ungetc один персонаж

В любом случае, нельзя полагаться на наличие более чем одного символа ungetc Пушбэк, поэтому я настоятельно рекомендую не писать код, который нуждается в этом...

Здесь есть несколько постов, предполагающих, что имеет смысл поддерживать 2 символа ради scanf,

Я не думаю, что это правильно scanf нужен только один, и это действительно причина ограничения. Первоначальная реализация (еще в середине 70-х годов) поддерживала 100, и в руководстве было примечание: в будущем мы можем решить поддерживать только 1, так как это все, что нужно для scanf. См. Страницу 3 оригинального руководства (возможно, не оригинальное, но довольно старое.)

Чтобы более наглядно увидеть, что scanf требуется всего 1 символ, рассмотрим этот код для %u особенность scanf,

int c;
while isspace(c=getc()) {} // skip white space
unsigned num = 0;
while isdigit(c)
    num = num*10 + c-'0',
    c = getc();
ungetc(c);

Только один звонок ungetc() нужен здесь. Там нет причин, почему scanf нуждается в символе все для себя: он может поделиться с пользователем.

Другие вопросы по тегам