Запутался в функции getchar и EOF

#include <stdio.h>

int main()
{
    int c = getchar();

    while (c != EOF) {
        putchar(c);
        c = getchar();
    }

    return 0;
}

Проблема заключается в том, чтобы отличить конец ввода от действительных данных. решение состоит в том, что getchar возвращает отличительное значение, когда нет больше ввода, значение, которое нельзя спутать ни с одним реальным символом. Это значение называется EOF, что означает «конец файла». Мы должны объявить c равным тип, достаточно большой, чтобы содержать любое значение, которое возвращает getchar. мы не можем использовать char, так как c должен быть достаточно большим, чтобы содержать EOF в дополнение к любому возможный чар. Поэтому мы используем внутр.

Из книги «Язык программирования Си». У меня есть три вопроса. Во-первых, почему я получаю вывод ^\Quit (core dumped), когда одновременно нажимаю клавиши ctrl и 4 во время выполнения вышеуказанной программы? Я использую машину GNU/Linux.

Во-вторых, я написал такую ​​программу:

#include <stdio.h>

int main()
{
    printf("The part before EOF\n");
    putchar(EOF);
    printf("The part after EOF\n");
}

Затем скомпилировал это как «eof.out» и изменил int c = getchar(); в программе из книги на char c = getchar();, сохранил его, а затем скомпилировал программу как «copy.out». Когда я запускаю команду ./eof.out | ./copy.out в терминале, я получаю вывод:

The part before EOF

Это означает, что программа «copy.out» работала правильно, так как она не напечатала второй printf, но приведенный выше отрывок из книги указывает на то, что должен был произойти какой-то сбой, так как я изменил int на char, так что же произошло?

В-третьих, когда я меняю char c = getchar(); на double c = getchar(); и запускаю команду ./eof.out | ./copy.out, я получаю следующий вывод:

The part before EOF
�The part after EOF

Почему putchar(EOF); не остановил copy.out ? Разве у double не больше байтов, чем у int и char? что происходит?

См. это и это относительно Ctrl + 4. Это в основном эквивалентно Ctrl + Break в Windows.

Super-intelligent Shade 30.07.2023 16:39
putchar(EOF) записывает один байт, который (почти наверняка, но может варьироваться в зависимости от платформы) 0xff. getchar считывает этот байт как целое число 255, и double y = getchar(), таким образом, присваивает значение 255 переменной y. Но 255 != EOF.
William Pursell 30.07.2023 16:49

Относительно «отрывок из книги выше указывает на то, что должен был быть какой-то сбой, так как я изменил int на char»: отрывок из книги не говорит, что должен быть какой-то сбой. В нем говорится, что EOF — это «значение, которое нельзя спутать ни с одним реальным символом»; это не говорит, что вы не можете преобразовать EOF в char. Если ваша реализация C использует беззнаковый тип char, преобразование переносит значение по модулю 2^N, где N — количество битов в char, обычно восемь, поэтому по модулю 256. Например, -1 отображается в 255…

Eric Postpischil 30.07.2023 16:49

… Если ваша реализация C использует знаковый char, преобразование определяется реализацией.

Eric Postpischil 30.07.2023 16:49

Диапазон кодовых точек ASCII 0..127. На большинстве (всех?) платформах char может содержать значения -128..127. На большинстве (всех?) платформах EOF определяется как -1. Итак, char будет отлично работать в вашей программе.

Super-intelligent Shade 30.07.2023 16:58

@EricPostpischil, книга технически неверна, если вы вырвете ее утверждение из контекста или интерпретируете «не может» как исключающее ошибку программиста. Но когда это утверждение понимается как утверждение о поведении getchar(), оно абсолютно правильно. При вызове, когда на самом деле есть символ, который можно вернуть, getchar() / getc() / fgetc() возвращает этот символ как unsigned char (преобразованный в int), тогда как EOF гарантированно расширяется до отрицательного целого числа. Первое всегда отличимо от второго.

John Bollinger 30.07.2023 17:03

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

Nate Eldredge 30.07.2023 17:07

@JohnBollinger: это обсуждалось на Stack Overflow много лет назад. В обычных реализациях C EOF нельзя путать с возвратом символа из getchar. Но если char и int имеют одинаковую ширину, например, обе 16 бит, то unsigned char, которое возвращает getchar, автоматически преобразуется в возвращаемый тип int, поэтому 65535 будет преобразовано в −1 (при условии переноса), и будет невозможно отличить символ 65535 от значения EOF исключительно по возвращаемому значению getchar. (Тестирование feof может сделать это.)

Eric Postpischil 30.07.2023 17:16

@JohnBollinger: см. здесь и здесь.

Eric Postpischil 30.07.2023 18:54

