質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

リストボックス

ユーザーがリストから1つ以上のアイテムを選択できるようにするGUI要素です。

Q&A

解決済

2回答

3200閲覧

[C言語]2つの双方向循環リストをつなげたいです。

apeirogon0813

総合スコア117

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

リストボックス

ユーザーがリストから1つ以上のアイテムを選択できるようにするGUI要素です。

0グッド

0クリップ

投稿2018/10/22 03:57

編集2018/10/22 05:06

前提・実現したいこと

頭(ダミー)がある2つの双方向循環リスト d1, d2においてd1の末尾をd2の先頭につなげ、d2の末尾をd1の先頭つなげたいのですが出力して見たところうまくいっていませんでした。
原因が分からないのでご教示願います。

d1 (2)(4) d2 (1)(3) 結合後 d1 (2)(4)(1)(3)

該当のソースコード(必要ならばすべてのソースコードを添付します)

C

1typedef int elementtype; 2 3struct dlnode { 4 elementtype element; 5 struct dlnode *prev, *next; 6}; 7 8typedef struct dlnode* dllist; 9 10void append_dllist(dllist d1, dllist d2) { 11 dllist n; 12 n = d1; 13 while(n->next != d1) { 14 n = n->next; 15 } 16 n->next = d2; 17 n = n->next; 18 while(n->next != d2) { 19 n = n->next; 20 } 21 n->next = d1; 22 delete(d2); 23 //free(d2);修正 24} 25 26void delete(dllist p) { 27 p->prev->next = p->next; 28 p->next->prev = p->prev; 29 free(p); 30} 31 32//nextで一巡するまで要素を[]で囲って出力し、prevで一巡するまで{}で囲って出力する 33void print_dllist(dllist d) { 34 dllist n; 35 n = d->next; 36 while(n != d) { 37 printf("[%d]", n->element); 38 n = n->next; 39 } 40 n = n->prev; 41 while(n != d) { 42 printf("{%d}", n->element); 43 n = n->prev; 44 } 45 printf("\n"); 46 47} 48 49

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/10/22 04:04

うまくいかなかったというのを具体的に。ソース全体を読みたくないので。。。また、デバッグを試されてみてどうでしたか?
guest

回答2

0

append_dllist 内に delete は不要です。
循環しているので、末尾を探すのに一周する必要もありません。

C

1#include <stdio.h> 2#include <stdlib.h> 3 4typedef int elementtype; 5 6struct dlnode { 7 elementtype element; 8 struct dlnode *prev, *next; 9}; 10 11typedef struct dlnode* dllist; 12 13dllist new_dllist(int element) 14{ 15 dllist result = malloc(sizeof(struct dlnode)); 16 result->element = element; 17 result->prev = result; 18 result->next = result; 19 return result; 20} 21 22void print_dllist(dllist list) 23{ 24 dllist ptr = list; 25 if (list == NULL) return; 26 printf("%s\n", "正順に表示します"); 27 do { 28 printf("%d ", ptr->element); 29 ptr = ptr->next; 30 } while (ptr != list); 31 printf("\n"); 32 printf("%s\n", "逆順に表示します"); 33 do { 34 printf("%d ", ptr->element); 35 ptr = ptr->prev; 36 } while (ptr != list); 37 printf("\n\n"); 38} 39 40void free_dllist(dllist list) 41{ 42 dllist ptr = list; 43 if (list == NULL) return; 44 do { 45 free(ptr); 46 ptr = ptr->next; 47 } while (ptr != list); 48} 49 50void append_dllist(dllist a, dllist b) 51{ 52 if (a == NULL || b == NULL) return; 53 a->prev->next = b; 54 b->prev->next = a; 55 dllist t = a->prev; 56 a->prev = b->prev; 57 b->prev = t; 58} 59 60void main() 61{ 62 dllist list = new_dllist(2); 63 append_dllist(list, new_dllist(4)); 64 65 dllist list2 = new_dllist(1); 66 append_dllist(list2, new_dllist(3)); 67 68 append_dllist(list, list2); 69 print_dllist(list); 70 free_dllist(list); 71}

投稿2018/10/22 07:13

編集2018/10/22 07:14
Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

apeirogon0813

2018/10/22 08:12 編集

Zuishinさんのコードには先頭のダミーは存在しませんのでappendないのdelete()はいらないと思います。 私のソースコードは以下のようで初めにダミーとなるノードを作りそこから繋げているのでdelete()は必要になってくるという次第です。情報が足りてなくて申し訳ございません。 また、循環しているので、末尾を探すのに一周する必要もありません。 これにはとても納得です! do-whille文の方がより淡麗で参考になります。 #include<stdio.h> #include<stdlib.h> typedef int elementtype; struct dlnode { elementtype element; struct dlnode *prev, *next; }; typedef struct dlnode* dllist; void print_dllist(dllist d) { dllist n; n = d->next; while(n != d) { printf("[%d]", n->element); n = n->next; } n = n->prev; while(n != d) { printf("{%d}", n->element); n = n->prev; } printf("\n"); } void insert(dllist p, elementtype e) { dllist n; n = (struct dlnode *) malloc (sizeof (struct dlnode)); n->element = e; if(p->next == NULL && p->prev == NULL) { p->prev = n; n->next = p; n->prev = p; p->next = n; } else { n->next = p; n->prev = p->prev; p->prev->next = n; p->prev = n; } } void delete(dllist p) { p->prev->next = p->next; p->next->prev = p->prev; free(p); } void append_dllist(dllist d1, dllist d2) { dllist n; n = d1; while(n->next != d1) { n = n->next; } n->next = d2; d2->prev = n; n = n->next; while(n->next != d2) { n = n->next; } n->next = d1; d1->prev = n; delete(d2); } int main(void) { int i; char buf[128]; dllist d1, d2, p; d1 = (struct dlnode *)malloc(sizeof(struct dlnode)); d1->prev = NULL; d1->next = NULL; d2 = (struct dlnode *)malloc(sizeof(struct dlnode)); d2->prev = NULL; d2->next = NULL; while(fgets(buf,sizeof(buf),stdin) != NULL) { sscanf(buf,"%d", &i); insert(d1, i); insert(d2, i); } print_dllist(d1); print_dllist(d2); p = d1->next; while(p != d1) { if(p->element % 2) { p = p->next; delete(p->prev); continue; } p = p->next; } p = d2->next; while(p != d2) { if(!(p->element % 2)) { p = p->next; delete(p->prev); continue; } p = p->next; } print_dllist(d1); print_dllist(d2); append_dllist(d1, d2); print_dllist(d1); return 0; }
Zuishin

2018/10/22 08:17

ダミーは何のために必要なんですか?
apeirogon0813

2018/10/22 08:42

課題においての指定なのですが、確かになぜ必要なのかわかりかねます。
Zuishin

2018/10/22 08:48

了解しました。 が、多分ダミーの使い方が間違っていると思います。 もう一度よく課題を確認することをお勧めします。
guest

0

ベストアンサー

このあたりの子が悪さしているように思えます。

C

1delete(d2); 2free(d2); 3 4void delete(dllist p) { 5 p->prev->next = p->next; // (4のノード).next = (3のノード) 6 // つまり、 (2)(4)(1)(3) が、(2)←→(4)←→(3) となります。 7 // ↑ ↑ 8 // (1)---┘ 9 p->next->prev = p->prev; // ここもおかしくなります。 10 free(p); // (1のノード)のメモリを解放する 11}
  • dlistってdlnode*ですよね。ノードだと思うんですが、それを解放しちゃっていいのか?というのがfree(d2)の悪そうな点です。
  • delete()は、その前に接続しなおしの処理があるのに、あらためてやることあるのかな?というのが悪そうな点です。

###変数やメンバ変数と、その中身のイメージ
きったない絵であれですが。

イメージ説明

投稿2018/10/22 04:13

編集2018/10/22 06:15
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

apeirogon0813

2018/10/22 05:05

おっしゃるとおり、二度もfreeする意味はないのでfree(d2)は消しましたが出力結果が [1][3][1][3][1][3][1][3][1][3]... と永遠に繰り返し出力されてしまいます、、
退会済みユーザー

退会済みユーザー

2018/10/22 05:08

freeは一度もやらなくていいと思います。ノードを削除するのはまずいですよね。deleteもいらなくないですか?
apeirogon0813

2018/10/22 05:13

delete()の際そのノードはどこからもたどることができないメモリになり、残ってしまう(メモリリーク)が起こらないようにしているのですがまちがてますでしょうか?
apeirogon0813

2018/10/22 05:14

ちなみに、delete内のfree()を消してみましたが出力は変わらず上記のようになってしまいました
退会済みユーザー

退会済みユーザー

2018/10/22 05:22 編集

いえ、deleteそのものが不要かと。deleteの各行が何をしたいか書き出してみてください。 たぶん、誤解の根本は、d2とその中身を誤解されていることにあります。d2はポインタで、その中身は(1)のアドレスです。なので、free(d2)やfree(p)とすると、その中身のアドレス指すノード、つまり(1)のノードを格納しているメモリが解放されます。ということは、せっかくつなげた(1)が消えてしまいます。
apeirogon0813

2018/10/22 05:34

free()はリスト内のある1つのノード(節点)のメモリを開放することではないのでしょうか? void delete(dllist p) { //pの指すノードの前のノードのnextにpの指すノードの次のノードをポインタする p->prev->next = p->next; //pの指すノードの次のノードのprevにpの指すノードの前のノードをポインタする p->next->prev = p->prev; リストから外されたノードpを開放する free(p); }
退会済みユーザー

退会済みユーザー

2018/10/22 05:40

> free()はリスト内のある1つのノード(節点)のメモリを開放することではないのでしょうか? はい、あっています。なので、ノードが消えてしまいます。今回やりたいことはリストをつなげることであって、なにかノードを消したいわけではないですよね?なので、freeが出てくるのはおかしいです。ちなみに、nextやprevへの代入ですが、これはノードが代入されているのではなく、ノードへの住所であるアドレスが代入(コピー)されいているだけです。住所をコピーしても、家がコピーされないのとおなじです。 ごめんなさい、deleteメソッドの各行で何がしたいかではなく、各行でそれをなぜしたいかを書いて下さい の間違いでした。
apeirogon0813

2018/10/22 05:51

d1, d2の先頭にはそれぞれ頭であるダミーが入っているのでd1とd2をつなげた際、真ん中にd2のダミーが入っていては要素の出力の時に邪魔ですのでfree()でダミーのノードを開放しました。 はい、住所をコピーすることによって二つのリストをつなげているのですが、だめなのでしょうか
退会済みユーザー

退会済みユーザー

2018/10/22 06:01

d2に入っているのはダミーでなく、ノード(1)へのアドレスです。なので、free(d2)とすると、ノード(1)が消えます。コード上で代入しているものはノードでなく全てアドレスである点に注意してください。なので、なにかしらfree()すると、ノード(家)が消えます。 > はい、住所をコピーすることによって二つのリストをつなげているのですが、だめなのでしょうか リストをつなげるのは、append_dllistメソッドの最初からdeleteメソッドの前までできているのでは?deleteであらためてリストをつなげなおすのでおかしくなっているのではないかな?と(ほかにも原因はあるかもしれませんが)
apeirogon0813

2018/10/22 06:53

動かしているのはポインタnであってd2は動かしていないのでd2はd2リストの頭であるダミーへのアドレスを指していると思うので開放すると結果的にd2のダミーのみが開放されると思うのですが。 ちなみにfreeあってもなくてもdelete()しないと以下のように出力されダミーの要素が入ってきてしまいます。 [2][0][1][3]{3}{1}{0}{2} ちなみにappend_dllist()においてprevの方を接続し忘れていたのでそこをつなげるとなぜかうまく出力されました。
退会済みユーザー

退会済みユーザー

2018/10/22 07:03

絵を見ていただければわかるのですが、ダミーとなるノードは存在しません。d2というポインタ変数そのものをダミーというのであれば理解できます。しかし、d2というポインタ変数という箱の中身であるアドレスはダミーではなく、値1のノードを指します。
退会済みユーザー

退会済みユーザー

2018/10/22 07:37

void append_dllist(dllist d1, dllist d2) { dlnode* d1Head = d1; dlnode* d1Tail = d1->prev; dlnode* d2Head = d2; dlnode* d2Tail = d2->prev; // d1の尾とd2の頭をつなぎます d1Tail->next = d2Head; d2Head->prev = d1Tail; // d2の尾とd1の頭をつなぎます d2Tail->next = d1Head; d1Head->prev = d2Tail; }
apeirogon0813

2018/10/22 08:05

すみません私が提示したソースコードだけではダミーのノードが作られているのかわかりませんよね。 ppnさんの絵の場合はたしかにダミーとなるノードは存在せずそのまま繋げるだけなのは賛成です。
退会済みユーザー

退会済みユーザー

2018/10/22 08:11

なるほど、そういうことでしたか。であれば、上のコードを少し変えて、最後にfree(d2)すればいいと思います。
apeirogon0813

2018/10/22 08:13

情報が不足していたため誤解させて申し訳ございませんでした。
退会済みユーザー

退会済みユーザー

2018/10/22 08:20

こんな感じですね。ただ、解放するときはZuishinさんのようにチェックいれたほうがいいです。 void append_dllist(dllist d1, dllist d2) { dlnode* d1Head = d1->next; dlnode* d1Tail = d1->next->prev; dlnode* d2Head = d2->next; dlnode* d2Tail = d2->next->prev; // d1の尾とd2の頭をつなぎます d1Tail->next = d2Head; d2Head->prev = d1Tail; // d2の尾とd1の頭をつなぎます d2Tail->next = d1Head; d1Head->prev = d2Tail; free(d2); }
apeirogon0813

2018/10/22 09:21

ありがとうございます。 参考にします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問