web-dev-qa-db-ja.com

strcpyではなくstrncpyを使用する必要があるのはなぜですか?

編集:例のソースを追加しました。

この例

char source[MAX] = "123456789";
char source1[MAX] = "123456789";
char destination[MAX] = "abcdefg";
char destination1[MAX] = "abcdefg";
char *return_string;
int index = 5;

/* This is how strcpy works */
printf("destination is originally = '%s'\n", destination);
return_string = strcpy(destination, source);
printf("after strcpy, dest becomes '%s'\n\n", destination);

/* This is how strncpy works */
printf( "destination1 is originally = '%s'\n", destination1 );
return_string = strncpy( destination1, source1, index );
printf( "After strncpy, destination1 becomes '%s'\n", destination1 );

この出力を生成したもの:

宛先は元々= 'abcdefg' 
 strcpyの後、宛先は '123456789'になります
 
 destination1 is 

だから誰がこの効果を望んでいるのだろうと思う。紛らわしいようです。このプログラムは、基本的に誰かの名前(たとえば、Tom Brokaw)をTom Bro763でコピーできると思います。

使用する利点は何ですかstrncpy()overstrcpy()

76
Kredns

strncpyは、バッファオーバーフローに対処するために、長さを指定することを要求します。 strcpyは末尾の\0に依存しますが、常に発生するとは限りません。

第二に、7文字の文字列で5文字のみをコピーすることを選択した理由は私にはわかりませんが、予想される動作を生み出しています。最初のn文字のみをコピーします。nは3番目の引数です。

n関数はすべて、バッファオーバーフローに対する防御コーディングとして使用されます。 strcpyなどの古い関数の代わりに使用してください。

91
Eric

strncpy()関数は、非常に特定の問題を念頭に置いて設計されました。元のUNIXディレクトリエントリの方法で格納された文字列を操作します。これらは固定サイズの配列を使用し、nul-terminatorはファイル名が配列より短い場合にのみ使用されました。

これが、strncpy()の2つの奇妙な点の背後にあるものです。

  • 宛先が完全に満たされている場合、宛先にヌルターミネーターを配置しません。そして
  • 必要に応じてヌルで常に宛先を完全に埋めます。

「より安全なstrcpy()」の場合、次のようにstrncat()を使用することをお勧めします。

if (dest_size > 0)
{
    dest[0] = '\0';
    strncat(dest, source, dest_size - 1);
}

それは常に結果を無効にし、必要以上にコピーすることはありません。

163
caf

私はstrncpyの背後にある意図を知っていますが、それは本当に良い機能ではありません。両方を避けてください。 レイモンド・チェンが説明する

個人的には、nullで終わる文字列を扱う場合は、strncpyとそのすべての友人を避けるだけです。名前の「str」にもかかわらず、これらの関数はヌル終了文字列を生成しません。 nullで終わる文字列を生の文字バッファに変換します。 2番目のバッファが間違っているため、ヌル終了文字列が予想される場所でそれらを使用します。ソースが長すぎる場合、適切なヌル終端を取得できないだけでなく、ソースが短い場合、不要なヌルパディングが発生します。

なぜstrncpyは安全ではないのですか? も参照してください。

31
Sinan Ünür

strncpyはstrcpyより安全ではありません。あるタイプのバグを別のタイプのバグと交換するだけです。 Cでは、C文字列を処理する場合、バッファのサイズを知る必要がありますが、回避する方法はありません。 strncpyは、他の人が言及したディレクトリに対して正当化されましたが、それ以外の場合は使用しないでください。

  • 文字列とバッファの長さがわかっている場合、なぜstrncpyを使用するのですか?せいぜい計算能力の無駄です(無駄な0を追加する)
  • 長さがわからない場合は、文字列を静かに切り捨てるリスクがありますが、これはバッファオーバーフローよりもはるかに優れています
27

あなたが探しているのは、常に文字列を0で終了し、バッファを初期化する関数strlcpy()です。また、オーバーフローを検出することもできます。唯一の問題は、(本当に)移植性がなく、一部のシステム(BSD、Solaris)にのみ存在することです。この関数の問題は、 http://en.wikipedia.org/wiki/Strlcpy の議論でわかるように、ワームの別の缶を開くことです。

私の個人的な意見では、strncpy()strcpy()よりもはるかに便利です。パフォーマンスが向上し、snprintf()の優れたコンパニオンです。それを持たないプラットフォームの場合、実装は比較的簡単です。 (アプリケーションの開発段階では、これら2つの関数(snprintf()およびstrlcpy())を、バッファオーバーフローまたは切り捨てでプログラムを残酷に中止するトラップバージョンに置き換えます。これにより、最悪の犯罪者をすばやくキャッチできます。特に、他の人のコードベースで作業している場合。

編集:strlcpy()は簡単に実装できます:

size_t strlcpy(char *dst, const char *src, size_t dstsize)
{
  size_t len = strlen(src);
  if(dstsize) {
    size_t bl = (len < dstsize-1 ? len : dstsize-1);
    ((char*)memcpy(dst, src, bl))[bl] = 0;
  }
  return len;
}
21

strncpy()関数の方が安全です。宛先バッファが受け入れることができる最大長を渡す必要があります。そうしないと、ソース文字列が正しく0で終了しない可能性があります。その場合、strcpy()関数は宛先にさらに文字を書き込み、宛先バッファーの後のメモリにあるものをすべて破損する可能性があります。これは多くのエクスプロイトで使用されるバッファオーバーランの問題です

また、read()のようなPOSIX API関数の場合、終端の0をバッファーに入れず、読み取ったバイト数を返すため、手動で0を入れるか、strncpy()を使用してコピーします。

サンプルコードでは、indexは実際にはインデックスではなく、countです。ソースからコピー先にコピーする文字数最大を示します。ソースの最初のnバイトの間にnullバイトがない場合、destinationに配置された文字列はnullで終了しません

3
CsTamas

strncpyは、宛先のサイズが小さい場合でも、宛先のサイズを「\ 0」でいっぱいにします。..

マンページ:

Srcの長さがnより小さい場合、strncpy()はdestの残りにヌルバイトを埋め込みます。

残りだけでなく...この後、n文字に達するまで。したがって、オーバーフローが発生します...(manページの実装を参照)

1
Jeronimo

それは私たちの要件に依存します。 Windowsユーザーの場合

文字列全体をコピーしたくない場合や、n文字だけをコピーしたい場合は、strncpyを使用します。ただし、strcpyは、終端のヌル文字を含む文字列全体をコピーします。

これらのリンクは、strcpyとstrncpyおよび私たちがどこで使用できるかを知るのに役立ちます。

約strcpy

約strncpy

0
Prakash

これは、元の文字列の一部のみを宛先にコピーする必要がある他の多くのシナリオで使用できます。 strncpy()を使用すると、strcpy()とは対照的に、元の文字列の限られた部分をコピーできます。あなたが書いたコードは publib.boulder.ibm.com から来ていると思います。

0
ARV