gets (gets)

Перейти к навигации Перейти к поиску

gets — функция, входящая в Стандартную библиотеку языка Си, объявляемая в заголовочном файле stdio.h, которая считывает строку стандартного ввода и помещает её в буфер, созданный вызывающей функцией. Если выдаёт ошибку, то теперь для её вызова следует использовать gets_s.

Реализация

[править | править код]

Может быть реализована следующим способом (при помощи getchar):

char *gets(char *s)
{
/*очистка буфера ввода */
fflush(stdin);

    int i, k = getchar();

    /* Возвращаем NULL если ничего не введено */
    if (k == EOF)
        return NULL;

    /* Считываем и копируем в буфер символы пока не достигнем конца строки или файла */
    for (i = 0; k != EOF && k != '\n'; ++i) {
        s[i] = k;
        k = getchar();

        /* При обнаружении ошибки результирующий буфер непригоден */
        if (k == EOF && !feof(stdin))
            return NULL;
    }

    /* Нуль-терминируем и возвращаем буфер в случае успеха.
    Символ перевода строки в буфере не хранится. */
    s[i] = '\0';

    return s;
}

Программист должен знать максимум числа символов, которые должны быть считаны gets, чтобы удостовериться, что выделяется буфер достаточного размера. Подобное невозможно без информации о данных. Эта проблема может приводить к созданию ошибок и открывает простор для нарушений компьютерной безопасности при помощи переполнения буфера. Многие источники советуют программистам никогда не использовать gets в новых программах[1][2][3].

Применение gets весьма осуждается. Функция оставлена в стандартах C89 и C99 для обратной совместимости. Множество инструментов разработки ПО, как, например, GNU ld, выдаёт предупреждения в случае обнаружения при компоновке кода с использованием gets.

Альтернативы

[править | править код]

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

char buffer[BUFFERSIZE];
gets(buffer);

кодом вида

char buffer[BUFFERSIZE];
fgets(buffer, sizeof(buffer), stdin);

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

Безопасность использования

[править | править код]

Безопасное использование gets требует от программиста проверки того, что переполнение буфера не станет проблемой. Стандарт языка Си этого не гарантирует; тем не менее, существует несколько относительно усложненных способов проверки этого с различной степенью переносимости. Одним из возможных вариантов является защитная страница для защиты памяти. В сочетании с обработчиками исключений, такими как SIGSEGV и sigaction, защитная страница может помочь с обработкой ошибок.

Примечания

[править | править код]
  1. GNU. GNU Библиотека Си — Строковый Ввод. — «Функция gets весьма опасна, так как она не обеспечивает никакой защиты от переполнения строки s. Библиотека GNU включает её только ради совместимости. Вам следует всегда использовать вместо неё fgets или getline.» Дата обращения: 2 августа 2008. Архивировано из оригинала 19 марта 2012 года.
  2. Почему все говорят не использовать gets()? comp.lang.c Часто Задаваемые Вопросы. Дата обращения: 2 августа 2008. Архивировано из оригинала 19 марта 2012 года.
  3. gets(3) — страница справки man по библиотечным функциям GNU/Linux  (англ.) — «Никогда не используйте gets(). Так как невозможно сказать, не зная ничего о данных, сколько символов будет прочитано gets(), и поэтому gets() продолжит помещать символы в буфер и после его заполнения, что весьма опасно в использовании. Это способно нарушить информационную защиту компьютерной системы.»