質問: セグメンテーションフォールトにつながる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) ``` なぜこのセグメンテーションフォールトがここで起きているのですか? 誰でも私をここで助けることができますか?
回答: 私はあなたにマン・ページ、ウェブサイトなどを指摘することができますが、最終的に重要なのは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の動的文字列に対してこれを行う方法は、これまでの使用法を参照してください。