2009-09-23 5 views
6

次のコードでは、文字列をchar * strにコピーします。長さは10文字で、strncpy()を使用しています。文字列はNULLで終了しませんが、それでも正常に動作します、なぜですか?

strncpy()マニュアルによると、 "警告:srcの最初のnバイトの間にヌルバイトがない場合、destに配置された文字列はnullで終了しません"。

ソース文字列の長さが26文字で、10文字をコピーしているため、NULL文字はstrの最後に配置されません。

しかし、私が '\ 0'を得るまで、0から始まるstrの内容を出力するとき、それは正常に動作します。

なぜですか?最後に '\ 0'がない場合、ループはなぜ正しい場所に停止しますか?

私が理解しているのは、「セグメンテーションフォールト」を与えなければならないということです。少なくとも、そこで停止してゴミ値を残してはいけません。ここで

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#define SIZE 10 

int main() 
{ 
    char *str ; 
    str = malloc(sizeof(char) * SIZE); 
    if(str == NULL) 
     exit(1); 
    memset(str, 0, sizeof(char) * SIZE); 

    strncpy(str, "abcdefghijklmnopqrstuvwxyz", sizeof(char) * SIZE); 

    unsigned int index; 
    for(index = 0; str[ index ] != '\0' ; index++) { 
     printf("str[ %u ] has got : %c \n ", index, str[ index ]); 
    } 

    return 0; 
} 

が出力されます。

 
str[ 0 ] has got : a 
str[ 1 ] has got : b 
str[ 2 ] has got : c 
str[ 3 ] has got : d 
str[ 4 ] has got : e 
str[ 5 ] has got : f 
str[ 6 ] has got : g 
str[ 7 ] has got : h 
str[ 8 ] has got : i 
str[ 9 ] has got : j 

任意の助けが理解されるであろう。

EDIT

は、文字列は「\ 0」かで終わるかどうかを確認するための適切な方法はありますか?私はいつも上記のループが究極のテストであると考えましたが、今はそうではないようです。

他のプログラマーが開発した関数から文字列を取得するとします。今、「\ 0」で正しい場所で終わることをどのように知るでしょうか。そうでないかもしれませんが、それは実際のサイズを超えて、いくらかの '\ 0'が得られるまでです。文字列の実際のサイズは決してわかりません。

どのようにこのような状況に取り組んでいますか?

提案がありますか?

+2

sizeof(char)is * always * 1 !! – paxdiablo

+1

@Pax:それでも、それについての議論があります:http://stackoverflow.com/questions/1011806/is-it-necessary-to-multiply-by-sizeof-char-when-manipulating-memory – sharptooth

+0

strncmpはしかし文字列のサイズではないので、そこで使うのは間違いです。 –

答えて

6

、私は、杓子定規であることはいくつかの問題を解明に役立つと思います。

Cでは文字列のようなものはありません。 "C string"という概念があります。これは、C標準ライブラリが動作するもので、NULで終わる文字列以外のものとして定義されているため、実際には "nullで終わらない文字列あなたの質問は "任意の文字バッファが有効なC文字列であるかどうかをどのように判断できますか?"または「見つかった文字列が目的の文字列であるかどうかを確認する方法」

最初の質問に対する答えは残念ながら、NULバイトが発生するまでバッファを直線的にスキャンすることです。これはCの文字列の長さを与えます。

第2の質問には簡単な答えがありません。実際、Cには長さのメタデータを持つ実際の文字列型(または関数呼び出しで配列のサイズを持ち歩く能力)がないため、上で決定した文字列の長さが長さであるかどうかを判断する実際の方法はありません意図した文字列。私たちがプログラム内のsegfaultsや出力の "garbage"を見始めるのは明らかでしょうが、一般的に最初のNULバイトまでスキャンすることでストリング操作をしています(通常、文字通り長さが上がらないようにバッファオーバーランエラー)

15

ちょうど、割り当てられたブロックの終わりを超えてヌルバイトがあることが起こります。

ほとんどのmalloc()より多くのメモリを割り当て、いわゆるガード値はnullバイトを含むように起こるのか、それが後でfree()が使用するいくつかのメタデータを置き、このメタデータは、その位置でのNULLバイトの権利を含むように起こるを置きます。

とにかくこの動作に頼るべきではありません。 null文字の位置も合法的に割り当てられるように、ヌル文字に対してもう1バイトを要求する必要があります(malloc())。

文字列がヌルターミネーションであるかどうかをテストするポータブルな方法はありません。あなたが割り当てられたブロックの終わりを過ぎると、あなたのプログラムはちょうどクラッシュすることがあります。または、ブロックの終わりを超えてヌル文字があり、誤って解釈された文字列を操作するときに、ブロックの終わりを超えてメモリを上書きすることがあります。

は、指定されたアドレスが割り当てられているかどうかをチェックする機能が必要です(別のアドレスと同じ割り当てに属している可能性があります)。これは遅くて価値がないので、これを行うための標準的な方法はありません。

つまり、ヌルで終了する文字列に遭遇したが、実際にはそれほど時間が掛からない場合、プログラムは未定義の動作になります。

+0

いいえ、ありません。 –

+0

