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

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

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

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

Q&A

解決済

5回答

954閲覧

if()文の条件を図にしています。矢印の方向がわかりません。

退会済みユーザー

退会済みユーザー

総合スコア0

C

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

0グッド

0クリップ

投稿2018/01/31 08:15

編集2018/02/13 21:53

void exchange() の中にif (&((*p)->next) == q があります。

イメージ説明
(*p)->next)はポインタpのnextでそのnextに入っている(*p)->next)のアドレスがポインタqのアドレス
と等しいということですよね。
上図でいいでしょうか。
よろしくお願いいたします。

#include <stdlib.h> #include <string.h> #include <assert.h> // #define N 256 #define FILENAME "address.csv" struct address{ char name[N]; char address[N]; char tel[N]; // 電話番号 char mail[N]; struct address *next; struct address *before; }; void data_show(struct address* head); void data_sort(struct address** head); void chop(char *p){ for (; *p; p++) ; p--; while (*p == '\r' || *p == '\n') *(p--) = 0; // } void list_add(struct address **head, char *name, char *address, char *tel, char *mail) { struct address *p; if ((p = malloc(sizeof(struct address))) != 0) { strcpy(p->name, name);  strcpy(p->address, address);  strcpy(p->tel, tel);  strcpy(p->mail, mail); p->next = *head; if (p->next != 0) p->next->before = p; p->before = 0; *head = p; } } void data_show(struct address *head) { if (head != 0) { printf(" %s, %s, %s, %s\n", head->name, head->address, head->tel, head->mail); data_show(head->next); } } struct address **listmin(struct address **head) { struct address **p; if (*head == 0) return 0; if ((*head)->next == 0) return head; // if (strcmp((*head)->name, (*(p = listmin(&((*head)->next))))->name) < 0) return head; else return p; } void exchange(struct address **p, struct address **q) {![![イメージ説明](993302354d80fe7a9b8e9f78d293b8cc.jpeg)](b2aa359925707f49b302e1bc144a7110.jpeg) struct address *r, *s, *t; assert(*p != 0 && *q != 0); if (p == q) return; r = *p; s = *q; if (&((*p)->next) == q ) { if (s->next != 0){ s->next->before = r; } s->before = r->before; r->before = s; *p = s; *q = s->next; s->next = r; return; }else{ //(&((*p)->next) == q) && (&((*q)->next) == p) if (s->next != 0) s->next->before = r; t = s->before; s->before = r->before; if (r->next != 0) r->next->before = s; r->before = t; t = r->next; r->next = s->next; s->next = t; *p = s; *q = r; } } void data_sort(struct address **head) { struct address **p; if (*head != 0) {   for (;;) { p = listmin(head); if (p == 0)break;    exchange(head, p); head = &((*head)->next);  exchange(head, p);  head = &((*head)->next);   } } } void release(struct address **head) { if (*head != 0) { release( &((*head)->next) ); free(*head); *head = 0; } } int main() { struct address *head; FILE* fp; static char buff[N], name[N], address[N], tel[N], mail[N]; char *token=","; head = 0; if ((fp = fopen(FILENAME,"r")) != 0) { while(fgets(buff, N, fp) != 0){ //本当の大元の文字列を書き換えないようにするために //bufを確保してコピーし、それをstrtok()の引数にしている。 char *p; chop(buff); printf( "ファイルから読んだ文字列:%s\n", buff ); p = strtok(buff, token); if ( p != NULL ) { strcpy(name, p); } else { printf( "氏名の切り出しに失敗しました。\n"); break; } p = strtok(NULL, token); if ( p != NULL ) { strcpy(address, p); } else { printf( "住所の切り出しに失敗しました。\n"); break; } p = strtok(NULL, token); if ( p != NULL ) { strcpy(tel, p); } else { printf( "電話番号の切り出しに失敗しました。\n"); break; } p = strtok(NULL, token); if ( p != NULL ) { strcpy(mail, p); } else { printf( "メールアドレスの切り出しに失敗しました。\n"); break; } list_add(&head, name, address, tel, mail); } fclose(fp); } data_sort(&head); data_show(head); release(&head); return 0; }

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

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

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

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

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

yohhoy

2018/01/31 08:27

質問文だけでは情報が不足しており、期待する回答が得られないように思います。変数p, qの構造体型はどんな定義ですか?struct ●●{ ... }のような宣言箇所があるはずです。
退会済みユーザー

退会済みユーザー

2018/01/31 08:29

はいわかりました。
yohhoy

2018/01/31 08:33 編集

変数p, qの宣言も追記されたほうが良いです。struct address *p, *q; 相当だとは思いますが...
退会済みユーザー

退会済みユーザー

2018/01/31 08:44

すみません。今追加しました。ここが確定しないと前に進まないのでよろしくお願いいたします。
yoorwm

2018/01/31 09:18

質問の意味が分からなかったのですが、ひょっとして、「->」の他に、「<-」という演算子があると思い込んでいるのでしょうか?
退会済みユーザー

退会済みユーザー

2018/01/31 09:22

いやそうではありません。if()文の条件の中を図にして書いているんです。その時のどれがどれをポイントしているかを示すための矢印の意味です。
退会済みユーザー

