strcpyの代わりにstrncpyを使用する必要があるのはなぜですか?


83

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

私はこの例に出くわしました

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は元々= 'abcdefg'
strncpyの後、destination1は「12345fg」になります

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

使用しての利点は何である strncpy() 以上は strcpy()


81
「いったいなぜ誰かがstrcpy代わりに使うのだろうstrncpy?」と尋ねるつもりだったと思います。
サムハーウェル

5
私がCの最初の学期のプログラミングコースのTAだったとき、getline慎重に作成された入力に対して採点すると、のような方法を使用すると誤った結果になることを生徒に保証しました。:)
サムハーウェル

4
コードが実際に何をするのか誤解していると思います。もっとよく見なさい。
Emil H

6
Cが文字列用の半分まともな標準ライブラリを取得したことがないのは本当に残念です。
starblue 2009

7
それはそれほど残念ではありません。つまり、それは私を完全に破壊し、高級言語をはるかに楽しくしました:)
Carson Myers

回答:


98

strncpyバッファオーバーフローに長さを入力するように要求することで、バッファオーバーフローと戦います。strcpy末尾\0に依存しますが、常に発生するとは限りません。

次に、7文字の文字列に5文字だけをコピーすることを選択した理由は私にはわかりませんが、期待どおりの動作が得られます。最初のn文字をコピーするだけnです。ここで、は3番目の引数です。

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


47
参照してくださいlysator.liu.se/c/rat/d11.htmlstrncpy最初に、このようなディレクトリエントリなどの構造物で固定長名のフィールドに対処するためのCライブラリに導入しました。このようなフィールドは、文字列と同じようには使用されません。最大長のフィールドには末尾のnullは不要であり、短い名前の末尾のバイトをnullに設定すると、フィールドごとの効率的な比較が保証されます。 strncpyもともと「有界strcpy」ではなく、委員会は、そのような用途により適した機能に変更するのではなく、既存の慣行を認識することを好みました。
シナンÜnür

35
なぜこれが多くの賛成票を獲得しているのかわかりません-strncpyはstrcpyのより安全な代替手段として意図されたものではなく、実際、文字列をゼロで終了しないため、より安全ではありません。また、提供された長さをNUL文字で埋めるという点でさまざまな機能があります。cafが返信で述べているように、これは固定サイズの配列の文字列を上書きするためのものです。
ディップスティック

26
のより安全なバージョンでstrncpyないという事実は残ってstrcpyます。
シナンÜnür

7
@Sinan:私はそれがより安全だとは決して言いませんでした。それは防御的です。それはあなたに長さを入れさせ、エルゴはあなたにあなたがしていることについて考えさせます。より良い解決策はありますが、それがはるかに防御的な機能strncpyであるstrcpyためではなく、人々が使用する(そして使用する)という事実は残っています...これは私が言ったことです。
エリック

9
n個の関数はすべて、バッファオーバーフローに対する防御コーディングとして使用されます。strcpyなどの古い関数の代わりに使用してください。これはに当てはまりますがsnprintf、には関係がなくstrncat、完全に当てはまりませんstrncpy。どうしてこの答えはこれほど多くの賛成票を得ることができるでしょうか?これは、この偽の機能に関して状況がいかに悪いかを示しています。これを使用することは防御的ではありません。ほとんどの場合、プログラマーはそのセマンティクスを理解せず、ゼロ以外で終了する可能性のある文字列を作成します。
chqrlie 2016

178

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

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

  • 完全にいっぱいになった場合、宛先にヌルターミネータを配置しません。そして
  • 常に目的地を完全に埋め、必要に応じてヌルで埋めます。

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

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

これにより、結果は常にnullで終了し、必要以上にコピーされることはありません。


しかし、当然のことながら、strncpyをあなたはどちらか欲しいものは常にではありません:strncpyををする文字の最大数を受け入れ追加していない宛先バッファのサイズを...しかし、それはわずかなことだ、そうおそらくない限り問題にならないだろう'ある文字列を別の文字列に連結しようとしています。
David Wolever 2009

私はそれの理由を知りませんでした、そしてそれは私がatmに取り組んでいることに非常に関連しています。
マットジョイナー2010

strncpy()関数は、文字列を固定長のnullが埋め込まれた形式で格納するように設計されています。このような形式は、元のUnixディレクトリエントリに使用されていましたが、0〜Nバイトの文字列をNバイトのストレージに格納できるため、他の無数の場所でも使用されています。今日でも、多くのデータベースは固定長の文字列フィールドにnullが埋め込まれた文字列を使用しています。strncpy()との混同は、文字列をFLNP形式に変換するという事実に起因します。必要なのがFLNP文字列である場合、それは素晴らしいことです。nullで終了する文字列が必要な場合は、自分で終了を指定する必要があります。
スーパーキャット2011年

1
dest[0] = '\0';strncat呼び出しの前に書き込む必要があるのはなぜですか?説明していただけませんか。
SNR

4
@snr:strncat()ソース文字列を宛先文字列の末尾に連結します。ソース文字列を宛先にコピーしたいだけなので、最初に宛先を空の文字列に設定します-それが何をするかdest[0] = '\0';です。
カフェ

34

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

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

strncpyが安全でない理由も参照してください


27

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

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

これはstrncpyの良い説明だと思うので、私はそれを投票しました。strncpyには独自の問題があります。それが、例えばglibが独自の拡張機能を持っている理由だと思います。そして、はい、プログラマーとしてあなたがすべての配列のサイズを知っている必要があるのは残念です。文字列として0で終了するchar配列を持つ決定は、私たち全員に多大なコストをかけました....
Friedrich

