X番地、Y番地、Z番地にある3つの構造体が X ⇒ Y ⇒ Z というつながりになっている状態です。構造体の先頭アドレスに加えて、構造体中の next, before ポインタのアドレスを XN, XB, YN, YB ... としました。p ポインタの値は XN であり、XN は(Xの)next ポインタのアドレスであって、next ポインタの値ではない事を念押しします。 p の値は next ポインタのアドレスである、即ち next ポインタをポイントしている。
さらに、その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)->next を Y->next と置き換えることができ、 (*p)->next は Y 番地の構造体の中の next ポインタだと理解できるはずです。
さて、図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
B と E を交換したら、どうなるか。
A ⇒ B ⇒ C ⇒ D ⇒ E ⇒ F を A ⇒ E ⇒ C ⇒ D ⇒ B ⇒ F というつながりにすることが目的ですが、リスト構造ではデータそのもの(構造体、その中の name[], address[]など)を動かさず、ポインタを付け替えて順番を変えるのです。次は交換後の図です。
図3
1 r =*p; s =*q;2if(&((*p)->next)== q){3// 隣り合う2つを交換する(省略)4}else{5// 離れた2つを交換する6if(s->next !=0){7 s->next->before = r;8}9if(r->next !=0){10 r->next->before = s;11}1213// 2つの before ポインタを交換する14 t = s->before;15 s->before = r->before;16 r->before = t;1718// 2つの next ポインタを交換する19 t = r->next;20 r->next = s->next;21 s->next = t;2223*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箇所について、図とコードの対応をとってみると良いと思う。
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 ポインタをポイントしている。」ですね。
これから図をみて、勉強します。大変助かりました。
おっしゃる通り、&((*p)->next) の解釈を誤っていました。見て直ぐにわからないので、後でよく読み直します。ありがとうございます。前に実行中の p, q, r, s の値を確かめたデータが残っていると思うので、それもチェックします。説明を今整理しています。すみません。今日は整理でおわりそうです。
また載せてもらった同じようなコードでアドレスの確認の練習をしたことがあり、図も添えてありましたので、分かっているつもりでいたのですが、分かっていなかったのですね。復習します。