セグメンテーションフォールトにつながるstrncpy

私はちょうどstrncpyを使いこなしています。

私のプログラムはこのように見える

typedef struct
{
    char from_str[10];
}test;

main ()
{

    test     s1;
    memset(&s1,0,sizeof(test));
    char       src[10]="himansh";
    char       dest[10];

    memset(dest,0,10);
    src[3]='\0';

    printf("src is %s and strlen is %d \n",
            src,strlen(src));

    fflush(stdout);

    strncpy(s1.from_str,src,100);

    printf("s1.from_str is %s , src is %s \n",
            s1.from_str,src);
    return 1;

}

ここで私はstrncpyを行う前に、 "src"文字列に "\ 0"文字を追加しました。 "src"文字列の長さは3になり、宛先配列はサイズ10になります。しかし、strncpyにはコピーするバイト数を100 。

これは私のソース文字列がNULLで終わることを意味します。現在のstrncpy関数は、指定したバイト数が3以上の場合でも3バイトだけコピーしようとします(この場合は100)。それはありますが、私はセグメンテーションフォルトも受け取ります。

私の結果は以下の通りです

src is him and strlen is 3
s1.from_str is him , src is him
Segmentation fault (core dumped)

なぜこのセグメンテーションフォールトがここで起きているのですか?

誰でも私をここで助けることができますか?

8
nl ru de
あなたがしようとしていると思うものは何か、それは二つの全く異なるものです。
追加された 著者 Brian Roach,

4 答え

私はあなたにマン・ページ、ウェブサイトなどを指摘することができますが、最終的に重要なのはCの標準です。標準的なランタイムライブラリの一部として、使用法と振舞いはC99-7.23.2.4で次のように定義されています。

#include 
char *strncpy(char * restrict s1,
      const char * restrict s2,
      size_t n);

説明    strncpy 関数は、s2が指す配列からnが指す配列までn文字(ヌル文字に続く文字はコピーされない)をコピーします   s1。オーバーラップするオブジェクト間でコピーが行われる場合、動作は未定義です。   s2が指す配列がn文字より短い文字列の場合、s1が指す配列のコピーにヌル文字が追加され、n文字はすべて書き込まれます。

     

返品    strncpy 関数は、s1の値を返します。

strncpy()は、ソース文字列がヌル文字であれば、目的の文字列を終了させませんしません長さ(ヌル文字ターミネーターを含まない)が指定された宛先バッファー長を満たしているかどうか)。

さらに、標準ではっきりと指定されていますが(上記参照)、宛先ストリングバッファを strncpy()で tail-fills しても、ソース文字列の長さが宛先バッファーサイズよりも少ないの場合に、指定された長さ n に達するまでヌル文字を返します。これは避けることのできない結論を導きます:

strncpy() API宛先バッファによって参照されるアドレスに常に n 文字を書き込みます。

あなたのケースでは、ターゲットバッファは10文字幅であるため、書き込み可能なメモリの定義済みの端を越えて90個の追加文字を書いているので、定義されていない動作の国に行きます。

この時点で、あなたは自分自身に尋ねる必要があります。間違いなく基本的なユースケースはあります。これにより、 n の文字をオーバーランしないことを予測できるように、最大​​ n の文字をターゲットバッファにコピーすることができます。期間。最終的には、ヌルで終了する文字列が必要なので、 適切な使い方は次のとおりです。

char dst[ N ]; 
strncpy(dst, src, N-1);
dst[N-1] = 0;

ここで N は文字の dst バッファの固定長で、 1 より大きいか等しいです。 dst は、ダイナミックに割り当てられたメモリポインタにすることもできます。

char *dst = malloc( N * sizeof(char) ); 
strncpy(dst, src, N-1);
dst[N-1] = 0;

上記の場合、 dst には常にヌル終了文字列を持っています。ソース文字列 length が指定されたターゲットバッファ長よりも小さい場合、 strncpy()は残りのバッファをヌル文字で末尾に埋め込みます。 -copied + tail-filled-null-charactersは n に等しく、最後の文は冗長です。 N-1 文字に達すると、ソース文字列の長さがターゲットバッファの長さと同じかそれ以上である場合、 strncpy() 、最後のステートメントはバッファの末尾にヌル文字を設定します。これにより、元のソースの「切り詰め」プレフィックス文字列が生成されますが、最も重要なことは、ターミネータをスキャンする後のstring-API呼び出しでターゲットバッファの境界を超えないことを保証します。

上記の手法の有用性は常に議論の余地があります。私はC ++の人なので、 std :: string はこの狂気から私の幸せな自己を救います。しかし現実はこれです: src全体dst にコピーされていないことが時々気になります。時々あなたはしません。有用性は状況に応じて非常にます。 UIに文字列データを表示する場合、これは重要ではありません。重要なデータに使用する文字列をコピーする場合、部分プレフィックス部分文字列は受け入れられません。警察が「ジョセフ・ジョンソンJr.」の逮捕状を発行すると、令状発行ソフトウェアの名簿には15文字しか記されていないため、父親(「ジョセフ・ジョンソン」)が拘束されたときに何らかの説明があります。 。

あなたのセグメンテーション違反はこの声明になります:

strncpy(s1.from_str,src, 100);//length parameter is wrong.

上記の大胆なステートメントを思い出してください。 " strncpy()は、宛先バッファによって参照されるアドレスに常に n 文字を書きます。これは、上記のコードが100文字をターゲットバッファに書き込むことを意味します。この場合、あなたの場合は10文字幅であるため、未定義の動作で ker-boom をクリックします。