1
固定形式のファイルにデータを保存する場合、ゼロが埋め込まれた文字列はかなり一般的です。確かに、データベースエンジンやXMLなどの人気と、進化するユーザーの期待により、固定形式のファイルは20年前よりも一般的ではなくなりました。それにもかかわらず、そのようなファイルは、多くの場合、データを格納するための最も時間効率の良い手段です。レコード内のデータの予想長と最大長の間に大きな差異がある場合を除いて、複数のチャンクに分割されたレコードを読み取るよりも、未使用のデータを含む単一のチャンクとしてレコードを読み取る方がはるかに高速です。
スーパーキャット2011年

g_strlcpy()を使用したレガシーコードのメンテナンスを引き継いだだけなので、パディングの非効率性は発生しませんが、転送されたバイト数が維持されなかったため、コードは結果を黙って切り捨てていました。
user2548100 2014年

21

あなたが探しているのは、strlcpy()常に文字列を0で終了し、バッファを初期化する関数です。また、オーバーフローを検出することもできます。唯一の問題は、(実際には)移植性がなく、一部のシステム(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;
}

3
strlcpyはLinuxとWindows以外のほとんどすべてで利用可能であると書くことができます!ただし、BSDライセンスであるため、ライブラリの1つにドロップして、そこから使用することができます。
Michael van der Westhuizen

のテストを追加しdstsize > 0、そうでない場合は何もしないことをお勧めします。
chqrlie 2016

はい、あなたが正しい。チェックを追加します。チェックがないと、宛先バッファーで長さのdstsizeがトリガーmemcpylenれ、オーバーフローします。
パトリックSchlüter

さらに、優れたソリューションを促進するための1つ。誰もがstrlcpyの再発明を不十分に続けているため、より多くの人々がstrlcpyについて知る必要があります。
rsp 2016年

@MichaelvanderWesthuizenこれはLinuxで利用できますが、glibcでは利用できません。詳細については、私の回答を参照してください(1) (2) (3)
rsp

3

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

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

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


1

strncpyは、宛先のサイズが小さい場合でも、ソースのサイズとして宛先を「\ 0」で埋めます。

マンページ:

srcの長さがn未満の場合、strncpy()はdestの残りの部分をnullバイトで埋めます。

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


3
strncpyは、宛先のサイズが小さいにもかかわらず、ソースのサイズとしてstrncpy宛先を「\ 0」で埋め ます。このステートメントは誤りであり、混乱を招く可能性があります。宛先を「\ 0」で埋めます。ソースの長さが短い場合は、size引数。size引数は、ソースのサイズではなく、ソースからコピーする最大文字数ではありませんstrncat。これは、宛先のサイズです。
chqrlie 2016

@chqrlie:その通りです。strncpy他のコピー操作に対する利点は、宛先全体が確実に書き込まれることです。コンパイラは、不確定な値を含む構造体をコピーするときに「創造的」になろうとする可能性があるため、構造体内の文字配列が完全に書き込まれるようにすることが、「驚き」を防ぐ最も簡単な方法です。
スーパーキャット2017

@supercat:この特定のケースのための非常に小さい利点...しかし、先がへの呼び出し後にパッチを適用する必要がありますstrncpyヌル終了を確保するために: strncpy(dest, src, dest_size)[dest_size - 1] = '\0';
chqrlie

@chqrlie:末尾のヌルバイトが必要かどうかは、データが何を表すかによって異なります。構造内でゼロ終端ではなくゼロ埋め込みデータを使用することは、以前ほど一般的ではありませんが、たとえば、オブジェクトファイル形式で8バイトのセクション名を使用するchar[8]場合、構造内で処理できるようになります。8文字まではchar[8]、7文字しか処理できない、または文字列をchar[9]バッファにコピーしmemcpyてから宛先にコピーする よりも優れている場合があります。
スーパーキャット2017

@chqrlie:文字列を処理するほとんどのコードは、文字列の長さを認識している必要があり、charゼロに達するまでポインターを使用して盲目的に実行しないでください。唯一の事はゼロで終了する文字列は、文字列リテラルであるために、本当に良いです、とさえ可変長は、エンコードされた接頭辞は、おそらくより良いがあるだろう。他のほとんどすべての場合、それはどちらかの長さで始まる文字列を持っている方が良いだろうか、ということを示すであろう特別な接頭辞持っているchar*本当にのようなものですstruct stringInfo {char header[4]; char *realData; size_t length; size_t size;}
スーパーキャット2017

-1

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


-1

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

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

これらのリンクは、strcpyとstrncpyについて、そしてどこで使用できるかを知るのに役立ちます。

strcpyについて

strncpyについて


-8

strncpyはstrcpyのより安全なバージョンです。実際のところ、strcpyを使用しないでください。これは、潜在的なバッファオーバーフローの脆弱性により、システムがあらゆる種類の攻撃に対して脆弱になるためです。


6
lysator.liu.se/c/rat/d11.htmlを参照してください。strncpy関数strncpyは、ディレクトリエントリなどの構造体の固定長の名前フィールドを処理するために、最初にCライブラリに導入されました。このようなフィールドは、文字列と同じようには使用されません。最大長のフィールドには末尾のnullは不要であり、短い名前の末尾のバイトをnullに設定すると、フィールドごとの効率的な比較が保証されます。strncpyは元々「有界strcpy」ではなく、委員会は、そのような用途により適した機能に変更するのではなく、既存の慣行を認識することを好みました。
シナンÜnür
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.