Застарілі функції
небезпечні
Ідеальним прикладом такої функції є get () , оскільки немає можливості сказати, наскільки великий буфер призначення. Отже, будь-яка програма, яка читає вхідні дані за допомогою get (), має вразливість переповнення буфера . З подібних причин слід використовувати 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.