Как правильно использовать EOF? - PullRequest
0 голосов
/ 01 июля 2018

У меня есть вопрос по поводу EOF.

Прежде всего, я пишу простую программу, которая копирует / печатает ввод пользователя.

Однако программа копирует EOF также в выходной файл.

Например, мой O.S - это Window, и мой EOF работает, когда я набираю (Enter -> cntrl + z -> Enter) по порядку. Если я ввожу комбинацию клавиш «Hello» + Enter + EOF, вывод выводит странную букву («?») В конце скопированного пользовательского ввода.

enter image description here

Как мне избавиться от '?' в конце вывода, и почему это происходит?

#include <stdio.h>

void copy(char to[], char from[]);

main()
{
    int i;
    int c;

    char origin[10];
    char copied[10];

    for(i = 0; (c = getchar()) != EOF; ++i)
    {
        origin[i] = c;
    }

    copy(copied, origin);


    for(i = 0; i < 10; i++)
        putchar(copied[i]); 



}

void copy(char to[], char from[])
{
    int i;

    i = 0;
    while((to[i] = from[i]) != '\0')
        i++;
}

Ответы [ 4 ]

0 голосов
/ 01 июля 2018

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

  • Цикл чтения продолжается до конца файла: если входной поток длиннее 10 байтов, код вызовет переполнение буфера, сохраняя байты за концом массива origin. Это первый случай неопределенного поведения.
  • Локальный массив origin неинициализирован, поэтому его содержимое не определено. Вы не сохраняете в нем нулевой терминатор после чтения байтов из stdin.
  • В функции copy вы полагаетесь на нулевой терминатор для остановки цикла копирования, но поскольку там не было сохранено ни одного, вы получаете доступ к неинициализированному содержимому после того, как все байты, прочитанные из stdin, были скопированы. Тест нулевого терминатора объединяется с присваиванием в while((to[i] = from[i]) != '\0'). Доступ к неинициализированным данным имеет неопределенное поведение. Кроме того, вы продолжаете чтение с origin до тех пор, пока не будет найден нулевой терминатор, что приведет к дальнейшему неопределенному поведению, если вы закончите чтение за концом массива, и даже более того, при записи за конец массива copied.
  • Последний цикл выводит все 10 элементов массива copied.
  • Даже если массив origin может случайно содержать нулевые байты в конце, что предотвращает неопределенное поведение в функции copy. Цикл вывода будет по-прежнему выводить забавные символы, так как вы не остановитесь на нулевом терминаторе, а вместо этого напечатаете его в stdout и снова будете иметь неопределенное поведение при чтении неинициализированного содержимого в конце copied после этого.
  • Также обратите внимание, что прототип для main без аргументов - int main(void). Синтаксис, который вы использовали, без возвращаемого типа, был распространен в 70-х и 80-х, но теперь он устарел и больше не должен использоваться.

Вот исправленная версия:

#include <stdio.h>

void copy(char to[], char from[]);

int main(void) {
    int i;
    int c;
    char origin[10];
    char copied[10];

    for (i = 0; i < 10 - 1 && (c = getchar()) != EOF; i++) {
        origin[i] = c;
    }
    origin[i] = '\0';

    copy(copied, origin);

    for (i = 0; copied[i] != '\0'; i++) {
        putchar(copied[i]);
    }

    return 0;
}

void copy(char to[], char from[]) {
    int i;

    i = 0;
    while ((to[i] = from[i]) != '\0')
        i++;
}
0 голосов
/ 01 июля 2018

Вы безоговорочно выводите все 10 членов вашего массива.
Это можно исправить, добавив часто используемые '\0' в конце букв для вывода.
С

origin[i] = '\0';

после прочтения.

И, наконец, вывод до этого маркера вместо всего

for(i = 0; copied[i]!='\0'; i++)

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

0 голосов
/ 01 июля 2018

Вы используете IDE (возможно, CodeBlocks), который использует буфер страниц между последующими операциями ввода-вывода, и поэтому вы фактически получаете вывод.

Далее вы вынуждены печатать все десять элементов массива в цикле вывода for, что является плохой практикой кодирования.

Этот простой фрагмент может помочь вам

scanf("%10[^\n]s",input);

Используйте его для чтения ввода из файла ./youpro

Спасибо Дэвиду Ранкину в комментарии, чтобы упомянуть об ошибке.

0 голосов
/ 01 июля 2018

Вы забыли завершить NUL origin. Таким образом, вы вызываете неопределенное поведение во время копирования. Вместо этого используйте следующий код:

for(i = 0; i < 9 && (c = getchar()) != EOF; ++i) /* `i < 9` to prevent array overruns */
{
    origin[i] = c;
}
origin[i] = '\0'; /* NUL-terminate your string */

Также измените код печати на:

for(i = 0; copied[i] != '\0'; i++) /* Print until a NUL-terminator */
    putchar(copied[i]); 
...