gets() принимает ввод, фактически не вводя его?

Я новичок в C, поэтому извините, если это глупый вопрос, но когда я запускаю следующий код:

#include <stdio.h>

int main () {
    int i;
    int test[10];
    char string[81];

    for(i = 0; i < 10; i++){
        scanf("%d", &test[i]);
    }

    for(i=0; i < 7; i++){
        gets(string);
        printf("String was entered\n");
    }

}

И введите любые 10 цифр, будет напечатана строка «строка была введена», даже если я не ввел строку в командном окне. Кто-нибудь может объяснить, почему? Есть ли способ остановить это?

Спасибо!


person Sam    schedule 18.09.2010    source источник
comment
Любая программа, использующая gets, в значительной степени автоматически имеет неопределенное поведение...   -  person R.. GitHub STOP HELPING ICE    schedule 18.09.2010
comment
Даже не читая дальше заголовка, я знаю, что вы используете (неправильное использование) scanf   -  person pmg    schedule 18.09.2010
comment
@pmg Можете ли вы уточнить? Почему это неправильное использование scanf?   -  person Sam    schedule 19.09.2010
comment
Потому что, если бы вместо этого вы использовали fgets() и sscanf(), проблема с пустым вводом вообще не возникла бы. scanf() практична, но ее трудно эффективно использовать в сочетании с другими функциями ввода. О, и я должен был сказать это раньше: никогда не используйте gets() НИКОГДА НИКОГДА!   -  person pmg    schedule 19.09.2010


Ответы (3)


После того, как вы прочитали данные с помощью scanf, новая строка все еще находится в очереди ввода, ожидая чтения. gets читает эту новую строку и останавливается (потому что она достигла конца строки!)

Обратите внимание, что плохая идея использовать gets: он не дает возможности ограничить количество символов, считываемых в буфер, поэтому, если вы введете больше символов, чем поместится в буфере, вы в конечном итоге переполнит буфер, что приведет к повреждению данных, сбою приложения, серьезной уязвимости в системе безопасности и/или любому количеству других непредсказуемых результатов. В качестве безопасной альтернативы вы можете использовать fgets вместо stdin:

fgets(string, sizeof(string), stdin);

(Обратите внимание, что вы, вероятно, захотите использовать какую-то символическую константу для размера string, чтобы не повторять ее в нескольких местах, или использовать sizeof(string), когда видно определение массива.)

person James McNellis    schedule 18.09.2010
comment
Спасибо за ответ! Есть ли способ очистить входную очередь, чтобы получить (или fgets?) нечего читать? - person Sam; 18.09.2010
comment
@Sam: нет, и если ты пытаешься очистить его, это, вероятно, означает, что ты делаешь что-то не так.. - person R.. GitHub STOP HELPING ICE; 18.09.2010
comment
@Sam: Чередование вызовов fgets и scanf может быть ... болезненным. :-) Один из способов справиться с этим - просто использовать тот или иной (и если вы используете fgets, вы можете использовать sscanf для анализа отдельных строк по предложению RBerteig. - person James McNellis; 18.09.2010
comment
Спасибо еще раз за помощь. Я выполняю учебную задачу, в которой нам было приказано использовать scanf и получать функции, чтобы сделать что-то справедливое (но немного более сложное), подобное приведенному выше коду. Не уверен, что инструктор просто не понимал, что что-то подобное произойдет, или я делаю что-то ужасно неправильно. - person Sam; 18.09.2010
comment
Инструктор должен быть расстрелян за то, что ученики вообще использовали gets. gets планируется удалить в следующей редакции стандарта C по уважительной причине. - person R.. GitHub STOP HELPING ICE; 18.09.2010
comment
@ Джонатан: Спасибо; Я обсуждал, было ли хорошей идеей использовать sizeof(string); это, как правило, очень сбивает с толку новичков (почему это работает здесь, а не при передаче массива в качестве аргумента функции?!), но я полагаю, что каждый должен когда-нибудь узнать, как это работает. - person James McNellis; 18.09.2010

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

В общем, scanf() и gets() - плохой выбор для нового кода. У них обоих есть проблемы, которые делают их болезненными при правильном использовании или даже опасными для использования вообще.

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

Было бы лучше потреблять ваш входной поток с помощью fgets(), который имеет ограничение на размер буфера, и анализировать его с помощью sscanf() и других функций после чтения.

person RBerteig    schedule 18.09.2010

следующий код должен работать

#include <stdio.h>
 int main () {
 int i;
  int test[10];
 char string[81],s[1];
 for(i = 0; i < 10; i++){
    scanf("%d", &test[i]);
}

gets(s);

for(i=0; i < 7; i++){
    gets(string);
    printf("String was entered\n");
 }

}

массив символов s[1] использовался для перехвата символа новой строки, не обработанного scanf

person kapil    schedule 14.03.2015