2016-04-19 11 views
0

多くの以前の投稿が示すように、私はクレイジー8のカードゲームをシミュレートするコードを作成しています。私は再生されているデッキからカードを削除するためのノード削除機能を持っています。それは最初のカードの後に​​動作しますが、リストから最初のカード(ノード)を削除しようとするたびに、それは削除されず、その後にプログラム全体が壊れてしまいます。ここに関数があります:cノードの最初の位置からノードを削除中にエラーが発生しました

void deleteNode(card *head, int coordinate) { 

    card *current = head; 
    card *temp = NULL; 
    temp = current; 
    int count = 1; 
    while (head != NULL) { 
     if (coordinate == 0) { 
      current = current->listp; 
      free(temp); 
      break; 
     } 
     else if (count == coordinate) { 
      temp = current->listp; 
      current->listp = current->listp->listp; 
      free(temp); 
      break; 
     } 
     else { 
      count++; 
      current = current->listp; 
     } 
    } 
} 

*渡された*頭は、再生されている手の先頭です。座標は、ユーザが再生したいカードの数である。例えば、彼らのデッキの最初のカードがQのHeartsであり、それが彼らがプレイしたいものなら、彼らは1つを入力するでしょう。関数呼び出しでは、ユーザーの選択肢から1つを引いて、リストと一致するようにします(リストは0から始まるので)。どんな助けもありがとう。私はこの問題が解決するまで私のプロジェクトを進めることはできません!

+2

最初のノードを削除するとリストのアドレスが変更されます(リストアドレスは最初のノードのアドレス)。 –

+0

head == nullはいつですか? – Pemdas

答えて

3

コメントに記載されているように、機能にのアドレス-のリストを渡さないために問題が発生しています。問題は基本的なものですが、多くの人を捉えています。リンクされたリストのアドレスは、の最初のノードのアドレスです。したがって、最初のノードを削除するときは、新しいリストアドレスを次のノードアドレスに設定して、リストを引き続き操作する必要があります。