退会済みユーザー

2018/01/31 09:24

(ポインタpのnext)からポインタのポインタqに矢印を書くのか、わからないのです。
rubato6809

2018/01/31 10:11

「ネットで拾ったのを手直しして課題として提出するのは不正行為(a_saitohさん)」という指摘に対する返事は無いのですか?
rubato6809

2018/01/31 10:13 編集

もうひとつ、逆の答えがあったというが、そもそも、どの答えとどの答えが逆なのか、示してください。
退会済みユーザー

退会済みユーザー

2018/01/31 10:16

コードの内容が分からないので質問しています。課題には回答はついていませんので、自分で参考になるプログラムを見つけて、答えをつくっています。課題の丸投げではありませんので、あしからず。
退会済みユーザー

退会済みユーザー

2018/01/31 10:19

回答いただいた方に再度回答をしてもらえるよう依頼しています。
rubato6809

2018/01/31 10:30

「新版明解C言語中級編の9章の自由課題8の復習て渋滞中」・・・これが電話帳プログラムの課題ですか?
guest

回答5

0

解決済みだけど質問者はモヤモヤのままでしょう。正解を示さないままでは私も引っかかりが残るので図をアップします。デジタルで矢印を上手く描けなかった(苦笑)ので矢印を手書きしました(他に誰も図を描かないのか?という思いもある)。

まず次の部分から確認します。バグ修正と説明の都合でコードを一部変更・省略しています。

C

1void exchange(struct address **p, struct address **q) 2{ 3 struct address *r, *s, *t; 4 5 r = *p; s = *q; 6 if (&((*p)->next) == q) { // こちらの条件だけで十分 7 if (s->next != 0) { 8 s->next->before = r; 9 } 10 s->before = r->before; // バグ修正 11 r->before = s; 12 *p = s; 13 *q = s->next; 14 s->next = r; 15 return;

r, s ポインタに値を代入し、if 文の条件が成り立った時点が図1です。
図1
図1

X番地、Y番地、Z番地にある3つの構造体が X ⇒ Y ⇒ Z というつながりになっている状態です。構造体の先頭アドレスに加えて、構造体中の next, before ポインタのアドレスを XN, XB, YN, YB ... としました。p ポインタの値は XN であり、XN は(Xの)next ポインタのアドレスであって、next ポインタの値ではない事を念押しします。
p の値は next ポインタのアドレスである、即ち next ポインタをポイントしている。

全てのデータはメモリ上にある=全てのデータにメモリアドレスがある。ポインタが活躍するプログラムを読み書きする時、メモリの値なのか、メモリのアドレスなのか、きちんと把握することは大事です。だから図を描いてみると良いのです。

さて、exchange() の2つの引数 p, q はポインタのポインタである:

  • struct address **p は X構造体の next ポインタ(XN番地)を指す
  • struct address **q は Y構造体の next ポインタ(YN番地)を指す

さらに、その2つの next ポインタの値を代入した r と s が指している Y と Z が交換対象である。交換しようとしているのが Y と Z である事と、p, q が指している場所を、勘違いしないようにしましょう。
つまり if (&((*p)->next) == q) の条件は隣り合う2つを交換する場合を意味します。ここ、問題ありませんよね?・・・と書いたら問題ありまくり、というかここが質問のキモでしたね。ポインタ、*演算子、&演算子などが苦手な人はひっかかるでしょう。図1を見ながら解説を編集します。

** &((*p)->next) の値は YN という番地**です。Y番地の構造体にある、nextポインタ自体のメモリアドレスです。**q ポインタの値も YN **です。だから一致するのです。これに対し質問者は

&((*p)->next) の値は…XN番地ではないのですか。YN番地でいいのでしょうか。

と問い返しました。図1に next ポインタはXN番地、YN番地、ZN番地の3つありますが、XN番地の next を ((*p)->next) だと勘違いしたのです。こういう時は気を落ち着けて struct address ** p; を、こんな風に整理すると良いと思います。

  • p の値は XN である(XN番地の next ポインタを指す)
  • *p の値は Y である(Y番地の構造体を指す)
  • **p の値(?)は Y番地にある構造体である

p, p, **p と、参照演算子「」の数が増えるにつれて図1の中の注目点が移っていきます。*p == Y を確認できれば (*p)->nextY->next と置き換えることができ、 (*p)->next は Y 番地の構造体の中の next ポインタだと理解できるはずです。

質問の次の部分はツッコミどころでした。

(*p)->next)はポインタpのnextでそのnextに入っているアドレスがポインタqのアドレスと等しい

そもそも、next ポインタは構造体の先頭をポイントするのに対し、p, q の値は next ポインタ(構造体の途中)をポイントする(=構造体の先頭をポイントしない)のだから、「next入っているアドレスがポインタqのアドレスと等しい」とはなりません。

よく見ると「(*p)->next)はポインタpのnext」が上記の勘違いを示しているし、「nextに入っているアドレスがポインタqのアドレスと等しい」にも混乱が見えます。私的には「ポインタqのアドレスと等しい」という表現にダメ出しをしたい。ここは「ポインタ q の値」としなくてはならない。

