Как индикатор ошибки потока влияет на следующий код ввода?

Каждый поток имеет «индикатор ошибки, который записывает, произошла ли ошибка чтения / записи».

Устанавливается, как правило, редко, различными функциями: fgetc(), fflush(), fseek(), ....

Очищается различными функциями: rewind(), clearerr(), fopen(), ....

int ferror(FILE *stream) сообщает о состоянии.

The ferror function returns nonzero if and only if the error indicator is set for stream.


В этом случае обязательно только что произошла ошибка ввода.

if (!ferror(istream)) {
  int ch = fgetc(istream);
  if (ch == EOF && ferror(istream)) {
    puts("Input error just occurred");
  }
}

Изучая fgetc() глубже, fgetc() возвращает EOF не потому, что был установлен индикатор ошибки, а потому, что «Если возникает ошибка чтения» или причины, связанные с концом файла, 1. Обычно, когда возникает ошибка (например, ошибка четности в последовательном потоке), код не продолжает чтение, не очистив ошибку, но подумайте, что произойдет, когда он продолжится.

Я вижу 8 ситуаций: индикатор ошибки устанавливается / очищается до fgetc(), fgetc() возвращает EOF или нет, а следующий ferror() может быть верным или нет.

int e1 = !!ferror(istream);
int eof = fgetc(istream) == EOF;
int e2 = !!ferror(istream);

Предполагая, что UB нет, 5 из 8 возможных, а не 3 неожиданных? особенно допустимый ввод возможен с установленным индикатором ошибки?2

e1 eof e2  
0  0   0   Normal reading of valid data    
0  0   1   Unexpected
0  1   0   End-of-file
0  1   1   Input error
1  0   0   Unexpected
1  0   1   Normal reading of valid data with error indicator set!
1  1   0   Unexpected
1  1   1   Input error or end-of-file

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

Если коды не очищают индикатор ошибки заранее и хотят определить, имеет ли вход линия редкую ошибку ввода, кажется, имеет смысл протестировать !feof(), а не ferror() для обнаружения.

Может ли проверка ferror() ввести в заблуждение? или я что-то пропустил по индикатор ошибки?

char buf[80];
for (int i=0; i<(80-1); i++) {
  int ch = fgetc(stdin);
  if (ch == EOF) {
    if (ferror(stdin)) {
      puts("Input error or (Prior input error and end of file occurred)");  // ambiguous
    } 
    if (feof(stdin)) { 
      puts("End of file occurred");
    } else {
      puts("Input error occurred");  // ferror() test not needed
      i = 0; // ignore prior input
    }
    break;
  }
  if (ch == '\n') break;
  buf[i++] = ch; 
}
buf[i] = 0; 

Похожие вопросы

Файловые операции с установленным индикатором ошибки . Этот безответный фокус сосредоточен на накоплении индикатор ошибки без тестирования возвращаемого значения fgetc() (ответы уходят в errno и выставляют флаг ошибки пользователя), а этот пытается просто устранить неоднозначность fgetc().

fgetc (): Достаточно ли просто проверить EOF? не адресует индикатор ошибки, установленный до fgetc().

Аналогичные проблемы применимы к потокам вывода и ввода-вывода, но в этом вопросе основное внимание уделяется потокам ввода.


1int fgetc(FILE *stream) Возврат

If the end-of-file indicator for the stream is set, or if the stream is at end-of-file, the end-of-file indicator for the stream is set and the fgetc function returns EOF. Otherwise, the fgetc function returns the next character from the input stream pointed to by stream. If a read error occurs, the error indicator for the stream is set and the fgetc function returns EOF. C11dr §7.21.7.1 2

2 По случаям 0-1-0, 1-1-1. Похоже, что UCHAR_MAX == UINT_MAX, unsigned char, можно было бы вернуть и приравнять к EOF, а не из-за ошибки конца файла или ввода.

Вы ищете основанный на стандартах ответ (возможно, языковед) или практический ответ?

Nominal Animal 13.11.2018 03:08

@NominalAnimal Ближе к стандартам, чем только на практике, поскольку я пытаюсь разгадать загадки индикатор ошибки, которые нужно решить: когда fgetc() возвращает EOF, это связано с недавней ошибкой, концом файла, каким-то широким unsigned char или что-то другое? LL добавил.

