Ответ в теме: Функции gets и gets_s. В чем опасность

      Комментарии к записи Ответ в теме: Функции gets и gets_s. В чем опасность отключены

Главная Форумы Программирование Программирование на С++ Функции gets и gets_s. В чем опасность Ответ в теме: Функции gets и gets_s. В чем опасность

#2965

MSDN описывает функцию gets как устаревшую:

These functions are obsolete. Beginning in Visual Studio 2015, they are not available in the CRT. The secure versions of these functions, gets_s and _getws_s, are still available

Компилятор gcc также выдает предупреждение о том, что функция устаревшая:

warning: ‘char* gets(char*)’ is deprecated [-Wdeprecated-declarations]

В Linux man рассказано об опасностях использования этой функции:

Never use gets(). Because it is impossible to tell without knowing the
data in advance how many characters gets() will read, and because
gets() will continue to store characters past the end of the buffer, it
is extremely dangerous to use. It has been used to break computer
security. Use fgets() instead.

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

Описанные проблемы показаны следующим кодом:

#include <stdio .h> 
struct Item {
char name[8];
int  cost;
};
int main(void) {
Item item;
item.cost = 100;
printf ("cost is %d\n", item.cost);
printf ("Enter an item name: "); 
gets(item.name);
printf ("cost is %d\n", item.cost);
printf ("name is %s\n", item.name);
}

Поля структуры располагаются в памяти последовательно, поэтому если пользователь введет название предмета более чем из семи символов (восьмым будет символ окончания строки), то стоимость предмета изменится:

gets_problem_example Проблемы использования gets

Если теперь мы поменяли бы еще раз стоимость предмета — изменилось бы и содержимое строки.

Microsoft предлагает решать проблему с помощью gets_s — эта функция принимает на вход не только буфер, но и размер буфера. В случае ошибки (буфер равен NULL или размер буфера недостаточен для сохранения введенной строки) функция возвращает NULL. Кроме того, gets_s может вызывать специальный обработчик ошибок.

Стандартная библиотека предлагает другое решение — функцию fgets, которая также принимает на вход размер буфера:

char *fgets(char *s, int size, FILE *stream);

Функция позволяет считывать данные с файла, а если требуется ввод с клавиатуры — в качестве третьего аргумента нужно передавать stdin. Если пользователь введет более size символов, будет записано лишь первые size байт, а остальные символы останутся в файле нетронутыми.

Заменив в приведенном выше коде ввод данных на
fgets(item.name, 8, stdin);
Мы получим другой вывод программы (теперь вводится только часть имени предмета, но не портятся данные и программа остается корректной.

fgets_example Использование gets вместо gets