sprintf
アプリケーションで文字列をコピーするために多くの が使用されていることがわかります。
私は文字配列を持っています:
char myarray[10];
const char *str = "mystring";
str
文字列をにコピーしたい場合はmyarray
、次を使用することをお勧めします。
sprintf(myarray, "%s", str);
また
strncpy(myarray, str, 8);
?
どちらも使用しないでください。
sprintf
は危険であり、推奨されておらず、 に取って代わられていsnprintf
ます。old を文字列入力で安全に使用する唯一の方法sprintf
は、 を呼び出す前に長さを測定することですsprintf
。これは見苦しく、エラーが発生しやすい方法です。または、フィールドの精度指定子を追加することによって (たとえば%.8s
、または%.*s
サイズ制限のための追加の整数引数を使用して)、これはまた、特に複数の%s
指定子が関係している場合、見苦しく、エラーが発生しやすくなります。
strncpy
も危険です。のバッファ サイズ制限バージョンではありませんstrcpy
。これは、文字を固定長の null で埋められた (nullで終了するのではなく) 配列にコピーするための関数です。ソースは、C 文字列または少なくともコピー先のサイズの固定長文字配列のいずれかです。その使用目的は、固定サイズのテキスト フィールドで動作し、null 終了のためにディスクまたはメモリ内で 1 バイトも無駄にしたくない、従来の UNIX ディレクトリ テーブル、データベース エントリなどでした。バッファサイズ制限として誤用される可能性がありますstrcpy
、しかし、そうすることは2つの理由で有害です. まず、バッファ全体が文字列データに使用されている場合 (つまり、ソース文字列の長さが少なくとも dest バッファと同じ長さである場合)、null 終了に失敗します。自分でターミネーションを追加することもできますが、これは見苦しく、エラーが発生しやすくなります。次に、ソース文字列が出力バッファーよりも短い場合は、strncpy
常に完全な宛先バッファーを null バイトで埋めます。これは単に時間の無駄です。
では、代わりに何を使用する必要がありますか?
BSDstrlcpy
関数が好きな人もいます。意味的には、戻り値が であり、文字列の長さに人為的な制限を課さないことをsnprintf(dest, destsize, "%s", source)
除いて、と同じです。ただし、ほとんどの一般的な非 BSD システムには がなく、自分で作成すると危険なエラーが発生しやすいため、それを使用する場合は、信頼できるソースから安全で既知の動作するバージョンを入手する必要があります。size_t
INT_MAX
strlcpy
私の好みはsnprintf
、自明でない文字列構造には単純に使用し、パフォーマンスが重要であると測定されたいくつかの自明なケースにはstrlen
+を使用することです。memcpy
このイディオムを正しく使用する習慣を身につければ、文字列関連の脆弱性を持つコードを誤って書くことはほとんどなくなります。
次の理由により、異なるバージョンの printf/scanf は非常に遅い関数です。
それらは可変引数リストを使用するため、パラメーターの受け渡しがより複雑になります。これは、さまざまなあいまいなマクロとポインターを介して行われます。すべての引数を実行時に解析して型を判別する必要があるため、余分なオーバーヘッド コードが追加されます。(VA リストは、言語の非常に冗長な機能でもあり、単純なパラメーターの受け渡しよりもはるかに弱い型付けであるため、危険でもあります。)
多くの複雑な書式設定と、サポートされているすべての異なる型を処理する必要があります。これにより、関数にも多くのオーバーヘッドが追加されます。すべての型の評価は実行時に行われるため、コンパイラは関数の使用されない部分を最適化することはできません。したがって、printf() で整数のみを出力したい場合は、プログラムにリンクされた浮動小数点数、複雑な算術演算、文字列処理などのサポートが得られ、スペースが完全に無駄になります。
一方、strcpy() や特に memcpy() などの関数は、コンパイラによって大幅に最適化されており、パフォーマンスを最大化するためにインライン アセンブルで実装されることがよくあります。
ベアボーンの 16 ビット ローエンド マイクロコントローラーで行った測定結果の一部を以下に示します。
経験則として、どの形式の製品コードでも stdio.h を使用しないでください。これは、デバッグ/テスト ライブラリと見なされます。MISRA-C:2004 は、製品コードで stdio.h を禁止します。
編集
主観的な数値を事実に置き換えました:
ターゲット Freescale HCS12、コンパイラ Freescale Codewarrior 5.1 での strcpy と sprintf の測定。sprintf の C90 実装を使用すると、C99 はさらに効果がなくなります。すべての最適化が有効になりました。次のコードがテストされました。
const char str[] = "Hello, world";
char buf[100];
strcpy(buf, str);
sprintf(buf, "%s", str);
パラメータ シャッフルのオン/オフ コール スタックを含む実行時間:
strcpy 43 instructions
sprintf 467 instructions
割り当てられたプログラム/ROM スペース:
strcpy 56 bytes
sprintf 1488 bytes
割り当てられた RAM/スタック スペース:
strcpy 0 bytes
sprintf 15 bytes
内部関数呼び出しの数:
strcpy 0
sprintf 9
関数呼び出しスタックの深さ:
strcpy 0 (inlined)
sprintf 3
文字列をコピーするためだけに sprintf を使用することはありません。それはやり過ぎであり、そのコードを読んだ人は確かに立ち止まって、なぜ私がそれをしたのか、彼ら(または私)が何かを見逃しているのではないかと思うでしょう.