chux - Reinstate Monica 13.11.2018 03:16

FWIW, в glibc это всегда происходит либо из-за обнаружения нового конца файла, либо из-за ошибки, поскольку функции fgetc() / getc() / getchar() никогда не проверяют флаг ошибки.

Nominal Animal 13.11.2018 03:30

@NominalAnimal Возвращает ли glibc также EOF для существующего флага конца файла, а не только для нового конца файла?

chux - Reinstate Monica 13.11.2018 03:35

Нет, он не проверяет наличие флага конца файла. Он попытается прочитать больше из потока. (Это также означает, что даже если ferror() или feof() вернет ненулевое значение, последний вызов fgetc() / getc() / getchar() не обязательно завершился ошибкой; условие могло возникнуть раньше.)

Nominal Animal 13.11.2018 04:56

@NominalAnimal Хммм, "не проверяет существующий флаг конца файла" выглядит как несоответствие C11 §7.21.7.1 2. По возможности, пожалуйста, дайте ссылку на ссылку или образец исходного кода для поддержки этого отсутствия проверки.

chux - Reinstate Monica 14.11.2018 12:10

glibc fgetc() реализован в libio / getc.c: _IO_getc (). Единственная выполняемая проверка - это CHECK_FILE(handle), реализованный в libio / libioP.h как макрос, и обычно оптимизируется. _IO_getc_unlocked () расширяется в libio / libio.h на libio / биты / типы / struct_FILE.h: __ getc_unlocked_body () ...

Nominal Animal 14.11.2018 16:13

который является либо прямым поиском в кэше, либо вызовом libio / genops.c: __ uflow (). _IO_UFLOW () разрешает переход к __uflow (). Чеков там нигде не вижу.

Nominal Animal 14.11.2018 16:17

Что касается EOF, скомпилируйте и запустите следующее: #include <stdio.h>int main(void) { int c; do { c = fgetc(stdin); if (c == EOF) printf("EOF\n"); } while (c != '.'); return 0; }. Полная остановка . завершает программу. При нажатии Ctrl + D в начале строки печатается строка EOF. Вы можете нажать ее несколько раз: показывает, что EOF не кэшируется (конечно, кроме feof(): он запоминает, был ли EOF замечен). Я проверю, вызывает ли EINTR функцию ferror (), и если да, то добавлю сюда «ответ» с полной программой репликации.

Nominal Animal 14.11.2018 16:24

Ага; проверено в Linux на AMD64 / x86-64 с использованием встроенной библиотеки GNU C версии 2.23-0ubuntu10 в Ubuntu 16.04.4 LTS. Программа проверки размещена как "ответ".

Nominal Animal 14.11.2018 17:07

Поведение glibc fgetc было изменено в версии 2.28, см. sourceware.org/ml/libc-alpha/2018-08/msg00003.html и sourceware.org/bugzilla/show_bug.cgi?id=1190. Мы наверняка думали, что 7.21.7p2,3 требует «липкого EOF» - изменение описывается как «исправление давней ошибки соответствия C99». (Однако индикатор EOF и индикатор ошибки - это два отдельных бита.)

zwol 14.11.2018 18:35

@zwol Я исследовал пример кода @ Номинальное животное и ответил ниже. IMO, функциональность 1) кажется несоответствующей 2) При вводе с клавиатуры допустимых данных после ^ d, fgetc()очищает флаг конца файла! 3) Все еще изучаю.

chux - Reinstate Monica 14.11.2018 18:42
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
12
775
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Все, что вы говорите, кажется правильным, и ch==EOF && !feof(f) - это правильный способ проверить наличие новых ошибок, не мешая накоплению ошибок.

Ответ принят как подходящий

Assuming no UB, are 5 of the 8 possible and not the 3 unexpected ones? especially is valid input possible with error indicator set?

