1. ホーム
  2. c

[解決済み] 標準ライブラリで避けるべき関数は何ですか?

2023-02-17 01:18:20

質問

Stack Overflow で、いくつかの C 関数は "obsolete" または "should be avoided" と書いてありました。この種の関数のいくつかの例とその理由を教えてください。

そのような関数の代替となるものは何ですか?

それらを安全に使用することはできますか - 何か良い方法はありますか?

どのように解決するのですか?

非推奨の関数

安全でない

このような関数の完全な例として gets() で、これは宛先バッファの大きさを伝える方法がないためです。その結果、gets()を使って入力を読み取るプログラムは バッファオーバーフローの脆弱性 . 同様の理由で strncpy() の代わりに strcpy() strncat() の代わりに strcat() .

さらにいくつかの例では tmpfile() mktemp() 関数に起因する 一時ファイルを上書きすることによる潜在的なセキュリティ問題 であり、より安全な mkstemp() 関数に取って代わられました。

非リーエントラント

その他の例としては gethostbyaddr() gethostbyname() はリエントラントでない (したがってスレッドセーフであることが保証されていない) ため、リエントラントの getaddrinfo() freeaddrinfo() .

セキュリティの欠如 (おそらく署名に十分な情報を含めることができず、安全に実装できない) や再入可能性の欠如が、非推奨の一般的な原因です。

時代遅れ、非移植可能

他のいくつかの関数は、単に機能が重複していたり、他の亜種と比較して移植性が低いために非推奨となります。たとえば bzero() は非推奨となり、代わりに memset() .

スレッドセーフとリエントランス

あなたは投稿の中で、スレッドセーフと再入力について尋ねました。わずかな違いがあります。関数は、共有され、変更可能な状態を使用しない場合、リエントラントです。例えば、必要な情報がすべて関数に渡され、必要なバッファも(関数へのすべての呼び出しで共有されるのではなく)関数に渡される場合、それはリエントラントです。つまり、異なるスレッドが独立したパラメータを使用することで、誤って状態を共有するリスクを回避することができるのです。再入可能性は、スレッドセーフよりも強力な保証である。複数のスレッドが同時に使用できる場合、その関数はスレッドセーフです。関数は、以下の場合にスレッドセーフです。

  • リエントラントである (すなわち、呼び出しの間で状態を共有しない)、または。
  • リエントラントではないが、共有状態のために必要に応じて同期/ロックを使用する。

一般に 単一の UNIX 仕様 IEEE 1003.1 (すなわち POSIX") では、リエントラントであることが保証されていない関数は、スレッドセーフであることが保証されません。つまり、リエントラントであることが保証されている関数だけが、(外部ロックなしで)マルチスレッドアプリケーションで移植的に使用することができるのです。しかし、これらの規格の実装が、リエントラントでない関数をスレッドセーフにすることを選択できないわけではありません。たとえば、Linux では、スレッドセーフの保証 (Single UNIX 仕様を超える) を追加するために、リエントラントでない関数に同期を頻繁に追加しています。

文字列 (および一般的なメモリ バッファ)

また、文字列や配列に何か根本的な欠陥があるのではないかという質問もありました。そのように主張する人もいるかもしれませんが、私は「いいえ、言語には根本的な欠陥はありません」と主張します。CとC++では、配列の長さ/容量を個別に渡す必要があります(他のいくつかの言語のように".length"のプロパティではありません)。これは、それ自体、欠陥ではありません。CやC++の開発者であれば、必要なところで長さをパラメータとして渡すだけで、正しいコードを書くことができます。問題は、この情報を必要とするいくつかのAPIが、それをパラメータとして指定しなかったことです。あるいは、何らかの MAX_BUFFER_SIZE 定数が使用されると仮定していました。このようなAPIは現在では非推奨となり、配列/バッファ/文字列のサイズを指定できる代替APIに置き換えられています。

スキャンフ (最後の質問への回答)

個人的には、C++ の iostreams ライブラリ (std::cin, std::cout, << と >> 演算子, std::getline, std::istringstream, std::ostringstream 等) を使用しているので、通常これを扱うことはありません。しかし、もし純粋な C 言語を使わざるを得ないとしたら、私個人としては、単に fgetc() または getchar() と組み合わせて strtol() , strtoul() などのように、手動でパースしています。とはいえ、私の知る限りでは、以下のような問題はないようです。 [f]scanf() , [f]printf()(プリントエフ などのように、フォーマット文字列を自分で作成し、任意のフォーマット文字列を渡したり、ユーザ入力がフォーマット文字列として使われることを決して許したりせず、そして で定義されているフォーマット・マクロを使用します。 で定義されているフォーマットマクロを適切に使用することです。(注意 snprintf() の代わりに sprintf() の代わりにを使うべきですが、これは宛先バッファのサイズを指定しなかったことと関係があり、フォーマット文字列の使用とは関係ありません)。また、C++の場合、指摘しておかなければならないのは boost::format は、可変長引数なしで printf のような書式を提供します。