「いや、q のアドレスとは(qは値としてアドレスを持つポインタなのだから)q がポイントしている場所(== YN)という意味だ、だからqの値のことだ」という弁解が予想できますが、「qのアドレス」と言ったら変数 q 自身のメモリアドレスを意味します(複数の解釈ができるとしても、第一の意味はこれ)。図1には q 自身の番地を示していないが、もちろん q 自身にもアドレスはあります。ただ、コードを解釈するのに q のアドレスを考える必要がないので省略したまで、「qのアドレス」という表現は無用=誤りだったのです。

繰り返しますが、コードを読み書きする時、変数の値なのか、変数のアドレスなのか、明確に区別することが大事です。区別が曖昧だとあんな表現をしてしまいがち。そして、こんなにもメモリアドレスを意識しなければならないのは、アセンブリ言語を除けばC/C++ぐらいしか無く、ポインタが苦手な人が多い要因でもあるでしょう。その苦手を克服するには、変数領域のイメージを図にしてみる、変数の具体的なアドレスを確認する、を繰り返すことだと思います。メモリダンプできると、なお良いと思う。こうしたことがポインタ廻りの文法を自然に理解する近道であり、手間を惜しむな、と言いたい。質問者はコードを理解しようと膨大なコメントを書き込んでいましたが、言葉・文章よりも図のほうが雄弁です。それにこの間のやり取りは図を使うと議論が明確になることを実証したのじゃないですか(笑)。

さて、図1の関係が成り立つ時、X ⇒ Y ⇒ Z というつながりを、X ⇒ Z ⇒ Y というつながりに変えようとします。各変数の値・各ポインタの値がそれぞれどうなればよいか、考えてみてください。


次に、if (&((*p)->next) == q) が成り立たない場合とは、離れた構造体を交換する場合です。
操作対象の構造体がひとつあると、その前後の構造体の next, before が変更を受けるので、都合3つの構造体に影響します。exchange()は2つを交換するから、最大で6個の構造体が影響を受けるという事。その6つを A ⇒ B ⇒ C ⇒ D ⇒ E ⇒ F とします。交換対象は B と E です。p, q, r, s の各ポインタがどこをポイントしているか、ご確認ください。
図2

図2,6つの構造体

B と E を交換したら、どうなるか。
A ⇒ B ⇒ C ⇒ D ⇒ E ⇒ F を A ⇒ E ⇒ C ⇒ D ⇒ B ⇒ F というつながりにすることが目的ですが、リスト構造ではデータそのもの(構造体、その中の name[], address[]など)を動かさず、ポインタを付け替えて順番を変えるのです。次は交換後の図です。
図3

図3、交換後

例えば、A番地の構造体のnextポインタ(AN番地)が「B→E」とあるのは、この操作でnextポインタの値がBからEに変更される事を表します。
この、ポインタの付け替えをしているのが else 以下のコードです。

C