はい、文字列の最後にヌルバイトが発生します。異なるサイズを試してみると、* bad *出力が得られます。 –

+0

したがって、文字列がヌル終了しているかどうかを確認する標準的な方法はありません。それは悪いニュースです。 私は、アプリケーションで作業しているすべてのプログラマーがいくつかの標準で合意しなければならないと思います。ポインタの最初の3文字のように、そのサイズを伝え、4文字目から実際の文字列が始まります。 –

4

なぜ機能しますか?

割り振られるメモリには、正しい場所に'\0'バイトが割り当てられます。たとえば、デバッグモードでVisual C++を使用している場合、ヒープマネージャーはメモリを割り当ててからプログラムに渡しますが、純粋な運にもなります)。

文字列が'\0'で終了するかどうかを確認する適切な方法はありますか?

いいえストリングはゼロ終端(C std lib文字列処理関数が期待するもの)であるか、余分な変数で長さを持ち歩く必要があります。 2人のうちのどちらもいない場合は、バグがあります。

他のプログラマーによって開発された関数の文字列の一部が、正しい場所で'\0'で終了することがわかりました。そうでないかもしれませんが、それは実際のサイズを超えて、いくらかが得られるまで'\0'になります。文字列の実際のサイズは決してわかりません。

どのようにこのような状況に取り組んでいますか?

できません。他の機能がそれを悪くしてしまうと、それは悪いことになります。

+0

メモリをゼロにするヒープマネージャについて:Microsoftコンパイラはメモリをゼロにしません(n個のデバッグまたはリリースビルド)。デバッグヒープを使用すると、MSVCランタイムは割り当てられたメモリをゼロではなく0xCDバイトで埋めます。メモリをクリアするのではなく、「ゴミ」を充填することは、通常、問題を見つける上でより効果的です。また、アロケーションの前後のメモリの一部は0xFD値で埋められます。 http://stackoverflow.com/questions/370195/when-and-why-will-an-os-initialise-memory-to-0xcd-0xdd-etc-on-malloc-free-new/370362#370362 –

+0

@マイケル:私が知っているすべてのことについて、あなたは正しいかもしれない。しかし、ISTRは、デバッグバージョンがVCで動作している間に、リリースバージョンがクラッシュする典型的な原因です。 '' – sbi

0

Sharptoothが動作の原因を説明しているので、それを繰り返すつもりはありません。バッファを割り当てる場合

、私はいつもオーバー割り当てるバイトで、このよう:あなたの編集については

#define SIZE 10 
char* buf = malloc(sizeof(char)*(SIZE+1)); 
/* error-check the malloc call here */ 
buf[SIZE] = '\0'; 
+0

「sizeof(char) - (SIZE + 1)」?マイナス? –

+0

我々もこれを行うことができます memet(dest、0、SIZE); strncpy(dest、source、SIZE -1); このようにして、最後のバイトはゼロになります。 –

+0

これは* - 回でなければなりません。新しいキーボード:) – gnud

0

割り当て領域を超えてゼロを持つことは幸いです。

他のすべてのプラットフォームでこのコードを試してみると、同じように動作しないことがあります。

0

シャープトゥースの答えが正しいと思います。割り当てられる領域がさらにあります。私は次のようにプログラムを変更:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#define SIZE 10 

int main() 
{ 
    char *str ; 
    int *p; 
    int actual_length; 
    str = malloc(sizeof(char) * SIZE); 
    if(str == NULL) 
     exit(1); 

    actual_length = (int)*(str - 4) - 1 - 4; 
    printf("actual length of str is %d\n", actual_length); 
    p = (int*) malloc(sizeof(int)); 
    if (p == NULL) exit(1); 
    *p = -1; 
    char* pc = (char*)(p - 1); 
    pc [0] = 'z'; 
    pc [1] = 'z'; 
    pc [2] = 'z'; 
    pc [3] = 'z'; 

    memset(str, 0, sizeof(char) * SIZE); 

    memcpy(str, "abcdefghijklmnopqrstuvwxyz", sizeof(char) * SIZE); 

    int i; 
    for (i = SIZE; i < actual_length; i++) 
    str[i] = 'y'; 

    unsigned int index; 
    for(index = 0; str[ index ] != '\0' ; index++) { 
     printf("str[ %u ] has got : %c \n ", index, str[ index ]); 
    } 

    return 0; 
} 

出力は

actual length of str is 12 
str[ 0 ] has got : a 
str[ 1 ] has got : b 
str[ 2 ] has got : c 
str[ 3 ] has got : d 
str[ 4 ] has got : e 
str[ 5 ] has got : f 
str[ 6 ] has got : g 
str[ 7 ] has got : h 
str[ 8 ] has got : i 
str[ 9 ] has got : j 
str[ 10 ] has got : y 
str[ 11 ] has got : y 
str[ 12 ] has got : z 
str[ 13 ] has got : z 
str[ 14 ] has got : z 
str[ 15 ] has got : z 
str[ 16 ] has got : \377 
str[ 17 ] has got : \377 
str[ 18 ] has got : \377 
str[ 19 ] has got : \377 

私のOSはDebianのスクイズ/ SIDです。

関連する問題