ターゲットバッファが固定長文字配列の場合、次のようにしてこれを修正します。

strncpy(s1.from_str,src, sizeof(s1.from_str)/sizeof(s1.from_str[0])-1);
s1.from_str[ sizeof(s1.from_str)/sizeof(s1.from_str[0])-1 ] = 0;

長さ `N charsの動的文字列に対してこれを行う方法は、これまでの使用法を参照してください。

13
追加された
strncpy は、固定サイズのフィールドを予測可能にファイルするためのものです。これは文字列処理関数ではありません。
追加された 著者 vonbrand,
thnx @WhozCraig ..非常に詳細で記述的です。私はstrncpyでうろついていました。私はstrncpyを安全に使う方法を知っています。私はちょうど 'src'文字列がNULLで終わっているかどうか知りたかったのですが、 'n'が 'src'の文字列より大きければ 'dest'文字列にコピーされる文字数は多く、 'dest'文字列のサイズは十分です'src'文字列で 'n'より小さい。 strncpyのMANページの説明が私には十分ではありませんでした..
追加された 著者 Himanshu Gupta,

From http://www.cplusplus.com/reference/cstring/strncpy/

char * strncpy(char * destination、const char * source、size_t num)   );

     

文字列から文字をコピーするsourceの最初のnum文字をコピーします。   行き先へ。ソースC文字列の終わり(これは、   ヌル文字によって)が見つかった場合、num文字がコピーされる前に、   合計文字数が0になるまで、宛先に0が埋め込まれます。   それに書かれている。

したがって、ソース文字列の長さは宛先バッファサイズのサイズよりも小さくなりますが、strncpyの動作を通過するため、残りの文字を宛先バッファサイズを超えてオーバーレイしようとします。セグメンテーションフォルト

100文字を超えてコピーするのではなく、サイズは宛先バッファの最大許容サイズと等しくなければなりません。

strncpy(s1.from_str,src,sizeof(s1.from_str)/sizeof(s1.from_str[0]) - 1); Actual size -1 to accomodate the null terminator 

_countof マクロを書く方がよいでしょう

#define _countof(s) (sizeof(s)/sizeof(s[0]))
................
strncpy(s1.from_str,src,_countof(s1.from_str) - 1);
5
追加された
@HimanshuGupta:答えが助けられたなら、upvotingして答えを受け入れる
追加された 著者 Abhijit,
@HimanshuGupta:割り当てられたメモリを超えた書き込みは未定義の動作(UB)であり、セグメンテーションフォルトは多くのUBの1つですあなたの鼻から飛んでくる悪魔を含む
追加された 著者 Abhijit,
@HimanshuGupta:ボンネットの下で何が起こるかは、strncpyがコピーされた場所の後ろにあるものを上書きしていることです。あなたが定義した変数、コンパイラがメモリ内のデータをレイアウトする方法、使用されていないことがわかっているためにいくつかの変数を削除する場合(最適化フラグに依存する場合)...、最後に上書きされたもの(戻りアドレス?)とそこに書き込まれたバイトは、(戻りアドレスの場合、不正な命令アドレスであるか、深いdo-doの真ん中にあるか、または無害であるかのように)解釈されます。
追加された 著者 vonbrand,
このsegフォールトはあるLinuxマシンで起こっていますが、別のUNIXマシンでは起こっていません。なぜそうなのか ?
追加された 著者 Himanshu Gupta,
thnx @Abhijit ..
追加された 著者 Himanshu Gupta,

See: http://www.manpagez.com/man/3/strncpy/

stpncpy()関数とstrncpy()関数は、s2から最大n文字をコピーします      s1に入れる。 s2がn文字未満の場合、s1の残りは      `\ 0 '文字で埋められます。それ以外の場合、s1は終了しません。

残りは満たされています....

そう:

strncpy( s1.from_str, src, 10 );
1
追加された
@icepack from_strの後の90バイト(おそらくあなたには属していないメモリ)が上書きされるからです。そしてそれはコンパイラの設定/ OSに依存します。 ...何が起こるか
追加された 著者 Mario The Spoon,
これはどうして質問に関係するのですか?
追加された 著者 SomeWittyUsername,
それは正しいですが、答えはこれを指定するものではなく、リファレンスからの単なるコピーです
追加された 著者 SomeWittyUsername,
私はちょっとばかげている。これはLinuxマシンで起こっていますが、私は別のUNIXマシンでsegフォルトを取得していません。
追加された 著者 Himanshu Gupta,
Thnx @icepack ..
追加された 著者 Himanshu Gupta,
Thnx @Marioスプーン
追加された 著者 Himanshu Gupta,
src文字列のサイズはdest文字列のサイズより小さく、nはsrc文字列とdest文字列のサイズよりも大きい。
追加された 著者 Himanshu Gupta,

strncpy(s1.from_str、src、100);

なぜ関数内で 100 を使用しているのですか?from_strとsrcの両方に10バイトの連続バイトが割り当てられていますが、100バイトをコピーしています。失敗。

このように使用すると、

strncpy(s1.from_str、src、10);

0
追加された
これにより未定義動作が発生するため、他のLinuxマシンでseg faultが発生することはありませんが、 strcpy を使用する方が良いでしょう。
追加された 著者 Adeel Ahmed,
私はちょっとばかげている。これはLinuxマシンで起こっていますが、私は別のUNIXマシンでsegフォルトを取得していません。
追加された 著者 Himanshu Gupta,
Thnx @Adeel Ahmed
追加された 著者 Himanshu Gupta,