Говоря конкретно о положениях стандарта, я склонен согласиться с вашим анализом:

  • Для очистки индикатора ошибки потока указано несколько функций, и fgetc() не входит в их число.. В общем, ни одна из них не является функциями передачи данных. Следовательно, если индикатор ошибки установлен для потока до того, как этот поток будет представлен в fgetc() для чтения, он все равно должен быть установлен, когда эта функция вернется, несмотря на все другие соображения. Это касается следующих случаев: *

    1  0   0   Unexpected
    1  1   0   Unexpected
    1  1   1   Input error or end-of-file
    

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

    1  0   1   Normal reading of valid data with error indicator set!
    
  • fgetc() указан для возврата EOF в каждой ситуации, в которой он указан для установки индикатора конца файла в потоке.. Следовательно, если fgetc() возвращает что-либо, кроме EOF, то при этом вызове он не будет устанавливать индикатор ошибки потока (или конца файла). Это касается следующих случаев:

    0  0   0   Normal reading of valid data    
    0  0   1   Unexpected
    

    С другой стороны, если fgetc()делает возвращает EOF, то впоследствии должен быть установлен либо индикатор конца файла потока, либо его индикатор ошибки. Но стандарт различает эти случаи и указывает, что пользователь может различать их с помощью функций feof() и ferror(). Это касается следующих случаев: *

    0  1   0   End-of-file
    0  1   1   Input error
    
  • Наконец, я согласен с тем, что ни одно поведение fgetc() не зависит от начального состояния индикатора ошибки потока. При условии, что поток изначально не позиционируется в конце и его индикатор конца файла изначально не установлен, «функция fgetc возвращает следующий символ из входного потока, на который указывает поток». Это устанавливает, что это, наиболее интересный случай, на самом деле разрешено:

    1  0   1   Normal reading of valid data with error indicator set!
    

    тем не мение, что случай разрешен в аннотации, не означает, что его можно наблюдать на практике. Детали кажутся неопределенными, и я ожидал бы, что они будут зависеть от реализации драйвера, обслуживающего рассматриваемый поток. Вполне возможно, что, столкнувшись с ошибкой, драйвер продолжит сообщать об ошибке при последующих чтениях до тех пор, пока не будет сброшен соответствующим образом, а возможно и дольше. С точки зрения C, это будет интерпретироваться как (дополнительная) ошибка, возникающая при каждом последующем чтении, и ничто в спецификациях языка не предотвращает этого. Даже не использовать одну из функций, очищающих индикатор ошибки потока.

If codes does not clear the error indicator before hand and wants to detect if a line of input had a rare input error, it seems to make sense to test !feof() and not ferror() to detect.

Is checking ferror() potentially misleading? or have I missed something about the error indicator?

Я согласен с тем, что если изначально установлен индикатор ошибки потока, а индикатор конца файла - нет, и чтение его с помощью fgetc() возвращает EOF, тогда ferror() не проводит различий между случаями конца файла и ошибками, тогда как feof() должен.

С другой стороны, можно ли продолжать чтение данного потока после того, как в нем обнаружена ошибка, зависит от реализации и, возможно, от конкретных обстоятельств. Это применимо, даже если индикатор ошибки сброшен с помощью вызова clearerr(), не говоря уже о том, очищен ли индикатор ошибки нет.


* Хотя я согласен с тем, что существует двусмысленность в отношении EOF в случае, если это UCHAR_MAX > INT_MAX, я утверждаю, что это лишь одна из нескольких причин, по которым такая реализация будет проблематичной. Поэтому с практической точки зрения я не считаю такие реализации полностью гипотетическими.

Дополнительный интерес к «продолжению полезного чтения данного потока после обнаружения ошибки» возникает из проверки функций ввода на надежность. Пример: my_getline (). Такие функции при вызове могут иметь или не иметь установленный индикатор ошибки. Предотвращение вызова при обнаружении предыдущей ошибки находится вне контроля функции - точно так же, как fgetc(). Обнаружение ошибки ввода, кажется, лучше полагается на c == EOF && !feof(stdin), чем на c == EOF && ferror(...), поэтому я искал другие хорошие идеи, касающиеся ошибка ввода.

chux - Reinstate Monica 14.11.2018 12:33

