Устаревшие функции
небезопасны
Прекрасным примером такой функции является gets () , потому что нет способа определить размер целевого буфера. Следовательно, любая программа, которая считывает ввод с помощью gets (), имеет уязвимость переполнения буфера . По аналогичным причинам следует использовать strncpy () вместо strcpy () и strncat () вместо strcat () .
Еще несколько примеров включают функции tmpfile () и mktemp () из-за потенциальных проблем безопасности с перезаписью временных файлов, которые заменяются более безопасной функцией mkstemp () .
Без повторного входа
Другие примеры включают gethostbyaddr () и gethostbyname (), которые не реентерабельны (и, следовательно, не гарантируют безопасность потоков), и были заменены реентерабельными getaddrinfo () и freeaddrinfo () .
Вы можете заметить здесь шаблон ... либо отсутствие безопасности (возможно, из-за того, что в сигнатуре не было включено достаточно информации для ее безопасной реализации), либо отсутствие повторного входа являются распространенными источниками устаревания.
Устаревшие, непереносимые
Некоторые другие функции просто устаревают, потому что они дублируют функциональные возможности и не так переносимы, как другие варианты. Например, bzero () устарел в пользу memset () .
Безопасность потоков и повторный вход В своем сообщении
вы спрашивали о безопасности потоков и повторном входе. Есть небольшая разница. Функция является реентерабельной, если она не использует какое-либо разделяемое изменяемое состояние. Так, например, если вся необходимая информация передается в функцию, и любые необходимые буферы также передаются в функцию (а не совместно используются всеми вызовами функции), то она является реентерабельной. Это означает, что разные потоки, используя независимые параметры, не рискуют случайно разделить состояние. Повторный вход - более надежная гарантия, чем безопасность потоков. Функция является потокобезопасной, если она может использоваться несколькими потоками одновременно. Функция является потокобезопасной, если:
- Он является реентерабельным (т.е. он не разделяет состояние между вызовами), или:
- Он не реентерабелен, но использует синхронизацию / блокировку по мере необходимости для общего состояния.
Как правило, в Единой спецификации UNIX и IEEE 1003.1 (т.е. «POSIX») любая функция, которая не гарантирует повторного входа, не является потокобезопасной. Другими словами, в многопоточных приложениях можно переносимо использовать только функции, которые гарантированно реентерабельны (без внешней блокировки). Однако это не означает, что реализации этих стандартов не могут сделать нереентерабельную функцию потокобезопасной. Например, Linux часто добавляет синхронизацию к функциям без повторного входа, чтобы добавить гарантию (помимо единой спецификации UNIX) безопасности потоков.
Строки (и буферы памяти в целом).
Вы также спросили, есть ли фундаментальный недостаток в строках / массивах. Кто-то может возразить, что это так, но я бы сказал, что нет, в языке нет фундаментального недостатка. C и C ++ требуют, чтобы вы передавали длину / емкость массива отдельно (это не свойство ".length", как в некоторых других языках). По сути, это не недостаток. Любой разработчик C и C ++ может написать правильный код, просто передав длину в качестве параметра там, где это необходимо. Проблема в том, что несколько API, которым требовалась эта информация, не смогли указать ее в качестве параметра. Или предположил, что будет использоваться некоторая константа MAX_BUFFER_SIZE. Такие API-интерфейсы устарели и заменены альтернативными API-интерфейсами, которые позволяют указывать размеры массива / буфера / строки.
Scanf (в ответ на ваш последний вопрос)
Лично я использую библиотеку iostreams C ++ (std :: cin, std :: cout, операторы << и >>, std :: getline, std :: istringstream, std :: ostringstream и т. д.), поэтому я обычно не занимаюсь этим. Однако, если бы мне пришлось использовать чистый C, я бы лично просто использовал fgetc () или getchar () в сочетании с strtol () , strtoul () и т. Д. И анализировал все вручную, поскольку я не большой поклонник varargs или строки формата. При этом, насколько мне известно, проблем с [f] scanf () , [f] printf () нет.и т. д., пока вы сами создаете строки формата, вы никогда не передаете произвольные строки форматирования и не разрешаете использовать вводимые пользователем данные в качестве строк форматирования, а при необходимости используете макросы форматирования, определенные в <inttypes.h> . (Обратите внимание, что snprintf () следует использовать вместо sprintf () , но это связано с невозможностью указать размер целевого буфера, а не с использованием строк формата). Я также должен отметить, что в C ++ boost :: format обеспечивает форматирование, подобное printf, без varargs.