1 r = *p; s = *q; 2 if (&((*p)->next) == q) { 3 // 隣り合う2つを交換する(省略) 4 } else { 5 // 離れた2つを交換する 6 if (s->next != 0) { 7 s->next->before = r; 8 } 9 if (r->next != 0) { 10 r->next->before = s; 11 } 12 13 // 2つの before ポインタを交換する 14 t = s->before; 15 s->before = r->before; 16 r->before = t; 17 18 // 2つの next ポインタを交換する 19 t = r->next; 20 r->next = s->next; 21 s->next = t; 22 23 *p = s; 24 *q = r; 25 }

t ポインタを使った3行のパターンが2つあります。これは X と Y を交換する時、一時変数 t を使って

C

1 t = X; 2 X = Y; 3 Y = t;

とするのと同じです。図3で言えば B と E の next, before 2つの交換です。他にも同じパターンで交換できる所がありそうですが、
ともかく全部で8本のポインタを書き換えるべきことが図から分かりますし、コード上にもポインタの値を書き換えるところが8行あります。質問者は自ら手を動かして、その8箇所について、図とコードの対応をとってみると良いと思う。


P.S.1
参考にしたコードにあった決定的なバグは、次に示した最後2行の順番が逆になっていることです。

C

1 if (&((*p)->next) == q) { 2 if (s->next != 0) s->next->before = r; 3 r->before = s; // 順序逆 4 s->before = r->before; // 順序逆

ここから得られる教訓は、ポインタの付け替えは操作の順序が重要な箇所がある、ということ。誤りを犯しやすい所だし、リストの操作は扱うデータに関わらず一般性があるのだから、いちいちプログラマが直接書かなくても良くしよう・・・それが C++ の std::list コンテナでしょう。

P.S.2
参考にしたコードはリスト構造の特質を活かしているとは思えません。おそらく通常のソートプログラムで2つの要素を交換することが exchange() 関数の発想の元でしょう。でもそれがコードを不必要に難しくしている大きな要因になっています。

どなたかが指摘していたようにリスト構造では、要素をリストに挿入する、要素をリストから削除する、といった簡単な操作を用意するものです。そのほうが処理が簡単になり見通しが良くなります。具体例を示すために、恥ずかしながら「いちいち書いた」同等のコードを示してみます。

C

1#include <stdio.h> 2#include <string.h> 3#include <stdlib.h> 4#include <assert.h> 5 6#define FILENAME "address.csv" 7#define LINESIZE 80 /* メモリ節約w */ 8#define N 16 /* メモリ節約w */ 9 10typedef struct address { 11 char name[N]; 12 char address[N]; 13 char tel[N]; 14 char mail[N]; 15 struct address *next; 16 struct address *prev; 17} ADDR; 18 19void ins_addr(ADDR *new, ADDR *ap) // (*ap) の直後に (*new) を挿入 20{ 21 new->prev = ap; 22 new->next = ap->next; 23 if (new->next) 24 new->next->prev = new; 25 ap->next = new; 26} 27 28ADDR *rmv_addr(ADDR *ap) // (*ap)をリストから外す:remove 29{ 30 ap->prev->next = ap->next; 31 if (ap->next) { 32 ap->next->prev = ap->prev; 33 } 34 return ap; // そのまま返す 35} 36 37ADDR *min_addr(ADDR *ap) // ap以降の「最小」を探す 38{ 39 ADDR *min = ap; // 先頭を「最小」にして検索開始 40 41 while (ap->next) { // 「次」があるなら 42 ap = ap->next; // その「次」と「最小」を比較 43 if (strcmp(ap->name, min->name) < 0) { 44 min = ap; // 「最小」を更新 45 } 46 } 47 return min; 48} 49 50void sort_list(ADDR *head) // head以降をソート 51{ 52 ADDR *min, *ap = head; 53 54 while (ap->next) { // 「次」がある間、繰返す 55 min = min_addr(ap->next); // 「次」以降の「最小」を見つけ 56 if (min) { 57 ins_addr(rmv_addr(min), ap); // 抜出し、先頭の直後に移動する 58 } 59 ap = ap->next; // 移動したばかりの所を先頭にして、繰返す 60 } 61} 62 63void free_list(ADDR *ap) // メモリ返却 64{ 65 while (ap) { 66 ADDR *n = ap->next; 67 free(ap); 68 ap = n; 69 } 70} 71 72void show_list(ADDR *ap) // リスト一覧表示 73{ 74 while (ap) { 75 printf("%-10s %p: next = %p, prev = %p\n", 76 ap->name, ap, ap->next, ap->prev); 77 ap = ap->next; 78 } 79 // printf("\n"); 80} 81 82ADDR *line2addr(char line[]) // 読み込んだ一行を構造体に格納する 83{ 84 ADDR *new; 85 const char *token = ",\n\r"; // カンマと改行文字で区切る 86 char *p; 87 88 new = malloc(sizeof(ADDR)); // メモリ獲得 89 assert(new != NULL); 90 91 p = strtok(line, token); 92 assert(p != NULL); // "氏名の切り出しに失敗しました。" 93 strcpy(new->name, p); 94 95 p = strtok(NULL, token); 96 assert(p != NULL); // "住所の切り出しに失敗しました。" 97 strcpy(new->address, p); 98 99 p = strtok(NULL, token); 100 assert(p != NULL); // "電話番号の切り出しに失敗しました。" 101 strcpy(new->tel, p); 102 103 p = strtok(NULL, token); 104 assert(p != NULL); // "メールアドレスの切り出しに失敗しました。" 105 strcpy(new->mail, p); 106 107 return new; 108} 109 110void make_list(const char *fname, ADDR *head) // ファイルをリストに読込む 111{ 112 FILE *fp; 113 char line[LINESIZE]; 114 115 fp = fopen(fname, "r"); 116 assert(fp != NULL); 117 while (fgets(line, LINESIZE, fp) != NULL) { 118 ADDR *n = line2addr(line); // 一行をADDR構造体に変換し 119 ins_addr(n, head); // headの直後(リスト先頭)に挿入する 120 } 121 fclose(fp); 122} 123 124int main(void) 125{ 126 ADDR head = { 127 "head(dummy)", "", "", "", NULL, NULL 128 }; 129 130 printf(" ... read file into list ...\n"); 131 make_list(FILENAME, &head); 132 show_list(&head); // 念の為 head から表示 133 printf(" ... sorting ...\n"); 134 sort_list(&head); 135 show_list(head.next); 136 free_list(head.next); 137 return 0; 138}

リスト中の「最小」を見つけたら、交換するのではなく、削除して(リストから外して)挿入し直すだけ、隣り合うかどうかに関係なく処理できます。
line2addr() 関数を除けば、各関数は10行程度、さほど難しくないはずです。再帰も不要。
参考にしたコードはポインタ変数 struct address *head; を大元にしていますが、そこは工夫の余地があると思います。私はheadとして空の構造体を置いてみました。それもあって、ポインタのポインタはひとつも使わずに済んでいます。
他に手抜きかもしれないが、エラー判定はassert()で済ませているし、chop()関数を使わず strtok() で改行文字を削除しています。お試しあれ。

投稿2018/02/09 07:19

編集2018/02/15 03:58
rubato6809

総合スコア1380

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

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

退会済みユーザー

退会済みユーザー

2018/02/10 12:15

いつもお世話になっています。ポインタの復習をして、説明と図を見ていますが、rの中にあるのは構造体のYですよね、Yというのは(*p)ということでしょうか。(*p)はポインタのポインタの値で、構造体の先頭アドレスでいいでしょうか。図を見てもよくわからないのですが。struct address** pが構造体の先頭アドレスでいいでしょうか。すみません、まだよくポインタがわかっていないみたいです。 とりあえずお礼まで。
rubato6809

2018/02/11 05:22 編集

そもそも、メモリとはどんなものか、理解できてるかな? > rの中にあるのは構造体のYですよね、 > Yというのは(*p)ということでしょうか。 一般的に言って、ポインタ変数の値はメモリの番地です。 そして、Y は構造体の先頭アドレス・先頭番地として図に描いてます。 r = *p; によって、XN番地の値 Y が r ポインタに代入されたのです。 > Yというのは(*p)ということでしょうか。 うん、まあそういう言い方もできるかな。 Y == *p == (X番地構造体にある)next ポインタの値、です。 まだわからなければ、何度でも質問してください。 > (*p)はポインタのポインタの値 それは違うんじゃないか。p はポインタのポインタである、p の値は XN である。 *p はポインタであり、即ちX番地構造体にある next ポインタである、 *p の値は Y である。この場合、括弧をつけてもつけなくても同じじゃないかな・・・と書いたけど &((*p)->next) においては(*p)と括弧をつけないとエラー。演算子の優先順位と結合規則の関係。 > struct address** pが構造体の先頭アドレスでいいでしょうか 明確に違う。 p の値は構造体の先頭アドレスではありません。 p の値は next ポインタのアドレスである、即ち next ポインタをポイントしている。
rubato6809

2018/02/10 14:50 編集

struct address** p; という変数宣言は、pという名前の変数を使う、p はポインタのポインタである、p自身は構造体をポイントしない、何かポインタをポイントする(予定である)、その「何かポインタ」がポイントするのが構造体だ、という意味。 図1で言えば、p → next → 構造体 という関係なので、p はポインタのポインタ。pがポイントする「何かのポインタ」とは next ポインタ。 struct address *s; この場合、s ポインタは構造体の先頭アドレスを値として持つ(ことが予定されてる)。
退会済みユーザー

退会済みユーザー

2018/02/10 23:29

おはようございます。やっとすきりしました。知りたかったことがもれなく、説明していただき、間違っていた認識も指摘してもらって、ようやくわかりました。struct address** pのpが何のためにあるのかが間違っていました。 「p の値は next ポインタのアドレスである、即ち next ポインタをポイントしている。」ですね。 これから図をみて、勉強します。大変助かりました。
退会済みユーザー

退会済みユーザー

2018/02/11 01:32

rが構造体の先頭の時と、sが末尾の時も、elseで対応できるということが、どこかで見ました。 まずは図を理解できるまで吟味したいと思います。
退会済みユーザー

退会済みユーザー

2018/02/11 01:35

&((*p)→next==q の場合の図を書いてみました。あっているでしょうか。 お願いいたします。
rubato6809

2018/02/11 03:29 編集

あってません。ざっと見たところ 1.「struct address *p」と書いてますが、「struct address ** p」ですよ。 2.「struct address ** pp」なんて変数は、プログラムのどこにも存在しません。 3. 図の左下、 nextポインタの値がq であるかのようですが、これも間違い。 4. 右下、 next ポインタの値が p であるかのようですが、同様に間違い。 ・・・こんなにも私の図と違うのは、何故だろう(苦笑)
rubato6809

2018/02/11 04:19

少し図がよくなったけど、2つの next フィールドに p と q が書かれているのは、決定的な誤りです。 回答中に &((*p)->next) == q の解説を少し追加しました。
rubato6809

2018/02/11 05:05

ベストアンサーChironianさんも 「ポインタqの値は nextのアドレスと一致しているという意味」と書いてますね。 nextの値じゃなく、next のアドレスと q が一致する。
退会済みユーザー

退会済みユーザー

2018/02/11 05:07

いま書いてもらった図を見ながら直しています。しばらくお待ちください。
退会済みユーザー

退会済みユーザー

2018/02/11 05:51

図をアップしました。Yのアドレスとstruct address* P の先頭アドレスが同じということで、いいでしょうか。
rubato6809

2018/02/11 06:00

図はだいぶ私の図に近づきましたね。関係を表す図は一つしかありませんから。 でも残念ながら > Yのアドレスとstruct address* P の先頭アドレスが同じということで、いいでしょうか 違います。
rubato6809

2018/02/11 06:03

逆にお伺いしたいのですけど、今まで勉強した中で、私が描いたような図を見た・描いた経験はなかったですか? メモリにアドレスがあることはご存知ですか?
退会済みユーザー

退会済みユーザー

2018/02/11 06:25

それは理解できています。図解のポインタや、ポインタのポインタの図解のコードの説明を見て、りかいできているつもりなんですが。教科書の後半なので、c言語のアウトラインは分かっているつもりなんですが。 自分でも図をコードに沿って書いたのですが、ポイントのつなぎ替えをしているのは、分かっていたのですがうまく矢印が出来ていないところがありました。隣り合うリストの条件の入り口で引っかかっています。疲れたので休憩します。図はたくさん書いて見たのですがどこか間違っているんですね。 else以下はおかげさまでよく理解できました。明後日またけんとうします。 ご面倒おかけしてすみません。数か月このプログラムに取り組んでいます。
rubato6809

2018/02/11 08:05

数カ月かけたのであればなおさら、 &((*p)->next) == q は時間をかけても良いのでキッチリ征服してください。そうしないと何時までもきちんとした理解が得られないでしょう。 同時に、それとは違うやり方にも触れたほうが良いです。恥ずかしながら私のコードを回答に追加しました。
退会済みユーザー

退会済みユーザー

2018/02/11 08:31

私もそう思って頑張ってきました。あと1章を残して、引っかかっています。ここで諦めたら何のために c言語にこだわってきたか分からなくなりますので、時間をかけてがんばります。 あしたはおしごとです。ありがとうございました。
rubato6809

2018/02/11 11:00

たぶん、このコードだけを毎日眺めていても、さしたる進展はないと思う。 むしろ、違うことに様々トライしてみること。一番良いのは(偶然に)貴方自身がこれに似たようなコードを書く機会に遭遇すること。時間をかける、とはそういう意味もある。
rubato6809

2018/02/13 01:42

問題文中の図を描き変えたら、私の図1と同じになりましたね。ですから図は正しくなりました。 でも 「(*p)->next)はポインタpのnextでそのnextに入っている(*p)->next)のアドレスがポインタqのアドレスと等しい」 という記述は、まだおかしい。私は間違いと判定します。 考えてもらいたいので訊きます。次の問に答えられますか? ・「そのnextに入っている(*p)->next)のアドレス」とは具体的に何か? ・「そのnextに入っている(*p)->next)のアドレス」・・・ここに2つ"next"があるけど、同じものなのか、異なるものなのか?また、具体的にどれか? ・「ポインタqのアドレス」とは何か?そのアドレスは図に示されているか?
退会済みユーザー

