2016-08-30 9 views
0

私は学習の初心者です。セグメンテーションは何度も起こります。私はまた、セグメンテーションフォールトに関するオンライン調査をいくつか行った:いくつかの理由は、メモリ問題、ヌルポインタ、またはメモリアクセスの問題を割り当てることです。しかし、なぜコードがうまくいかないのか分かりませんが、時にはセグメント違反と言われることもあります。以下は、私は両方insertAtdestroyList機能でこの問題を取得していますコードです:セグメンテーションフォルトの修正Cで時々

#include <stdio.h> 
#include <stdlib.h> 

typedef struct NODE{ 
    int data; 
    struct NODE* next; 
} node; 

node* insertAt(node*, int, int); 
void printList(node*); 
void destroyList(node*); 

node* myList; 
int counter = -1; 

int main() 
{ 
    myList = NULL; 
    int pos, input; 

    myList = insertAt(myList, 0, 333); 
    myList = insertAt(myList, 0, 555); 
    myList = insertAt(myList, 1, 222); 
    myList = insertAt(myList, 3, 444); 

    printf("My List:\n"); 
    printList(myList); 

    destroyList(myList); 

    printf("After Destroy:\n"); 
    printList(myList); 

    return 0; 
} 

node* insertAt(node* head, int pos, int newData) 
{ 
    node* temp = (node*) malloc(sizeof(node)); 
    temp->data = newData; 
    counter++; 

    if(head == NULL){ 
     head = temp; 
     return head; 

    }else if (pos == 0) 
    { 
     temp->next = head; 
     head = temp; 
     return head; 

    }else if(head != NULL && pos > counter){ 
     node* current = head; 
     node* temp2 = current; 
     while(current != NULL){ 
      temp2 = current; 
      current = current->next; 
     } 
     temp->next = current; 
     temp2->next = temp; 
     return head; 

    }else 
    { 
     node* current = head; 
     while(pos-1>0){ 
      current = current->next; 
      pos--; 
     } 
     temp->next = current->next; 
     current->next = temp; 
     return head; 
    } 
} 

void printList(node* head) 
{ 
    node* ptr = head; 

    while (ptr != NULL) { 
     printf("%i ", ptr->data); 
     ptr = ptr->next; 
    } 
    printf("\n"); 
} 

void destroyList() 
{ 
    node* temp; 
    while(myList){ 
     temp = myList; 
     myList = temp->next; 
     free(temp); 
    } 
} 
+0

あなたは非常に奇妙な部門を持っています... –

+0

gdbを使ってデバッグしようとすると – Dave

+0

グローバル変数 'counter'は* next *リストの挿入のためにリセットされません。それを 'insertAt'のローカル変数にして、関数の先頭でそれを初期化します。完全に必要でない限り、グローバル変数は使用しないでください。 –

答えて

0

はこのように、GDBの下であなたのプログラムを実行します。

のスポットを示し、バックトレースを出力します
$ gdb ./a.out 
(gdb) run 
... 
Segmentation fault 
(gdb) bt 

をフォールトを引き起こしたコードとそれを呼び出す関数ライブラリ機能でセグメンテーションが発生した場合は、コードに到達するまでバックトレースを見続け、そこで修正できるものを確認してください。

+3

それは良いアドバイスですが、本当はその質問に対する答えではありません。 –

+0

はい、答えです。これは、あなたのプログラムがなぜsegfaultingするのかの答えを見つけるのに使うツールです。 – Chad

+2

私はそれが「なぜこのプログラムは時々セグメンテーションをするのですか?」と解釈しているので、質問*への回答*ではありません。あなたのアドバイスは答えより良いコメントをするでしょう。 –

1

私はセグメンテーションフォルトについてオンラインでいくつかのリサーチを行いました。いくつかの理由で、メモリの問題、ヌルポインタ、またはメモリアクセスの問題が割り当てられています。

は常には、あなたのプログラムがそれに属していないメモリにアクセスしようとしたことを示し、かなりセグメンテーションフォールトは、かの方法でそれにアクセスすることを許可されていません。あなたは、プログラムがそれを行うかもしれない様々な方法に分解することができますが、一般的な規則は、有効なポインタだけを逆参照し、変更可能なデータだけを修正しようとすることです。