関数にポインタを渡すと、次のようになります。 void deleteNode(card *head, ...の場合、deleteNodeには、headコピーが入ります。コピーにはすべて独自のアドレスがあり、そのアドレスは呼び出し元関数のポインタのアドレスとは関係ありません。ポインターのは、発信者にあるとおり、deleteNodeと同じですが、アドレスはまったく異なります。

deleteNodeの最初のノードを削除すると問題が発生します。メモリはfree 'dであり、deleteNode関数が戻ります。今すぐ呼び出し元に戻って(おそらくmain())、次にhead - bamにアクセスしようとすると! segfault。どうして? headのアドレスはmainに更新されていないので、元のノードを指しています。元のノードのメモリには何をしましたか?deleteNode? (あなたが最初のノードを保持するメモリのブロックを指摘ポインタにfreeと呼ばれる - そのなくなって...)

問題を解決するには、単にdeleteNodeアドレスのリスト(head)を渡します。 (例えば、void deleteNode(card **head, ...)。次に、あなたはのアドレスがhead(例えば、ポインタのポインタのポインタ)で動作しています。最初のノードを削除する前に、*head = head->listp;を設定し、新しいリストアドレスを呼び出し元の関数に反映させます(main())。たとえば、コードは次のように書くことができます。

void delnode (card **head, int coordinate) 
{ 
    card *current = *head; 
    card *victim = NULL; 
    victim = current; 
    int count = 1; 

    while (current != NULL) { 
     if (coordinate == 0) { 
      *head = current->listp; 
      free (victim); 
      break; 
     } 
     else if (count == coordinate) { 
      victim = current->listp; 
      current->listp = current->listp->listp; 
      free (victim); 
      break; 
     } 
     else { 
      count++; 
      current = current->listp; 
     } 
    } 
} 

ただし、最小限の労力で機能のロジックを少し改善することができます。例えば

void delnode (card **head, int coordinate) 
{ 
    card *current = *head; 
    card *victim = current; 
    int count = 1; 

    if (coordinate == 0) { 
     *head = current->listp; 
     free (victim); 
     return; 
    } 

    while (current != NULL) 
    { 
     if (count == coordinate) { 
      victim = current->listp; 
      current->listp = current->listp->listp; 
      free (victim); 
      return; 
     } 
     count++; 
     current = current->listp; 
    } 
} 

最後に、How to Ask a QuestionHow to create a Minimal, Complete, and Verifiable exampleを記述するリンクをご覧ください。あなたのコードや関連するエラーを含む必要な情報を提供することで、皆さんがあなたの質問にお手伝いします。

この質問は完璧な例です。誰かがあなたを助け、実際に問題や答えをコンパイルして確認するために、ここであなたの基礎的なリスト構造が何であるかを知るためのサンプルプログラムを書くように頼んでいます。ここで質問をするとき、MCVEを提供する目的は、他の人があなたのコードをコンパイルして問題を確認し、必要に応じてデバッガでコンパイルされたコードを実行することです。私たちがあなたを助けるのに役立つサイトの最小限の提案やルールに従えば、さらに多くの助けと肯定的な反応が得られます。

これは、サンプルコードのこの小さなビットで削除の操作を確認できます。任意のコードでは

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

typedef struct card { 
    int cardno; 
    struct card *listp; 
} card; 

card *createnode (int c); 
card *insert (card **list, int c); 
void prnlist (card *list); 
void delnode (card **head, int coordinate); 
void dellist (card *list); 
void *xcalloc (size_t nmemb, size_t sz); 

int main (void) { 

    card *list = NULL; 

    insert (&list, 18);  /* insert test nodes */ 
    insert (&list, 6); 
    insert (&list, 54); 
    insert (&list, 12); 
    insert (&list, 60); 
    insert (&list, 30); 

    printf ("\noriginal list:\n"); 
    prnlist (list); 

    printf ("\ndeleting node: 2\ndeleting node: 0\n"); 
    delnode (&list, 2);  /* delete 3rd & 1st nodes */ 
    delnode (&list, 0); 

    printf ("\nfinal list:\n"); 
    prnlist (list); 

    dellist (list);   /* free allocated memory */ 

    return 0; 
} 

card *createnode (int c) 
{ 
    card *node = xcalloc (1, sizeof *node); 

    node->listp = NULL; 
    node->cardno = c; 

    return node; 
} 

card *insert (card **list, int c) 
{ 
    card *iter = *list; 
    card *node = createnode (c); 

    if (!*list) {  /* add 1st node to list */ 
     *list = node; 
     return *list; 
    } 

    /* insert all other nodes at end */ 
    for (; iter->listp; iter = iter->listp) {} 

    iter->listp = node; 

    return *list;  
} 

void prnlist (card *list) 
{ 
    card *iter = list; 
    for (; iter->listp; iter = iter->listp) 
     printf (" cardno : %d\n", iter->cardno); 
    printf (" cardno : %d\n", iter->cardno); 
} 

void delnode (card **head, int coordinate) 
{ 
    card *current = *head; 
    card *victim = current; 
    int count = 1; 

    if (coordinate == 0) { 
     *head = current->listp; 
     free (victim); 
     return; 
    } 

    while (current != NULL) 
    { 
     if (count == coordinate) { 
      victim = current->listp; 
      current->listp = current->listp->listp; 
      free (victim); 
      return; 
     } 
     count++; 
     current = current->listp; 
    } 
} 

void dellist (card *list) 
{ 
    card *iter = list; 
    while (iter) { 
     card *victim = iter; 
     iter = iter->listp; 
     free (victim); 
    } 
} 

void *xcalloc (size_t nmemb, size_t sz) 
{ 
    void *memptr = calloc (nmemb, sz); 

    if (!memptr) { 
     fprintf (stderr, "xcalloc() error: virtual memory exhausted.\n"); 
     exit (EXIT_FAILURE); 
    } 

    return memptr; 
} 

使用例/出力

$ ./bin/lldelcard 

original list: 
cardno : 18 
cardno : 6 
cardno : 54 
cardno : 12 
cardno : 60 
cardno : 30 

deleting node: 2 
deleting node: 0 

final list: 
cardno : 6 
cardno : 12 
cardno : 60 
cardno : 30 

メモリエラーチェック

動的にメモリを割り当て、あなたの書き込みには、割り当てられたメモリの任意のブロックに関する2 responsibilitesを持っています: (1)常にメモリブロックの開始アドレスへのポインタを保持する、(2)不要になったときに解放することができる。

メモリエラーチェックプログラムを使用して、割り振られたメモリブロックの外側または外側に書き込まれていないことを確認し、初期化されていない値の読み込みまたはベースを試み、最後に解放したことを確認することが不可欠ですあなたが割り当てたすべてのメモリ。

Linuxの場合valgrindが通常の選択です。新しいメモリブロックを誤用する方法はたくさんあります。メモリエラーチェッカーを使用すると、問題を特定して、segfaultを介して問題を発見するのではなく、割り当てたメモリの適切な使用を検証できます。すべてのプラットフォームに同様のメモリチェッカーがあります。それらはすべて使いやすく、プログラムを実行するだけです。

$ valgrind ./bin/lldelcard 
==9094== Memcheck, a memory error detector 
==9094== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. 
==9094== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info 
==9094== Command: ./bin/lldelcard 
==9094== 

original list: 
cardno : 18 
cardno : 6 
cardno : 54 
cardno : 12 
cardno : 60 
cardno : 30 

deleting node: 2 
deleting node: 0 

final list: 
cardno : 6 
cardno : 12 
cardno : 60 
cardno : 30 
==9094== 
==9094== HEAP SUMMARY: 
==9094==  in use at exit: 0 bytes in 0 blocks 
==9094== total heap usage: 6 allocs, 6 frees, 96 bytes allocated 
==9094== 
==9094== All heap blocks were freed -- no leaks are possible 
==9094== 
==9094== For counts of detected and suppressed errors, rerun with: -v 
==9094== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1) 

必ず確認すべてのヒープブロックが解放された - 何の漏れがERROR概要可能と同様に重要ではない:0エラーを0コンテキストから。

あなたのコーディングで幸運を祈る。

+0

助けてくれてありがとう!ポインタへのポインタは私がまだ取り組んでいないものですが、このコードを改善するのに役立ちます。 –

+0

喜んで教えてください学習Cは遅い学習プロセスですが、十分に努力する価値があります。ポインタは他のものの値をその値として保持する変数にすぎません(それは間接の1レベルです)。*ポインタへのポインタ*は、ポインタのアドレスを保持する変数以上のものではありません。その値として(間接参照の第2レベルです)単一ポインタ、間接参照、二重ポインタ、間接参照の2つのレベルなど...キーとは、スローダウン、学習を楽しむCを学ぶためにもう少し時間。 –

関連する問題