• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:構造体のリスト削除)

セグメントエラーが発生するリスト削除プログラムの修正方法について

このQ&Aのポイント
  • セグメントエラーが発生するリスト削除プログラムの修正方法を教えてください。
  • 関数delete()を修正し、セグメントエラーが発生しないようにしてください。
  • 修正後のプログラムを使ってリスト削除を実行した結果、正しく動作することを確認してください。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.2

これを前提知識なしで出されたら、出題者が悪いと思いますが、そうでなかったらちゃんと授業を聞いていたかと問いただしたくなります。リストの削除の方法って授業で説明しませんでしたか? 他の人が書いたプログラムというのも勉強になると思いますので一応説明します。 deleteという操作は、リストから該当する要素を発見し、その要素を取り除くという操作で行います。 言い換えると、発見するというプログラムと取り除くというプログラムの組み合わせです。 まず、リストから所定の要素を探しだすというプログラムを書きましょう。 それは、こういうコードになるはずです。 struct kara * find(struct kara *start, struct kara *to_find) { struct kara* cur; for(cur = start;cur != NULL;cur = cur->next) { if (cur == to_find) { printf("found %s %d\n", cur->name, cur->age); return cur; } } return NULL; } このプログラムはto_findに探したい要素のポインターを入れると、それを探しだして、printfで内容を表示した上でそれを返します。 次に、リストからの要素の削除です。 リストから要素を削除するという操作は、自分がいなかったかのようにポインターを付け替えるという操作です。 例えば、次のよなリストでbを消すことを考えます。 a -> b -> c この場合、bが消えたあとの状態はこうなります。 a -> c これは、bの指している先であるcをaの指し先に変更するということです。 言い換えると、bのprevのnextをbのnextにするということです。 よって、プログラムでやると b->prev->next = b->nextとなります。 また、prevについても同じ事をするのでb->next->prev = b->prevとなります。 ただし、リストの一番最後の要素に対して削除をする場合、b->nextは存在しないのでb->next->prev = b->prevの動作をする必要はありません。 まとめると、 if (b->prev != NULL) { b->prev->next = b->next; } if (b->next != NULL) { b->next->prev = b->prev; } となります。ただ、削除対象がstartだったとき b->prev == NULLのだけちょっとしたトリックが必要です。 この部分を実際のプログラムに起こすと書くとこんなかんじです。 struct kara * delete (struct kara *start,struct kara *p) { struct kara* to_delete = find(start, p); if (to_delete != NULL) { if (to_delete->prev != NULL) to_delete->prev->next = to_delete->next; else /* head of the list. */ start = to_delete->next; if (to_delete->next != NULL) to_delete->next->prev = to_delete->prev; } return start; } 以上でdelete関数が出来上がりました。 余談ですが、最後の要素を毎回手繰るというのは無駄な操作なので、こういうことをしたい場合は普通はtailqというtailを指すポインタを持つ両方向リンクリストを使うのが普通だと思います。 ついでに余談ですが、リスト操作はコンピュータサイエンスの基礎知識ではありますが、基本的にやることは決まっているので、queue.hなどを使って書くのが普通だと思います。 ちょろっと調べたら、そのtailqの内部構造を解説したページを見つけたので参考までに。 http://d.hatena.ne.jp/koseki2/20120105/tailq もう一つ余談を言うと、セグメントエラーが出るようなプログラムで動きを確かめるにはgdbを使うと良いです。gdbを使うと、どこでセグメントエラーが出ているか一瞬でわかるので、とりあえず治すのは格段に早くなります。 今回の場合、gdbを使うとstart = p->next->next->next;で出ていると一発でわかります。まぁ、わかったところで上記のようなコードを書かないと動かないのですが。 では、頑張って。

izupawapuro
質問者

補足

説明はまったくもってないです。簡単な構造体の作り方のみしか教わりません。 こういった応用問題は独学で学ぶしかないので、こういった頼りが必要になってしまいます。先生に聞いても答えてくれはくれないので。 少し参考にして自分なのにdelete関数を作ってみます。

その他の回答 (1)

  • Wr5
  • ベストアンサー率53% (2173/4061)
回答No.1

リンク構造、ちゃんと理解していない…ってコトでしょうな。 start = delete(start, p); struct kara *delete (struct kara *start,struct kara *p) ということは…startからpを探して、リンクのつなぎ替えを実施しさらにその先頭を返却する。 ということになります。 で、 >ただし、データが先頭であっても動作しなければならない。 ということなので条件分岐が必要…ですな。 まず… startとpが同じか?で分岐。 同じだったら先頭を取り除くので、 startの次へ進めます。 NULL(つまりリストにはstartしかない)であれば戻り値はNULLで、非NULLならstart(またはp)をリンクから外すためにprevをNULLにします。 # リスト構造としてはstartのnextもNULLにすべき…でしょうが……。 startとpが異なる場合はstartからリストを辿ってpを検索、 pの次の要素のprevにpが持っているprevをコピー、pの前の要素のnextにpが持っているnextをコピーしてリンクのつなぎ替えを行います。 # それぞれ、絵に描いてどういうつながりになるのか確認してみるといいでしょう。 pをリンクから外した後で先頭の要素のアドレスを返す。 と……。 まぁ、もう少し効率のいい方法もありますが。 # pは判っているのだからstartから検索する必要はない…よね。と…。 ちなみに… >struct kara * >delete (struct kara *start,struct kara *p) >{ > for(p = start;p != NULL;p = p->next) こんなコトしたら、削除対象のpのアドレスがいきなり不明になります。

izupawapuro
質問者

補足

こういった問題を説明なしに出されるので、なんともいえません。 教えてくれるのはほんの一部で 構造体の簡単な作り方ー!みたいな感じで教えてくれるだけで 本質は課題として出されて、まったく構造体のポインタなどの説明がないんですよね。 最後のはよくわからないのでやってしまいました。 そもそもこのプログラムの前半自体は先生が書いたもので 自分で書いたものではないので、3回表示するんだろうな、くらいで 後は問題通りに何か色々やってみた結果です。

関連するQ&A