しかし、なぜコードが動作するのですが、時にはセグメンテーションフォールトと呼ばれるのは混乱していますか?

Cでは、特定の動作がセグメンテーションフォルトを生成することを指定していません。この規格には、「セグメンテーション・フォールト」という用語も含まれていません。しかし、「未定義の振る舞い」について言及しています。これは、Cおよびその標準ライブラリのセマンティックルールに従わないコードを実行すると得られるものです。

セグメンテーションフォールトは、多くのシステムで未定義の動作が発生する可能性がありますが、Cの範囲外です。 Cは、特定の状況下では未定義の振る舞いの特定の形を約束しません。その振る舞いは定義されていて未定義ではありません。これは、プログラマが意図した動作であっても、未定義の動作の他の形式の1つが見えることになります。それは実際に時々見られる。

さらに、特定の入力のような特定の条件の下でのみ、特定のプログラムが未定義の動作(セグメンテーションフォルトに現れる可能性があります)を持つ場合があります。

どんな場合でも、あなたのプログラムは時には、(あなたが判断できる限り)意図したとおりに動作しても、未定義の動作がないことを証明するものではありません。


特定のコードについては、定義されていない動作を示すことがあるいくつかの欠陥があります。その中には:

  • あなたはそれがNULLであるかどうかを確認せずにmalloc()の戻り値を使用します。 malloc()は、NULLを返すことによってメモリ割り当てに失敗したことを通知します。後で逆参照しようとすると、未定義の動作が呼び出されます。

  • それがリストに最初のノードを挿入し、それがリストの最後にノードを追加する場合、insertAt()は不定値でそれを残して、新しいノードのnextポインタの設定に失敗しました。後で関数がリストをたどると、不定値が評価され、未定義の動作が生成されます。実際には、不確定値がNULLポインタ値であると判明した場合、予期した動作をする可能性があります。それは決して保証されませんが、それはまったく起こりそうもありません。

  • あなたmain()機能は、それがprintList()に、その後、無効myListポインタを渡すことによって、destroyList()に割り当てが解除された後のリストを印刷しようとします。

1

ツールはValgrindです。それはあなたのためにあらゆる種類の隠された記憶の問題を見つけるでしょう。

たとえば、このコードは「機能します」。

$ cat test.c 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

int main() 
{ 
    char *string = calloc(3, sizeof(char)); 

    strcpy(string, "foo"); 
    printf("%s\n", string); 

    free(string); 

    return 0; 
} 

しかし、Valgrindは微妙なoff-by-oneメモリエラーを検出します。

$ make 
cc -Wall -g test.c -o test 
$ ./test 
foo 
$ valgrind ./test 
==62034== Memcheck, a memory error detector 
==62034== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al. 
==62034== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info 
==62034== Command: ./test 
==62034== 
==62034== Invalid write of size 1 
==62034== at 0x10043B5C0: _platform_memmove$VARIANT$Nehalem (in /usr/lib/system/libsystem_platform.dylib) 
==62034== by 0x1001B8421: stpcpy (in /usr/lib/system/libsystem_c.dylib) 
==62034== by 0x10022BBED: __strcpy_chk (in /usr/lib/system/libsystem_c.dylib) 
==62034== by 0x100000F2C: main (test.c:9) 
==62034== Address 0x100a8f6d3 is 0 bytes after a block of size 3 alloc'd 
==62034== at 0x10000A1B9: calloc (vg_replace_malloc.c:715) 
==62034== by 0x100000F11: main (test.c:7) 
==62034== 
==62034== Invalid read of size 1 
==62034== at 0x10000B2C8: strlen (vg_replace_strmem.c:470) 
==62034== by 0x1001EDA4B: __vfprintf (in /usr/lib/system/libsystem_c.dylib) 
==62034== by 0x1002166C0: __v2printf (in /usr/lib/system/libsystem_c.dylib) 
==62034== by 0x1001EC381: vfprintf_l (in /usr/lib/system/libsystem_c.dylib) 
==62034== by 0x1001EA21B: printf (in /usr/lib/system/libsystem_c.dylib) 
==62034== by 0x100000F42: main (test.c:10) 
==62034== Address 0x100a8f6d3 is 0 bytes after a block of size 3 alloc'd 
==62034== at 0x10000A1B9: calloc (vg_replace_malloc.c:715) 
==62034== by 0x100000F11: main (test.c:7) 
==62034== 
foo 
==62034== 
==62034== HEAP SUMMARY: 
==62034==  in use at exit: 26,553 bytes in 188 blocks 
==62034== total heap usage: 273 allocs, 85 frees, 32,788 bytes allocated 
==62034== 
==62034== LEAK SUMMARY: 
==62034== definitely lost: 0 bytes in 0 blocks 
==62034== indirectly lost: 0 bytes in 0 blocks 
==62034==  possibly lost: 0 bytes in 0 blocks 
==62034== still reachable: 0 bytes in 0 blocks 
==62034==   suppressed: 26,553 bytes in 188 blocks 
==62034== 
==62034== For counts of detected and suppressed errors, rerun with: -v 
==62034== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 19 from 19) 

