Почему мне требуется несколько символов EOF (CTRL Z)?

#c #windows-xp #cmd #eof

#c #windows-xp #cmd #eof

Вопрос:

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

Если это имеет значение, я компилирую свой исходный код в среде Windows XP с использованием компилятора Tiny C (TCC) и выполняю двоичные файлы в консоли XP (cmd.exe).

Проблема: обработка End-of-File (EOF) characters . Я собрал небольшой тестовый пример, чтобы проиллюстрировать проблему. Похоже, что программа обрабатывает символ EOF (частично). Я попытаюсь продемонстрировать проблему с примерами ввода / вывода.

 #include <stdio.h>

int main() 
{
    int character, count;

    character = 0;
    character = getchar();

    for (count = 0; character != EOF;   count) 
    {
        character = getchar();
    }

    printf("Count: %d", count);
    return 0;
}
  

Пример ввода 1: abcd^Z[enter] (где ^ Z / CTRL Z представляет символ EOF, а [enter] представляет клавишу ввода.)

Пример вывода 1: Count: 4 (ожидает дополнительных входных данных или завершается должным образом на ^ C / ^ Z [enter])

Пример ввода 2: abcd^Zefgh

Пример вывода 2: Count: 4 (ожидает дополнительных входных данных или завершается должным образом на ^ C / ^ Z [enter])

Как отмечалось в обоих примерах, количество символов не выводится до тех пор, пока не будет инициирована последовательность ^C / ^ Z [enter]. До запуска программа ожидает (и действительно обрабатывает) дополнительные входные данные. Однако, как отмечалось в примере 2, когда программа встречает начальный символ ^ Z, она прекращает обработку этой строки ввода, ожидая дополнительных входных данных или возвращая правильное количество, если инициируется последовательность ^ C / ^ Z [enter].

Я не могу понять, почему программа только частично обрабатывает символ EOF. Мне кажется, что если он усекает конец примера 2, то он также должен полностью выходить из цикла. Есть идеи, почему при распознавании символа EOF программа не выводит текущее количество и не завершает работу?

Комментарии:

1. В качестве обновления я определил, что крошечный компилятор C, похоже, распознает символ EOF ^ Z. И ^Z (ввод), и EOF (символическая константа) распознаются как значение -1.

2. В другом обновлении я определил другую символическую константу EOT (End-of-Transmission) равной 4. Теперь, когда я тестирую эту константу, выдавая a ^ D (распознается как значение 4), все работает в соответствии с планом. Однако, если EOT определено равным -1, как EOF, программа снова прерывается. Это действительно кажется странным, поскольку ^ Z распознается программой как значение -1.

3. Вы ошибаетесь, рассматривая ^Z как символ. Какой бы символ на уровне терминала / консоли ни был назначен для генерации EOF, он интерпретируется другим уровнем до того, как попадет в ваше приложение. В Windows ^D по умолчанию не является специальным, поэтому вы просто получаете байт ^D (4).

4. @R .. Да, я думаю, ты прав. После дальнейшего обдумывания проблемы я пришел к выводу, что ^ Z распознается как -1 при чтении само по себе (вероятно, потому, что cmd усекает дальнейший ввод, что приводит к чтению нулевой длины). Здесь я только предполагаю, но это имеет смысл при рассмотрении сценария 2, поскольку программа считывает только ‘abcd’ в ожидании дальнейшего ввода. Кажется, что ^ Z (в отличие от ^ D, который фактически представляет символ ASCII 4) является просто символическим представлением небытия / void. Еще раз спасибо за ваше понимание. Вы определенно помогли мне укрепить эту концепцию.

Ответ №1:

Этот ответ соответствует unix, но я думаю, что аналогичный phenonemon происходит в Windows. Базовая форма EOF — нулевой длины read . На интерактивных устройствах ввода (терминалах) существует специальный механизм для наличия EOF во входном потоке, но если уже есть входные данные для чтения, они будут использованы вместе с этим вводом (в результате чего получится ненулевая длина read ) и, таким образом, никогда не будут замечены приложением. Только когда EOF происходит без предварительного буферизации входных данных, приложение может это заметить и отреагировать на него.

Если у вас есть доступ к Linux (или другой * nix) системе, напишите аналогичную тестовую программу и запустите ее под strace . Посмотрите на основные read вызовы, которые происходят, и причина этого, в противном случае неинтуитивного поведения, будет иметь смысл.

Комментарии:

1. Информация очень ценится. На данный момент у меня нет запущенной * nix-машины, но в ближайшие несколько дней ее нужно будет настроить с единственной целью проверки этой гипотезы. Однако, если сигнал EOF используется, как предложено, мне интересно, почему программа не продолжает с удовольствием считывать все, что осталось в строке (если, возможно, cmd не выполняет усечение после ^ Z, прежде чем программа зайдет так далеко.)

2. Вероятно, это особенность Windows, с которой я не знаком. 🙂

Ответ №2:

Это восходит к каменному веку вычислительной техники. По крайней мере, CP / M, возможно, дольше, чем в операционных системах раннего декабря. CP / M не сохранял размер файла, он только отслеживал количество секторов диска, по 128 байт в каждом. Это не проблема для двоичных файлов, программа просто прекращает чтение, когда у нее достаточно. Но, безусловно, проблема для текстовых файлов.

Итак, по соглашению, конец текстового файла был отмечен кодом 0x1a, Control Z. Из-за устаревших текстовых файлов, размер которых превышал объем текста в них, это приходилось переносить в каждом последующем поколении реализаций CRT. Windows на это наплевать, это чисто деталь реализации CRT. Вот почему ввод Ctrl Z на консоли не приводит к чему-то особенному. Как только вы нажмете Enter, ЭЛТ в cmd.exe снова вызывает устаревшее поведение и объявляет EOF.

Ответ №3:

Я не знаю наверняка с TCC, но в довольно многих (большинстве?) случаев вам нужно ввести ^ Z более или менее отдельно, чтобы он был распознан как EOF (т. Е. вам нужна последовательность [enter] ^ z [enter]).

Ответ №4:

EOF не генерируется Windows автоматически при вводе ^ Z; это просто соглашение, перенесенное из DOS. Среда выполнения вашего компилятора C должна распознать это и установить флаг EOF, и я предполагаю, что Tiny C этого не делает.

^ C, с другой стороны, распознается командной средой Windows. Это не обязательно означает EOF, я думаю, это скорее сигнал прерывания.

Комментарии:

1. Да, ^ C / CTRL C отправляет сигнал SIGINT или что-то еще, что вызывает завершение текущего процесса (именно поэтому он работает независимо). Однако я все еще не совсем понимаю, почему abcd ^ Zefgh учитывает abcd только в том случае, если символ EOF не обрабатывается. И тогда, если это так, почему программа продолжает цикл и собирает входные данные после этого.

2. @bfisher, я думаю, что преждевременно принимать этот ответ — я не полностью ответил на вопрос, не так ли?

Ответ №5:

Я бы предположил, что стандартный ввод выполняется с буферизацией строк (это в Unix). В DOS были некоторые getch() и getche() функции более низкого уровня, чем stdio, поэтому они обходят буферизацию stdio. Я не знаю, как отключить буферизацию ввода в Windows, в Unix это делается путем установки терминала в неканонический режим.