@chux, я согласен с тем, что, выполнив int c = fgetc(s); в соответствующей реализации C, без знания начального состояния потока s, проверка ошибки при чтении через c == EOF && !feof(s) является допустимой и более надежной, чем ее аналог, включающий ferror. Если вместо этого будет использоваться ferror, то сначала необходимо убедиться, что индикатор ошибки потока сброшен. Мне неизвестны альтернативы, принципиально отличные от этих.

John Bollinger 14.11.2018 15:14

Вот очень грубая, очень минимальная программа для изучения поведения библиотеки GNU C по отношению к fgetc(), ferror() и feof(), как запрошено OP в комментарии:

#define  _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>

static volatile sig_atomic_t  interrupted = 0;

static void interrupt_handler(int signum)
{
    interrupted = 1;
}

static int install_interrupt(const int signum)
{
    struct sigaction  act;

    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_handler = interrupt_handler;
    act.sa_flags = 0;
    if (sigaction(signum, &act, NULL) == -1)
        return -1;

    return 0;
}

int main(void)
{
    int  n, c;

    if (install_interrupt(SIGALRM)) {
        fprintf(stderr, "Cannot install SIGALRM handler: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    if (ferror(stdin)) {
        fprintf(stderr, "Standard input is already in error state.\n");
        return EXIT_FAILURE;
    }
    if (feof(stdin)) {
        fprintf(stderr, "Standard input is already in end-of-input state.\n");
        return EXIT_FAILURE;
    }

    fprintf(stderr, "Testing stream error state. Please wait.\n");

    alarm(1);
    c = fgetc(stdin);
    if (c != EOF) {
        fprintf(stderr, "Stream error state test failed.\n");
        return EXIT_FAILURE;
    }

    fprintf(stderr, "fgetc(stdin) returned EOF.\n");
    fprintf(stderr, "ferror(stdin) returns %d.\n", ferror(stdin));
    fprintf(stderr, "feof(stdin) returns %d.\n", feof(stdin));

    fprintf(stderr, "\n");
    fprintf(stderr, "Testing stream end-of-input state. Please press Ctrl+D.\n");
    c = fgetc(stdin);
    if (c != EOF) {
        fprintf(stderr, "fgetc() returned %d; EOF was expected.\n", c);
        return EXIT_FAILURE;
    }
    fprintf(stderr, "fgetc(stdin) returned EOF.\n");
    fprintf(stderr, "ferror(stdin) returns %d.\n", ferror(stdin));
    fprintf(stderr, "feof(stdin) returns %d.\n", feof(stdin));
    if (!ferror(stdin) || !feof(stdin)) {
        fprintf(stderr, "Expected error and end-of-file states; aborting.\n");
        return EXIT_FAILURE;
    }

    fprintf(stderr, "\n");
    fprintf(stderr, "Testing fgetc() when stream in error and end-of-file state.\n");
    fprintf(stderr, "Please type something, then press Enter.\n");
    n = 0;
    c = fgetc(stdin);
    while (c != EOF && c != '\n') {
        n++;
        c = fgetc(stdin);
    }
    if (c == EOF) {
        fprintf(stderr, "Further input is not possible.\n");
        return EXIT_FAILURE;
    } else
        fprintf(stderr, "Further input is possible: %d characters (including Enter) read.\n", n + 1);

    return EXIT_SUCCESS;
}

Когда я компилирую и запускаю вышеуказанное в Linux, программа выведет

Testing stream error state. Please wait.
fgetc(stdin) returned EOF.
ferror(stdin) returns 1.
feof(stdin) returns 0.

Состояние ошибки было вызвано прерыванием доставки сигнала при вызове fgetc(stdin). Как видите, ferror(stdin) действительно возвращает ненулевое значение. Обратите внимание, что feof(stdin) возвращает 0.

Вывод продолжается:

Testing stream end-of-input state. Please press Ctrl+D.

Нажатие Ctrl + C дает результат

fgetc(stdin) returned EOF.
ferror(stdin) returns 1.
feof(stdin) returns 1.

На этом этапе стандартный ввод находится как в состоянии ошибки, так и в состоянии конца файла. Вывод продолжается:

Testing fgetc() when stream in error and end-of-file state.
Please type something, then press Enter.

Если теперь ввести, скажем, OKEnter, мы получим

Further input is possible: 3 characters (including Enter) read.

Это доказывает, что по крайней мере реализация библиотеки GNU C вообще не проверяет ошибку потока или состояние конца файла. Он просто попытается прочитать больше данных (используя базовую операцию read() в системах POSIXy).

Я также нашел похожие результаты, но склонен думать, что это не соответствует стандарту. (IOWs, ошибка в этой версии). Я также заметил, что флаг конца файла был сброшен вызовом fgetc() с 5 клавишами "x\n", Ctrl d, последовательность ".\n" с ключом '.'.

chux - Reinstate Monica 14.11.2018 19:57

@chux: В системах POSIXy поведение EOF имеет смысл, потому что если файл будет добавлен после того, как мы прочитаем его до конца, мы захотим получить добавленные данные; и поскольку нет функции cleareof(), повторное открытие файла было бы единственным способом сделать это в противном случае. Соответствует ли что-либо из этого стандартам, у меня нет мнения: я решил держаться подальше от language-lawyer. (Если это ошибка в glibc, ее исправление - это донкихотская борьба против людей, которые считают, что знают стандарты лучше, чем кто-либо другой. Не стоит усилий, ИМО.)

Nominal Animal 14.11.2018 20:09

Re: "cleareof() нет". Разве clearerr(istream) не будет достаточно вместо повторного открытия файла? IAC, спасибо за изучение углов C I / O.

chux - Reinstate Monica 14.11.2018 20:14

@NominalAnimal Для записи: если вы считаете, что нашли ошибку в glibc, и не хотите спорить об этом с Джозефом и / или Полом Эггертом, вы можете написать мне по электронной почте, и я сделаю это. Ульрих больше не участвует.

zwol 14.11.2018 20:27

@zwol: Да, libio / getc.c: _IO_getc () и libio / getc_u.c: __ getc_unlocked () по-прежнему не имеют проверки _IO_ferror_unlocked() в HEAD. Я бы рекомендовал превратить _IO_getc_unlocked() в функцию, которая выполняет проверку и возвращает __getc_unlocked_body(). Также RFC для разработчиков: возможно, измените _IO_EOF_SEEN в libio / bits / types / struct_FILE.h на 0x0030, чтобы feof() возвращал ненулевое значение, если eof или ошибка.

Nominal Animal 14.11.2018 21:58

@NominalAnimal Спасибо. Я раскрою это подробнее на выходных. Как указывает Джон Боллинджер, для стандарта не требуется липкий индикатор ошибка, а только липкий индикатор EOF. Думаю, _IO_EOF_SEEN нельзя изменить, не нарушив ABI, но, вероятно, есть еще кое-что, что мы могли бы сделать с тем же эффектом.

zwol 14.11.2018 22:23

@zwol: Не беспокойся, это было всего лишь предложение. ИМХО стандарт тогда басовый: нет практической необходимости, чтобы EOF был липким, но если ошибка не липкий, тогда есть реальный риск случайно пропущенных ошибок. Мне так хотелось бы, чтобы среди разработчиков стандарта C было больше инженеров-программистов и меньше ученых-программистов (и представителей компаний); чтобы преодолеть разрыв между теорией и практикой.

Nominal Animal 15.11.2018 01:39

Я так понимаю стандарт, что он явно не говорит, что fgetc разрешено возвращать значение, отличное от EOF, если индикатор ошибки уже был установлен в потоке при входе, но он также не говорит явно, что это не могу. Я сочувствую наблюдению Nominal Animal (которое я извлечу из комментариев к его ответу на случай, если он будет удален или перемещен в чат; позвольте мне на мгновение потрогать свой личный топор и заметить, что политика обработки комментариев как "эфемерных" вредна и должны быть отменены):

IMHO the standard is then bass-ackwards: there is no practical need for EOF to be sticky, but if error is not sticky, then there is a real risk of accidentally missing errors.

Однако, если все существующие реализации последовательно не рассматривают ошибку как постоянную, изменить поведение будет очень сложно продать комитету. Поэтому прошу у сообщества тесты:

Ниже представлена ​​сокращенная неинтерактивная версия Программа испытаний номинального животного. Он смотрит только на поведение fgetc после ошибки чтения, но не на EOF. Он использует SIGALRM для прерывания чтения вместо Ctrl-C, поэтому вам не нужно ничего делать, кроме как запустить его.

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>

static _Noreturn void
perror_exit (const char *msg)
{
  perror (msg);
  exit (1);
}

static void
handler (int unused)
{
}

int
main (void)
{
  struct sigaction sa;
  int pipefd[2];
  FILE *fp;
  int ch, pa;

  setvbuf (stdout, 0, _IOLBF, 0);

  sa.sa_handler = handler;
  sa.sa_flags = 0; /* DO interrupt blocking system calls */
  sigemptyset (&sa.sa_mask);
  if (sigaction (SIGALRM, &sa, 0))
    perror_exit ("sigaction");

  if (pipe (pipefd))
    perror_exit ("pipe");

  fp = fdopen (pipefd[0], "r");
  if (!fp)
    perror_exit ("fdopen");

  printf ("before fgetc 1, feof = %d ferror = %d\n",
          feof (fp), ferror (fp));

  alarm (1);
  ch = fgetc (fp);

  if (ch == EOF)
    printf ("after fgetc 1, ch = EOF feof = %d ferror = %d\n",
            feof (fp), ferror (fp));
  else
    printf ("after fgetc 1, ch = '%c' feof = %d ferror = %d\n",
            ch, feof (fp), ferror (fp));

  write (pipefd[1], "x", 1);
  alarm (1);
  ch = fgetc (fp);
  pa = alarm (0);

  printf ("after fgetc 2, alarm %s\n",
          pa ? "did not fire" : "fired");

  if (ch == EOF)
    printf ("after fgetc 2, ch = EOF feof = %d ferror = %d\n",
            feof (fp), ferror (fp));
  else
    printf ("after fgetc 2, ch = '%c' feof = %d ferror = %d\n",
            ch, feof (fp), ferror (fp));

  return 0;
}

На всех Unix, которые я могу получить на данный момент, результат этой программы согласуется с наблюдением Джона Боллинджера о том, что

the case of most interest, is in fact allowed:

1  0   1   Normal reading of valid data with error indicator set!

Я особенно хотел бы знать, что эта программа печатает при запуске в альтернативных библиотеках C на базе Linux (например, musl, bionic); Unix-системы, которые не относятся к Linux и не относятся к типу BSD; и Windows. Если у вас есть что-то еще более экзотическое, попробуйте и это. Я отмечаю это сообщение вики сообщества; пожалуйста, отредактируйте его, чтобы добавить результаты теста.

Программа тестирования должна быть приемлемой для любого C89-совместимого компилятора для среды, в которой существует unistd.h, а signal.h определяет sigaction, за исключением одного использования ключевого слова C11 _Noreturn, которое предназначено только для подавления предупреждений. Если ваш компилятор жалуется на _Noreturn, скомпилируйте с -D_Noreturn=; на результаты это не повлияет. Если у вас нет unistd.h, тестовая программа не сделает ничего значимого в вашей среде. Если у вас нет sigaction, вы мая сможете адаптировать программу для использования альтернативных интерфейсов, но вам нужно убедить SIGALRM как-то прервать блокирующий read.

Полученные результаты

before fgetc 1, feof = 0 ferror = 0
after fgetc 1, ch = EOF feof = 0 ferror = 1
after fgetc 2, alarm did not fire
after fgetc 2, ch = 'x' feof = 0 ferror = 1

(«нормальное чтение достоверных данных с установленным индикатором ошибки»)

  • Linux с glibc 2.27
  • NetBSD 7.1.2
  • FreeBSD 11.2-РЕЛИЗ-p4
  • macOS 10.14, clang-1000.10.44.2
  • macOS 10.14, gcc 8.2.0 (доморощенный)

.

before fgetc 1, feof = 0 ferror = 0
after fgetc 1, ch = EOF feof = 0 ferror = 1
after fgetc 2, alarm did not fire
after fgetc 2, ch = EOF feof = 0 ferror = 1

(Поведение «липкая ошибка»: fgetc(fp) немедленно возвращает EOF без вызова read, если ferror(fp) истинен при входе)

  • Plan 9 (компилятор не распознает _Noreturn)

.

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