摂取するのは大変ですが、重要な行はtest.cとその真上です。最初のメッセージを見てみると...

==62034== Invalid write of size 1 
==62034== at 0x10043B5C0: _platform_memmove$VARIANT$Nehalem (in /usr/lib/system/libsystem_platform.dylib) 
==62034== by 0x1001B8421: stpcpy (in /usr/lib/system/libsystem_c.dylib) 
==62034== by 0x10022BBED: __strcpy_chk (in /usr/lib/system/libsystem_c.dylib) 
==62034== by 0x100000F2C: main (test.c:9) 
==62034== Address 0x100a8f6d3 is 0 bytes after a block of size 3 alloc'd 
==62034== at 0x10000A1B9: calloc (vg_replace_malloc.c:715) 
==62034== by 0x100000F11: main (test.c:7) 

これは、関数のそれにつながる呼び出し、それを引き起こした可能性があるその後、任意の関連するエラーのスタックに続いて、エラーメッセージ、です。

サイズ1の書き込みが無効です。」は、割り当てられたメモリから1バイト分歩いたことを示しています。 by 0x100000F2C: main (test.c:9)は、それがstrcpyであるtest.cの9行目で起こったとし、コールチェーンの上記の行で確認しています(はおそらくstpcpyのマクロなのでstpcpyと表示されます)。

"アドレス0x100a8f6d3はサイズ3のブロックの後に0バイトです。"はエラーがおそらく誤ったメモリ割り当ての結果であることを示しています。 by 0x100000F11: main (test.c:7)は、それがの次の呼び出しで確認されたcallocコールであるtest.c行7にあるとします。

これはすべて、私が必要としていたよりも1バイト少ないバイトを割り当てたと言います。 C文字列には末尾のヌル文字があるので、3バイト文字列には4バイト必要です。

(P.S. strcpyを使用しないでください。)

しかし、私は混乱していますなぜ、時にはコードの仕事、時にはそれがセグメンテーションフォールトを言うこと?

これは、Cを使用すると、任意のメモリを自由に書き換えられるためです。現代のオペレーティングシステムは、少なくとも自分のプロセスのメモリ内にあなたを保ちます。あなたが何か重要なものを上書きしたり、メモリに読み書きしようとすると、あなたがするべきではない...またはあなたがラッキーになることがあるなら、これはあらゆる種類の問題を引き起こす可能性があります!メモリは毎回わずかに異なるように割り当てられているので、メモリの問題を伴うプログラムの実行ごとに動作が異なる場合があります。

上記の私のコード例は、無効なメモリに書き込みを行っても機能します。それは単なる1バイトなので、多分それは幸運になり、誰も気にしないところに書きました。または、おそらくcallocは必要以上のメモリを割り当てられました。

ポイントは、valgrind、または他のメモリチェッカーを学び、実行して、それが破棄されたことを修正します。

(PS valgrindのためにあなたのOSパッケージをインストールします。オペレーティングシステムのバグや癖のために微調整されます。そうしないと、オペレーティングシステムの独自のコードに関する警告のすべての種類を取得する可能性があります。)

+0

Valgrindは適切なツールです。ただし、関数実装は質問に表示されます。iframe内をスクロールするだけです。 –

+0

@JohnBollinger D'oh! – Schwern

関連する問題