fgetc, проверка EOF

В книге Системное программирование Linux я прочитал что-то вроде этого:

fgetc возвращает символ, прочитанный как преобразование unsigned char в int или EOF в конце файла или при ошибке. Распространенная ошибка при использовании fgetc:

char c;
if ((c = fgetc()) != EOF) {...}

Правильная версия этого кода:

int c;
if ((c = fgetc()) != EOF) { printf("%c", (char)c); ... }

Итак, почему я не могу привести возвращаемое значение к char перед сравнением с EOF? Почему я должен сравнивать EOF именно с int? Поскольку EOF определяется как -1, разве обычно оно не приводится к char?
Есть ли платформы/компиляторы, на которых это неверно?


person pproger    schedule 15.06.2012    source источник
comment
возможный дубликат while( !feof(file) ) всегда неправильный   -  person jww    schedule 14.09.2014
comment
@jww: в этом вопросе не используется feof(), поэтому он не является дубликатом while (!feof(file)) всегда неправильно .   -  person Jonathan Leffler    schedule 01.12.2014


Ответы (2)


Вы не можете преобразовать возвращаемое значение в char, потому что возвращаемое значение может быть EOF, а значение EOF зависит от системы и не равно любому допустимому коду символа. ссылка

Обычно это -1, но вы не должны так считать.

Проверьте этот отличный ответ с c-faq-site:

Возможны два режима отказа, если, как в приведенном выше фрагменте, возвращаемое значение getchar присваивается символу.

  1. Если тип char является знаковым и если EOF определен (как обычно) как -1, символ с десятичным значением 255 ('\377' или '\xff' в C) будет расширен по знаку и будет сравниваться равным EOF, преждевременно завершающий ввод. (при условии, что 8 бит char).

  2. Если тип char не имеет знака, фактическое значение EOF будет усечено (за счет отбрасывания его старших битов, что, вероятно, приведет к 255 или 0xff) и не будет распознано как EOF, что приведет к фактически бесконечному вводу.

Надеюсь, поможет!

Отредактировано: (добавлен комментарий @FatalError к этому ответу, это объясняется на сайте c-faq, но мне это кажется более понятным)

Если вы примените его к char, то EOF примет то же значение, что и некоторый допустимый символ, и, следовательно, станет неотличимым от этого символа. Одного этого должно быть достаточно, чтобы не делать результат комментарием char @FatalError.

person Cacho Santa    schedule 15.06.2012
comment
И что? я не понимаю. после приведения char будет равен '-1', не так ли? - person pproger; 15.06.2012
comment
@pproger Стандарт на самом деле не определяет, является ли только char подписанным или беззнаковым. Если вам нужен гарантированно подписанный символ, вы должны использовать signed char. - person Corbin; 15.06.2012
comment
@Corbin Хм, я думал, что char всегда подписан. Спасибо, посмотрю стандарт - person pproger; 15.06.2012
comment
@cacho Я подозревал это, но не нашел никакой информации об этом. Спасибо - person pproger; 15.06.2012
comment
Если вы приведете его к char, то EOF примет то же значение, что и некоторый допустимый символ, и, следовательно, станет неотличимым от этого символа. Одного этого должно быть достаточно, чтобы не делать результат char. - person FatalError; 15.06.2012
comment
@pproger правильно. Как упоминал Качо, часто это так, но я считаю, что стандарт требует только отрицательного значения и не указывает, что это -1. - person FatalError; 16.06.2012
comment
@FatalError, я добавил ваш комментарий к ответу, надеюсь, это не проблема..... - person Cacho Santa; 16.06.2012
comment
@cacho Конечно, никаких проблем. - person FatalError; 16.06.2012

Есть две возможности, когда вы присваиваете значение char перед сравнением с EOF:

  • char является значением со знаком. В этом случае есть допустимый символ (часто ÿ, СТРОЧНАЯ ЛАТИНСКАЯ БУКВА Y С ДИЭРЕЗИСОМ, U+00FF), который будет неправильно истолкован как EOF.
  • char — беззнаковое значение. В этом случае EOF будет преобразован в 0xFF, а затем повышен до int как положительное значение, которое никогда не будет сравниваться равным EOF, который является отрицательным значением.

В любом случае, программа будет неправильно себя вести некоторое время.

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

person Jonathan Leffler    schedule 15.06.2012