退会済みユーザー

2018/02/13 02:35

ありがとうございました。rabato6809さんの図を清書してかきかえました。質問のところは書き変えていません。今は回答してもらった文を読みながら、コードにコメントを付けて、整理しています。 図も、見やすいように、図2、図3を書き変えています。それまで待ってもらえますか。
退会済みユーザー

退会済みユーザー

2018/02/13 02:44

それで一つ疑問があります。編集 2018/02/11 16:55の回答の中の、「・・・と書いたら問題ありまくりな感じなので図1を見ながら少しだけ解説を加えます。 そもそも &((*p)->next) の値は YN という番地です。Y番地の構造体にある、nextポインタ自体のメモリアドレスです。q ポインタの値も YN です。だから一致するのです。」というところの &((*p)->next) の値は YN という番地です。はXN番地ではないのですか。YN番地でいいのでしょうか。お尋ねの件もここに関係していると思うのですが。 読みながら整理していて、分からなくてこまっています。
rubato6809

2018/02/13 03:25 編集

&((*p)->next) の値は YN という番地ですよ。図1はそう描いてあります。 少なくとも、そうでなければ q ポインタの値と一致しません。 要するに、貴方の問題は &((*p)->next)  の解釈を誤っていること。 まず、 「*p」とは Y であること、即ち「(*p) == Y番地」だということが理解できてますか? 念の為: XN は p の値であって、 *p の値ではありません。 同様に、YN は q の値であり、 *q は Z という値になります。 次に (*p)->next が、図のどこなのか、わかりますか? もうひとつ、実行中の p, q, r, s の値を確かめたことはありますか?各構造体の先頭アドレスを確かめたことはありますか?確かめられますか? printfデバッグでも良いし、手元で使えるデバッガがあれば使うも良し。
rubato6809