Принято, @EricPostpischil. Я действительно думаю, что это в значительной степени теоретическая возможность, а не практическая, потому что даже если char имеет тот же размер, что и int в некоторой реализации — скорее всего, под управлением целевой архитектуры — этой реализации не нужно распознавать символы, соответствующие всем значениям в диапазоне типа char. Действительно, эта проблема является веской причиной для того, чтобы такую ​​реализацию не делать. Но вы правы, в принципе такое могло случиться.

John Bollinger 30.07.2023 20:44
double c = getchar(); - что это за безумие?
Paul Sanders 30.07.2023 20:50
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
11
84
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

getchar и putchar работают со значениями unsigned char, а не со значениями char, поэтому объявление c типом char приводит к тому, что допустимый символ 255 путают с EOF.

Чтобы упростить объяснение, этот ответ предполагает обычную реализацию C, за исключением случаев, когда указано: char знаковый и восьмибитный, EOF равен −1 и преобразование в целые типы со знаком по модулю 2w, где w — ширина типа в битах. Стандарт C допускает здесь некоторые вариации, но эти предположения типичны для обычных реализаций C и соответствуют поведению, указанному в вопросе.

Рассмотрим этот код для eof.c из вопроса:

#include <stdio.h>

int main()
{
    printf("The part before EOF\n");
    putchar(EOF);
    printf("The part after EOF\n");
}

Когда эта программа выполняется putchar(EOF), происходит следующее:

  • putchar преобразует EOF в unsigned char. Это указано в C 2018 7.21.7.3 (через 7.21.7.7 и 7.21.7.8).
  • Преобразование -1 в unsigned char дает 255, потому что преобразование в беззнаковый восьмибитный целочисленный тип переносит по модулю 256, а -1 + 256 = 255.
  • Код символа 255 записывается в стандартный вывод.

… заменил int c = getchar(); в программе из книги на char c = getchar();, сохранил, а затем скомпилировал программу как 'copy.out'. Когда я запускаю команду ./eof.out | ./copy.out в терминале, я получаю вывод:

The part before EOF

Что происходит с c = getchar();, когда считывается байт 255 и оценивается c = getchar():

  • getchar возвращает 255. Обратите внимание, что это код символа как значение unsigned char согласно C 2018 7.21.7.1 (через 7.21.7.5 и 7.21.7.6).
  • Чтобы присвоить 255 c, 255 преобразуется в тип char. В соответствии с приведенным выше предположением это оборачивает по модулю 256, что дает -1.

−1 — это значение EOF, поэтому c != EOF ложно, поэтому цикл завершается, и программа завершается.

Почему не putchar(EOF); остановить copy.out ? Разве у double не больше байтов, чем у int и char? что происходит?

С double c значение, присвоенное c, является значением, возвращаемым из getchar; изменений нет из-за того, что тип назначения не может представить все возвращаемые значения getchar. Когда getchar возвращает допустимый код символа 255, c устанавливается на 255, и цикл продолжается. Когда getchar возвращает код -1 для конца файла, c устанавливается в -1, и цикл завершается.

… книга указывает, что должен был быть какой-то сбой, так как я изменил int на char

Отрывок из книги не говорит, что должен быть какой-то провал. В нем говорится, что EOF — это «значение, которое нельзя спутать ни с одним реальным символом»; это не говорит, что вы не можете преобразовать EOF в char. Если ваша реализация C использует тип unsigned char, преобразование переносит значение по модулю 2w, где w — количество битов в char, обычно восемь, поэтому по модулю 256. Например, -1 отображается в 255. Если ваша реализация C использует знак char, преобразование определяется реализацией. Таким образом, ваша программа eof.c не выводит индикацию конца файла при оценке putchar(EOF). Вместо этого он выводит код символа 255.

Ладно, честно, я пропустил эту часть...

chqrlie 30.07.2023 22:46

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

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

Как прочитать размер файла /proc/cmdline
Привет, ребята, может ли кто-нибудь сказать мне, что не так с моим кодом ??? Я только начал, поэтому он маленький и очень простой, но я не могу указать, что именно не так
Почему в простом двоичном файле ELF есть перекрывающиеся и смещенные сегменты?
Возникла проблема с памятью в пользовательской структуре динамического массива в C
(void) переменная альтернатива для Rust
Как заставить clangd включить реализацию библиотеки только для заголовков
При попытке объединить два связанных списка, почему я получаю ошибку сегментации (сброс ядра)?
Почему во многих открытых исходных кодах так популярно использовать псевдонимы #define или typedef для функций или переменных, встроенных в C?
В C, почему 10/3 дает 3,000, когда должно давать 3,333? (переменные, содержащие 10 и 3, объявлены «двойными»)
Как отправить байт с клиента c на сервер Java?