Листинг 11.6. Программа getsputs. с

Символьные строки и строковые функции 431

Вот как мог бы выглядеть результат пробного запуска программы раньше:
Введите строку.
Я хотел бы ознакомиться с теорией работы со строками!
Ваша строка, выведенная дважды:
Я хотел бы ознакомиться с теорией работы со строками!
Я хотел бы ознакомиться с теорией работы со строками!
Готово.
Обратите внимание, что вся введенная строка кроме символа новой строки сохраняется в массиве words, а вызов puts (words) обеспечивает такой же эффект, как и вызов printf("%s\n", words).
А вот так выглядит более новый вывод:
Введите строку.
warning: this program uses gets(), which is unsafe.
Предупреждение: эта программа использует функцию gets О, что небезопасно.
О, нет!
Ваша строка, выведенная дважды:
О, нет!
О, нет!
Готово.
Компилятор предпринял довольно необычное действие, вставив предупреждение в вывод программы! Таким образом, это сообщение будет отображаться при каждом запуске этой программы. Не все компиляторы будут поступать подобным образом. Некоторые из них могут выдавать предупреждение на этапе компиляции, но это не настолько привлекает внимание.
В чем же состоит проблема? Дело в том, что функция gets() не делает проверку, умещается ли вводимая строка в массив. Учитывая, что единственным аргументом функции является words, она просто не в состоянии выполнить такую проверку. Вспомните, что имя массива преобразуется в адрес его первого элемента. Следовательно, функции gets() известно только то, где начинается массив, но не то, сколько элементов в нем содержится.
Если строка ввода окажется слишком длинной, возникнет переполнение буфера — другими словами, символы переполнят предназначенное для них целевое пространство. Лишние символы мтут просто попасть в неиспользуемую память и привести к проблемам, которые проявятся не сразу, или же они могут перезаписать другие данные в программе. Й это — отнюдь не все возможные варианты. Ниже приведен пример запуска программы, в которой значение STLEN было переустановлено в 5, чтобы сделать переполнение буфера более вероятным.
Введите строку.
warning: this program uses gets(), which is unsafe.
Предупреждение: эта программа использует функцию gets О, что небезопасно,
Думаю, все будет отлично.
432 глава 11
Ваша строка, выведенная дважды:
Думаю, все будет отлично.
Думаю, все будет отлично.
Готово.
Segmentation fault: 11
Ошибка сегментации: 11
“Ошибка сегментации” звучит не слишком здорово, правда? В системе Unix такое сообщение указывает на то, что программа попыталась получить доступ в память, которая не была для нее выделена.
К сожалению, язык С предоставляет много возможностей для того, чтобы неудачное программирование приводило к обескураживающим и трудно отслеживаемым ошибкам. Почему же тогда только недостаток функции gets() удостоен особого внимания? Вероятно потому, что ее непредсказуемое поведение создает риск для безопасности. В прошлом злоумышленники пользовались особенностями системных программ, в которых применялась функция gets(), для вставки и выполнения кода, который компрометировал безопасность системы.
На определенном этапе многие представители сообщества программистов на С рекомендовали исключить функцию gets() из программного словаря. Комитет, создавший стандарт С99, также опубликовал обоснование стандарта. В этом обосновании были признаны проблемы, связанные с использованием функции gets(), и применять ее не рекомендовалось. Вместе с тем в обосновании оправдывалось сохранение функции gets() в качестве части стандарта из-за ее удобства в случае корректного использования, а также из-за того, что она была частью огромного объема существующего кода.
Тем не менее, комитет по созданию стандарта С11 придерживался более строгих взглядов и исключил функцию gets() из стандарта. С другой стороны, стандарт определяет то, что компилятор должен поддерживать, а не то, что он не должен поддерживать. На практике большинство компиляторов продолжают поддерживать эту функцию ради обратной совместимости. Но, как это имеет место с применяемым нами компилятором, это отнюдь не идет ему на пользу.
Альтернативы функции gets()
Традиционной альтернативой gets() является функция fgets(), которая обладает несколько более сложным интерфейсом и немного по-другому обрабатывает вводимые данные. Кроме того, в стандарте C11 к общему набору добавлена функция gets s(). Она больше похожа на gets() и ее легче использовать в существующем коде в качестве замены. Тем не менее, она является частью необязательного расширения семейства функций ввода-вывода stdio.h, поэтому компиляторы С стандарта СИ не обязаны ее поддерживать.
Читать дальше