2018/02/13 04:08 編集

ポインタを使った、もっと簡単なコードをおさらいするのもいい。 void func(void) { char a = 'A'; char *p = &a; // char *p; p = &a; としても同じだよ char **pp = &p; printf("&a = %p, a = %c\n", &a, a); printf("p = %p, *p = %c\n", p, *p); printf("&p = %p\n", &p); printf("&pp = %p, pp = %p, *pp = %p, **pp = %c\n", &pp, pp, *pp, **pp); } みたいなプログラム。この pp と p と a を図示してみるところから始めるとか。
rubato6809

2018/02/13 03:55 編集

テストプログラム↑を何度か書き変えました。コンパイルして動かしてみてください。
rubato6809

2018/02/13 03:55

はずかしいバグをなおしましたw
退会済みユーザー

退会済みユーザー

2018/02/13 04:53

おっしゃる通り、&((*p)->next)  の解釈を誤っていました。見て直ぐにわからないので、後でよく読み直します。ありがとうございます。前に実行中の p, q, r, s の値を確かめたデータが残っていると思うので、それもチェックします。説明を今整理しています。すみません。今日は整理でおわりそうです。 また載せてもらった同じようなコードでアドレスの確認の練習をしたことがあり、図も添えてありましたので、分かっているつもりでいたのですが、分かっていなかったのですね。復習します。
退会済みユーザー

退会済みユーザー

2018/02/13 05:14

もう一つお聞きしたいのですが、私の図の解釈が間違っているかもしれません。 図はx番地にあるx構造体とその下に続くNX番地にあるY((X番地構造体にある)next ポインタの値)構造体はくっついているようにありますが、実際には大分離れたところにあるんですよね。 このあたりをわたくしが混同しているようなんですが。教えていただければありがたいです。
退会済みユーザー

退会済みユーザー

2018/02/13 05:25

「*p」とは Y であること、即ち「(*p) == Y番地」である。 XN は p の値であって、 *p の値ではない。 同様に、YN は q の値であり、 *q は Z という値である。ということは理解できてます。 *p==Y番地であるから (*p)->next は Y->next==YNでよろしいでしょうか。ここのところが やっとわかりました。 間違っていませんか? よろしくお願いいたします。
rubato6809

2018/02/13 05:53

大分離れたところにある・・・X と XN が離れている、ということだと思いますが、 では具体的な X と XN の番地を示していただけますか。そして XN - X を計算してみてください。どれくらい離れていますか?その数値(距離)に心当たりはありませんか? ついでに、Y と YN わかるといいですね。 > *p==Y番地であるから (*p)->next は Y->next==YNでよろしいでしょうか 惜しい。私的には正解ではない。 「*p==Y番地であるから (*p)->next は Y->next」のことだ、とすれば、ここまでは間違いとはいわない。 Y->next の値は Z である。よって Y->next == YN とはならない。ここが間違い。 その値が変数のアドレスなのか、変数の値なのか、きちんと区別できること・メモリ上にデータがどのように配置されているかイメージできることがポインタを征服する条件だと私は思う。 &(Y->next) として、&演算子で next ポインタ・・・nextフィールドと呼ぶのが正しいかも・・・のアドレスをとった、そのアドレスがYNである。 なので「 (Y->next)のアドレス == YN 」であれば正解です。同時に q == YN なら条件が成立するというわけ。
退会済みユーザー

退会済みユーザー

2018/02/13 06:12

はい、ありがとうございます。アドレスチェックは後で読み直すときにします。 とりあえず、知りたかったことが理解できるところまできました。 今日もお付き合いしていただきありがとうございます。
退会済みユーザー

退会済みユーザー

2018/02/13 06:40

示してもらったコードを実行したら、いがいとはなれてないですね。ポインタからポインタのポインタまでは4バイトでした。文字’A'のアドレスもそんなに離れてないですね。 図も書いて見ます。
guest

0

ベストアンサー

こんにちは。

(&((*p)->next) == qの条件が成立しているということは、ポインタqの値は nextのアドレスと一致しているという意味です。
ということは、qはnextを指しています。従って「next <-q」と表現してOKと思います。

投稿2018/01/31 10:29

Chironian

総合スコア23272

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

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

退会済みユーザー

退会済みユーザー

2018/02/03 07:38

ありがとうございます。上の最初の図の矢印を逆にした図でいいのでしょうか。 それとも下の図のほうがいいのでしょうか。お手数おかけします。 よろしくおねがいたします。
Chironian

2018/02/03 08:11

ppやqqってなんでしょう? また、これらがなんだろうと両方とも間違っているように見えます。*pや*qの下にあるnextやbeforeはなんでしょう? 省略せずにきっちり書いた方が良いと思いますよ。 字面通りですが、pは*pが記録されているメモリの先頭を指し、*pは**pが記録されているメモリの先頭を指します。*pはaddress構造体へのポインタ型ですね。**pはaddress構造体型です。 p → *p → **p とも書けます。**pは、name, address, tel, mail, next, beforeメンバを持っています。
退会済みユーザー

退会済みユーザー

2018/02/03 09:39

すみませいん。色々修正しました。この図でいいでしょうか。
rubato6809

2018/02/03 09:46

どちらの図も間違い。ppとかqqとか、そんな変数、プログラムのどこにも無い、構造体の中のnextポインタが指してる先はpとかqではない。 下の図だと赤色の矢印と紫の線が両端とも矢になってるのは、もう混乱しているとしか…(苦笑)
Chironian

2018/02/03 09:49 編集

後もう少しと思います。 struct address **qq と書くと判りにくいです。 これは「struct address** q」ですね。「address構造体へのポインタのポインタ」である q と読めます。 次の、struct address *qは間違いです。 struct address* (*q)と書くと判りやすいと思います。(*q)が「address構造体へのポインタ」ですから。 > (*p)->next)はポインタpのnextでそのnextが入っているアドレスが(ポインタのポインタ)qと等しいということですよね。 その通りです。これは「qが(*p)->nextを指す」と同じ意味ですね。 右側一番上の「struct address** q」から、左側の「struct address *next」へ矢印を引くと正解です。
退会済みユーザー

退会済みユーザー

2018/02/03 09:54

はい、もう一度修正します
rubato6809

2018/02/03 09:58

まずは p, q と2つの構造体の関係を正しく図に表すことが最初のステップだろうな。 Chironianさんの解説と、プログラムコードを、良く読むことだね。
rubato6809

2018/02/03 10:04

図に描かせてみると、理解できてるか否か、一目瞭然。
rubato6809

2018/02/03 10:08

繰り返すけど、ppもqqも存在していないんだけどなあ。 それと、構造体の中のnext, beforeポインタがどこを指しているか、描き表せるものがあるんだけどねえ。
退会済みユーザー

退会済みユーザー

2018/02/03 10:16

すみません。混乱してますが。少しは見えてきたので後日ポインタを勉強しなおしてから、質問しなおします。図をアップ出来たところまで、来ましたので良しとします。先日までできなかったんですから。
Chironian

2018/02/03 10:21

了解です。頑張ってください。 因みに、rubato6809さんもフォローしてくれているように、ppやqqは存在しません。存在しないものを書くと混乱します。 また、(*q)は、address構造体へのポインタです。従って、下にあるaddress構造体を指している矢印が正解です。 nextはaddress構造体へのポインタであってaddress構造体ではありませんので、(*q)からnextへの矢印は不正解です。
退会済みユーザー

退会済みユーザー

2018/02/03 20:20

ありがとうございます。よく読み直します。
rubato6809

2018/02/03 22:20

双方向リストは「図にするとこんな感じ↓」 http://tricky-code.net/datastructure/ds09dlink.php 隣り合う2つはnextとprev(previous)が互いをポイントする(から交差する)のであって、nextとnextは交差しないのです。 それと、 if (&((*p)->next) == q || &((*q)->next) == p) となってるけど、あのプログラムで &((*q)->next) == p という条件が成り立つことは無いので if (&((*p)->next) == q) だけで考えたほうが良いかも。 Enjoy!
rubato6809

2018/02/04 08:43

指摘が遅くなって申し訳ない。図中「struct address* (*p)」から、 赤い矢印が右側構造体の next に向かい、同時に 黒い矢印が左側構造体に向う。 明らかな混乱です。「struct address* (*q)」も同様。
rubato6809

2018/02/04 15:40

&演算子って、何だ? ポインタの値って何だ? ポインタのポインタって何だ?そして、これらの関係をちゃんと図示できるか(笑)? ポインタのポインタを使う理由は何だ?どこでポインタのポインタが作られたのか、わかっているか?等など 今解読しようとしているコードは基本を押さえていないと惑わされます。そういう意味で、書いた人の「腕力」は窺えるし、力試しにはなるけど、センスもレベルも高くなく不必要に難しいコードになっている(ポインタのポインタを使わずに書ける問題だし、再帰など不要)、実際バグもあった、手間暇かけて勉強するような良いコードとは思えない。 ポインタ絡みで、もう少し簡単なレベルから復習するとか、リスト構造を使うメリットを勉強し直すとか、違うアプローチを考えたほうが良いと思う。
退会済みユーザー

退会済みユーザー

2018/02/05 01:10

ありがとうございます。
guest

0

if (&((*p)->next) == q || &((*q)->next) == p)
ですが、あくまでもポインタ同士が等しいかどうかの判定なので、
これだけを見ると、図上の矢印の方向はどちらでもいいのだと思います。

図で矢印が必要なのはリスト構造がどのようにつながっているかを表現するためです。
構造体同士がどのように連結しているかを意識して図を描いてみたらいかがでしょう。

投稿2018/02/03 03:11

TaroToyotomi

総合スコア1430

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

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

退会済みユーザー

退会済みユーザー

2018/02/03 05:34

そうですよね。ありがとうございます。今作図したものを質問に貼り付ける準備と、ポインタのつなぎ替えを 勉強しており、貼り付けは分かりましたので。図の検証をし直してからの再質問をします。
guest

0

質問の回答ではありませんが、C言語のポインタの話題で矢印を->とか<-と表記するとあらぬ誤解を受けますので(私も少々理解に時間がかかりました)、矢印は矢印記号を使った方がよろしいかと思います。

投稿2018/01/31 12:39

catsforepaw

総合スコア5938

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

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

退会済みユーザー

退会済みユーザー

2018/02/03 07:33

そうですね図の作成と、アップに大分時間がかかりました。 ありがとうございます。
guest

0

もしかして、

if( &((*p)->next) == q )

のこと?

それなら、if and only ifです。

if and only ifとは

向きは関係なく、pのnext の値と q が同じなら...

っていう意味。

投稿2018/01/31 09:42

BeatStar

総合スコア4958

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

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

退会済みユーザー

退会済みユーザー

2018/01/31 09:50

すみません。回答していただいた方から図を書くとわかり易いということで、図を書いて理解しようとしています。 回答いただいた方は、c言語のスペシャリストの方もいらっしゃるので、まよっているところです。 2つの相反する回答を頂いたので、質問を挙げた次第です。 あなたのおっしゃる通りです。ありがとうございます。
rubato6809

2018/01/31 12:47

残念ながら、質問者の意図と関係ない回答だと思う
退会済みユーザー

退会済みユーザー

2018/02/03 07:40

今日とりあえず最初の質問の図をアップしましたので。ご意見をお願いいたします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問