Qubc
@Qubc
Ненавижу полисемию.

Почему поведение fscanf ( stdin, «%c», &c ) различается при чтении EOF в msvc и gcc?

Если вводить обычные символы - то в обоих компилятора обе функции работают идентично. После ввода обычного печатного символа и нажатия Enter в потоке появляются два символа: введённый символ и символ новой строки. fscanf() и fgetc() читают сначала первый символ, потом второй. Это ок. Но с Ctrl+Z это не работает.

После ввода Ctrl+Z и нажатия Enter, если я правильно понимаю, в потоке появляется только EOF и символ новой строки. Если fscanf читает EOF, то она не будет изменять содержимое переменной c. Но почему gcc не требует ввод ещё одного символа новой строки, а msvc - требует?

#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>

void test1 () {
    puts ( "test1" );
    signed char c = 0;

    c = fgetc ( stdin );
    fprintf ( stdout, "1: %d\n", c );

    c = fgetc ( stdin );
    fprintf ( stdout, "2: %d\n", c );
}

void test2 () {
    puts ( "test2" );
    signed char c = 0;

    fscanf ( stdin, "%c", &c );
    fprintf ( stdout, "1: %d\n", c );

    fscanf ( stdin, "%c", &c );
    fprintf ( stdout, "2: %d\n", c );
}
int main ( void ) {
    //test1 ();
    test2 ();  
    return 0;
}


stream: Ctrl+Z Enter Ctrl+Z Enter
gcc:
test2
^Z
1: 0
^Z
2: 0


stream: Ctrl+Z Enter Enter
msvc:
test2
^Z

1: 0
2: 10
  • Вопрос задан
  • 145 просмотров
Пригласить эксперта
Ответы на вопрос 3
@Mercury13
Программист на «си с крестами» и не только
В таком случае, когда есть подозрение на конец файла, надо проверять возвращаемое значение функции fscanf:
• EOF — файл закончился;
• 0 — не прочитали (в данном случае, когда читаем символ такого явно не будет);
• 1 — прочитали.

Если во втором случае MSVC возвращает EOF, но заполняет — поведение некузявое, но допустимое.
Если во втором случае MSVC возвращает 1 — поведение дрянь.

Вообще для такого дела fscanf — большая пушка, лучше использовать fgetc.
Ответ написан
jcmvbkbc
@jcmvbkbc
"I'm here to consult you" © Dogbert
После ввода Ctrl+Z и нажатия Enter, если я правильно понимаю, в потоке появляется только EOF и символ новой строки.

Это странное заявление. Потому что "конец файла" стандартом С (конкретно, С99) определяется так: end-of-file, that is, no more input from a stream. Это значит, что ни EOF как символ, ни что бы то ни было после конца файла прочитано быть не может. Если чтение работает (т.е. функции чтения не возвращают ошибок, либо ошибки не указывают на конец файла), то конец файла не достигнут.

Сравните поведение этих программ при обычном запуске и при запуске с перенаправлением ввода из пустого файла. Если есть разница, то это может указывать на то, что нажатие Ctrl+Z в cmd не вызывает закрытие потока ввода для процесса.
Ответ написан
^Z / ^D не является "настоящим" концом файла, они не закрывают пайп, а заставляют терминал отправить пустой буффер ввода без установки кода ошибки, что в низкоуровневых функциях типа read() является признаком конца файла. Последующие вызовы read() могут продолжают возвращать данные, поэтому поведение зависит от того, как получение признака конца файла обрабатываются высокоуровневыми функциями типа scanf и реализации структуры FILE, а в случае Windows - еще и как в стандартной библиотеке реализованы файловые дискрипторы и низкоуровневые функции, т.к. в самой системе их нет - т.е. все зависит от реализации стандартной библиотеки С. Стандартом это поведение не регулируется. Кроме всего прочего, оно может отличаться для разных шелов и разных типов терминалов.
Кроме того, как было замечено выше - нельзя использовать значения введенных scanf аргументов не проверив код возврата, если scanf вернул в вашем случае что-то отличное от 1 - они не определены.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы