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