回答編集履歴

11

一文字強調

2018/02/15 03:58

投稿

rubato6809
rubato6809

スコア1380

test CHANGED
@@ -98,7 +98,7 @@
98
98
 
99
99
 
100
100
 
101
- そもそも、next ポインタは構造体の先頭をポイントするのに対し、p, q の値は next ポインタ(構造体の途中)をポイントする(=構造体の先頭をポイントしない)のだから、「nextに入っているアドレスがポインタqのアドレスと等しい」とはなりません。
101
+ そもそも、next ポインタは構造体の先頭をポイントするのに対し、p, q の値は next ポインタ(構造体の途中)をポイントする(=構造体の先頭をポイントしない)のだから、「next****入っているアドレスがポインタqのアドレスと等しい」とはなりません。
102
102
 
103
103
 
104
104
 

10

回答の肝心な部分を追加編集(再投稿)

2018/02/15 03:58

投稿

rubato6809
rubato6809

スコア1380

test CHANGED
@@ -66,21 +66,53 @@
66
66
 
67
67
  さらに、その2つの next ポインタの値を代入した r と s が指している Y と Z が交換対象である。交換しようとしているのが Y と Z である事と、p, q が指している場所を、勘違いしないようにしましょう。
68
68
 
69
- つまり ``` if (&((*p)->next) == q) ``` の条件は**隣り合う2つを交換する場合**を意味します。ここ、問題ありませんよね?
69
+ つまり ``` if (&((*p)->next) == q) ``` の条件は**隣り合う2つを交換する場合**を意味します。ここ、問題ありませんよね?・・・と書いたら問題ありまくり、というか**ここが質問のキモ**でしたね。ポインタ、*演算子、&演算子などが苦手な人はひっかかるでしょう。図1を見ながら解説を編集します。
70
-
71
-
72
-
73
- ・・・と書いたら問題ありまくりな感じなので図1を見ながら少しだけ解説を加えます。
70
+
74
-
71
+
72
+
75
- そもそも** &((*p)->next) の値は YN という番地**です。Y番地の構造体にある、nextポインタ自体のメモリアドレスです。**q ポインタの値も YN **です。だから一致するのです。たぶん質問者はそこがわかっていない。
73
+ ** &((*p)->next) の値は YN という番地**です。Y番地の構造体にある、nextポインタ自体のメモリアドレスです。**q ポインタの値も YN **です。だから一致するのです。これに対し質問者は
74
+
75
+
76
+
76
-
77
+ > &((*p)->next) の値は…XN番地ではないのですか。YN番地でいいのでしょうか。
78
+
79
+
80
+
77
-
81
+ と問い返しました。図1に next ポインタはXN番地、YN番地、ZN番地の3つありますが、XN番地の next を ((*p)->next) だと勘違いしたのです。こういう時は気を落ち着けて ``` struct address ** p; ``` を、こんな風に整理すると良いと思います。
82
+
83
+ - p の値は XN である(XN番地の next ポインタを指す)
84
+
85
+ - *p の値は Y である(Y番地の構造体を指す)
86
+
87
+ - **p の値(?)は Y番地にある構造体である
88
+
89
+
90
+
91
+ p, *p, **p と、参照演算子「*」の数が増えるにつれて図1の中の注目点が移っていきます。*p == Y を確認できれば ``` (*p)->next ``` を ``` Y->next ``` と置き換えることができ、 ``` (*p)->next ``` は Y 番地の構造体の中の next ポインタだと理解できるはずです。
92
+
93
+
94
+
95
+ 質問の次の部分はツッコミどころでした。
78
96
 
79
97
  > (*p)->next)はポインタpのnextでそのnextに入っているアドレスがポインタqのアドレスと等しい
80
98
 
81
99
 
82
100
 
83
- よく見ると「(*p)->next)はポインタのnext」ではない、「nextに入っているアドレスがポインタqのアドレスと等しい」というのも混乱です。るほど、ここが鬼門のようですポインタ、*演算子、&演算子などが苦手な人はひっかかるでしょうね。
101
+ そもそも、next ポインタは構造体先頭をポイントするのに対し、p, q の値は next ポインタ(構造体の途中)をポイントする(=構造体の先頭をポイントしない)のだから、「nextに入っているアドレスがポインタqのアドレスと等しい」とりません
102
+
103
+
104
+
105
+ よく見ると「(*p)->next)はポインタpのnext」が上記の勘違いを示しているし、「nextに入っているアドレスがポインタqのアドレスと等しい」にも混乱が見えます。私的には「ポインタqのアドレスと等しい」という表現にダメ出しをしたい。ここは「ポインタ q の値」としなくてはならない。
106
+
107
+
108
+
109
+ 「いや、q のアドレスとは(qは値としてアドレスを持つポインタなのだから)q がポイントしている場所(== YN)という意味だ、だからqの値のことだ」という弁解が予想できますが、「qのアドレス」と言ったら変数 q 自身のメモリアドレスを意味します(複数の解釈ができるとしても、第一の意味はこれ)。図1には q 自身の番地を示していないが、もちろん q 自身にもアドレスはあります。ただ、コードを解釈するのに q のアドレスを考える必要がないので省略したまで、「qのアドレス」という表現は無用=誤りだったのです。
110
+
111
+
112
+
113
+ 繰り返しますが、コードを読み書きする時、**変数の値なのか、変数のアドレスなのか、明確に区別することが大事**です。区別が曖昧だとあんな表現をしてしまいがち。そして、こんなにもメモリアドレスを意識しなければならないのは、アセンブリ言語を除けばC/C++ぐらいしか無く、ポインタが苦手な人が多い要因でもあるでしょう。その苦手を克服するには、変数領域のイメージを図にしてみる、変数の具体的なアドレスを確認する、を繰り返すことだと思います。メモリダンプできると、なお良いと思う。こうしたことがポインタ廻りの文法を自然に理解する近道であり、手間を惜しむな、と言いたい。質問者はコードを理解しようと膨大なコメントを書き込んでいましたが、言葉・文章よりも図のほうが雄弁です。それにこの間のやり取りは**図を使うと議論が明確になる**ことを実証したのじゃないですか(笑)。
114
+
115
+
84
116
 
85
117
 
86
118
 
@@ -202,9 +234,9 @@
202
234
 
203
235
  if (s->next != 0) s->next->before = r;
204
236
 
205
- r->before = s; // 順序逆
237
+ r->before = s; // 順序逆
206
-
238
+
207
- s->before = r->before; // 順序逆
239
+ s->before = r->before; // 順序逆
208
240
 
209
241
  ```
210
242
 

9

追加説明の修正、その4。さらに追伸2項目を追加

2018/02/15 02:55

投稿

rubato6809
rubato6809

スコア1380

test CHANGED
@@ -80,13 +80,15 @@
80
80
 
81
81
 
82
82
 
83
- 「nextに入っているアドレスがポインタqのアドレスと等しい」というところに混乱がる。
83
+ よく見ると(*p)->next)はポインタpのnext」ではないし、「nextに入っているアドレスがポインタqのアドレスと等しい」というのも混乱です。なるほど、ここ鬼門のようです。ポインタ、*演算子、&演算子などが苦手な人はひっかかでしょうね
84
-
85
-
86
-
84
+
85
+
86
+
87
- X ⇒ Y ⇒ Z というつながりを、X ⇒ Z ⇒ Y というつながりに変えようとしています。各変数の値・各ポインタの値がそれぞれどうなればよいか、考えてみてください。
87
+ さて、図1の関係が成り立つ時、X ⇒ Y ⇒ Z というつながりを、X ⇒ Z ⇒ Y というつながりに変えようとします。各変数の値・各ポインタの値がそれぞれどうなればよいか、考えてみてください。
88
+
89
+
90
+
88
-
91
+ ---
89
-
90
92
 
91
93
  次に、``` if (&((*p)->next) == q) ``` が成り立たない場合とは、**離れた構造体を交換する場合**です。
92
94
 
@@ -186,4 +188,322 @@
186
188
 
187
189
  ともかく全部で8本のポインタを書き換えるべきことが図から分かりますし、コード上にもポインタの値を書き換えるところが8行あります。質問者は自ら手を動かして、その8箇所について、図とコードの対応をとってみると良いと思う。
188
190
 
191
+
192
+
193
+ ---
194
+
195
+ P.S.1
196
+
197
+ [参考にしたコード](http://codepad.org/B83f1FSq)にあった決定的なバグは、次に示した最後2行の順番が逆になっていることです。
198
+
199
+ ```C
200
+
201
+ if (&((*p)->next) == q) {
202
+
203
+ if (s->next != 0) s->next->before = r;
204
+
205
+ r->before = s; // 順序逆
206
+
207
+ s->before = r->before; // 順序逆
208
+
209
+ ```
210
+
211
+ ここから得られる教訓は、ポインタの付け替えは操作の順序が重要な箇所がある、ということ。誤りを犯しやすい所だし、リストの操作は扱うデータに関わらず一般性があるのだから、いちいちプログラマが直接書かなくても良くしよう・・・それが C++ の std::list コンテナでしょう。
212
+
213
+
214
+
215
+ P.S.2
216
+
217
+ [参考にしたコード](http://codepad.org/B83f1FSq)はリスト構造の特質を活かしているとは思えません。おそらく通常のソートプログラムで2つの要素を交換することが exchange() 関数の発想の元でしょう。でもそれがコードを不必要に難しくしている大きな要因になっています。
218
+
219
+
220
+
221
+ どなたかが指摘していたようにリスト構造では、要素をリストに挿入する、要素をリストから削除する、といった簡単な操作を用意するものです。そのほうが処理が簡単になり見通しが良くなります。具体例を示すために、恥ずかしながら「いちいち書いた」同等のコードを示してみます。
222
+
223
+ ```C
224
+
225
+ #include <stdio.h>
226
+
227
+ #include <string.h>
228
+
229
+ #include <stdlib.h>
230
+
231
+ #include <assert.h>
232
+
233
+
234
+
235
+ #define FILENAME "address.csv"
236
+
237
+ #define LINESIZE 80 /* メモリ節約w */
238
+
239
+ #define N 16 /* メモリ節約w */
240
+
241
+
242
+
243
+ typedef struct address {
244
+
245
+ char name[N];
246
+
247
+ char address[N];
248
+
249
+ char tel[N];
250
+
251
+ char mail[N];
252
+
253
+ struct address *next;
254
+
255
+ struct address *prev;
256
+
189
- (まだ続く)
257
+ } ADDR;
258
+
259
+
260
+
261
+ void ins_addr(ADDR *new, ADDR *ap) // (*ap) の直後に (*new) を挿入
262
+
263
+ {
264
+
265
+ new->prev = ap;
266
+
267
+ new->next = ap->next;
268
+
269
+ if (new->next)
270
+
271
+ new->next->prev = new;
272
+
273
+ ap->next = new;
274
+
275
+ }
276
+
277
+
278
+
279
+ ADDR *rmv_addr(ADDR *ap) // (*ap)をリストから外す:remove
280
+
281
+ {
282
+
283
+ ap->prev->next = ap->next;
284
+
285
+ if (ap->next) {
286
+
287
+ ap->next->prev = ap->prev;
288
+
289
+ }
290
+
291
+ return ap; // そのまま返す
292
+
293
+ }
294
+
295
+
296
+
297
+ ADDR *min_addr(ADDR *ap) // ap以降の「最小」を探す
298
+
299
+ {
300
+
301
+ ADDR *min = ap; // 先頭を「最小」にして検索開始
302
+
303
+
304
+
305
+ while (ap->next) { // 「次」があるなら
306
+
307
+ ap = ap->next; // その「次」と「最小」を比較
308
+
309
+ if (strcmp(ap->name, min->name) < 0) {
310
+
311
+ min = ap; // 「最小」を更新
312
+
313
+ }
314
+
315
+ }
316
+
317
+ return min;
318
+
319
+ }
320
+
321
+
322
+
323
+ void sort_list(ADDR *head) // head以降をソート
324
+
325
+ {
326
+
327
+ ADDR *min, *ap = head;
328
+
329
+
330
+
331
+ while (ap->next) { // 「次」がある間、繰返す
332
+
333
+ min = min_addr(ap->next); // 「次」以降の「最小」を見つけ
334
+
335
+ if (min) {
336
+
337
+ ins_addr(rmv_addr(min), ap); // 抜出し、先頭の直後に移動する
338
+
339
+ }
340
+
341
+ ap = ap->next; // 移動したばかりの所を先頭にして、繰返す
342
+
343
+ }
344
+
345
+ }
346
+
347
+
348
+
349
+ void free_list(ADDR *ap) // メモリ返却
350
+
351
+ {
352
+
353
+ while (ap) {
354
+
355
+ ADDR *n = ap->next;
356
+
357
+ free(ap);
358
+
359
+ ap = n;
360
+
361
+ }
362
+
363
+ }
364
+
365
+
366
+
367
+ void show_list(ADDR *ap) // リスト一覧表示
368
+
369
+ {
370
+
371
+ while (ap) {
372
+
373
+ printf("%-10s %p: next = %p, prev = %p\n",
374
+
375
+ ap->name, ap, ap->next, ap->prev);
376
+
377
+ ap = ap->next;
378
+
379
+ }
380
+
381
+ // printf("\n");
382
+
383
+ }
384
+
385
+
386
+
387
+ ADDR *line2addr(char line[]) // 読み込んだ一行を構造体に格納する
388
+
389
+ {
390
+
391
+ ADDR *new;
392
+
393
+ const char *token = ",\n\r"; // カンマと改行文字で区切る
394
+
395
+ char *p;
396
+
397
+
398
+
399
+ new = malloc(sizeof(ADDR)); // メモリ獲得
400
+
401
+ assert(new != NULL);
402
+
403
+
404
+
405
+ p = strtok(line, token);
406
+
407
+ assert(p != NULL); // "氏名の切り出しに失敗しました。"
408
+
409
+ strcpy(new->name, p);
410
+
411
+
412
+
413
+ p = strtok(NULL, token);
414
+
415
+ assert(p != NULL); // "住所の切り出しに失敗しました。"
416
+
417
+ strcpy(new->address, p);
418
+
419
+
420
+
421
+ p = strtok(NULL, token);
422
+
423
+ assert(p != NULL); // "電話番号の切り出しに失敗しました。"
424
+
425
+ strcpy(new->tel, p);
426
+
427
+
428
+
429
+ p = strtok(NULL, token);
430
+
431
+ assert(p != NULL); // "メールアドレスの切り出しに失敗しました。"
432
+
433
+ strcpy(new->mail, p);
434
+
435
+
436
+
437
+ return new;
438
+
439
+ }
440
+
441
+
442
+
443
+ void make_list(const char *fname, ADDR *head) // ファイルをリストに読込む
444
+
445
+ {
446
+
447
+ FILE *fp;
448
+
449
+ char line[LINESIZE];
450
+
451
+
452
+
453
+ fp = fopen(fname, "r");
454
+
455
+ assert(fp != NULL);
456
+
457
+ while (fgets(line, LINESIZE, fp) != NULL) {
458
+
459
+ ADDR *n = line2addr(line); // 一行をADDR構造体に変換し
460
+
461
+ ins_addr(n, head); // headの直後(リスト先頭)に挿入する
462
+
463
+ }
464
+
465
+ fclose(fp);
466
+
467
+ }
468
+
469
+
470
+
471
+ int main(void)
472
+
473
+ {
474
+
475
+ ADDR head = {
476
+
477
+ "head(dummy)", "", "", "", NULL, NULL
478
+
479
+ };
480
+
481
+
482
+
483
+ printf(" ... read file into list ...\n");
484
+
485
+ make_list(FILENAME, &head);
486
+
487
+ show_list(&head); // 念の為 head から表示
488
+
489
+ printf(" ... sorting ...\n");
490
+
491
+ sort_list(&head);
492
+
493
+ show_list(head.next);
494
+
495
+ free_list(head.next);
496
+
497
+ return 0;
498
+
499
+ }
500
+
501
+ ```
502
+
503
+ リスト中の「最小」を見つけたら、交換するのではなく、削除して(リストから外して)挿入し直すだけ、隣り合うかどうかに関係なく処理できます。
504
+
505
+ line2addr() 関数を除けば、各関数は10行程度、さほど難しくないはずです。再帰も不要。
506
+
507
+ [参考にしたコード](http://codepad.org/B83f1FSq)はポインタ変数 struct address *head; を大元にしていますが、そこは工夫の余地があると思います。私はheadとして空の構造体を置いてみました。それもあって、ポインタのポインタはひとつも使わずに済んでいます。
508
+
509
+ 他に手抜きかもしれないが、エラー判定はassert()で済ませているし、chop()関数を使わず strtok() で改行文字を削除しています。お試しあれ。

8

追加説明の修正、その3

2018/02/11 07:55

投稿

rubato6809
rubato6809

スコア1380

test CHANGED
@@ -80,7 +80,7 @@
80
80
 
81
81
 
82
82
 
83
- 「ポインタqのアドレスと等しい」というところに混乱がある。図にも表せていないようだ。
83
+ nextに入っているアドレスがポインタqのアドレスと等しい」というところに混乱がある。
84
84
 
85
85
 
86
86
 

7

無駄な改行を削除

2018/02/11 04:54

投稿

rubato6809
rubato6809

スコア1380

test CHANGED
@@ -76,9 +76,7 @@
76
76
 
77
77
 
78
78
 
79
- > (*p)->next)はポインタpのnextでそのnextに入っているアドレスがポインタqのアドレス
79
+ > (*p)->next)はポインタpのnextでそのnextに入っているアドレスがポインタqのアドレスと等しい
80
-
81
- と等しい
82
80
 
83
81
 
84
82
 

6

説明追加、その2

2018/02/11 04:42

投稿

rubato6809
rubato6809

スコア1380

test CHANGED
@@ -72,7 +72,17 @@
72
72
 
73
73
  ・・・と書いたら問題ありまくりな感じなので図1を見ながら少しだけ解説を加えます。
74
74
 
75
- そもそも** &((*p)->next) の値は YN という番地**です。Y番地の構造体にある、nextポインタ自体のメモリアドレスです。q ポインタの値も YN です。だから一致するのです。たぶん質問者はそこがわかっていない。
75
+ そもそも** &((*p)->next) の値は YN という番地**です。Y番地の構造体にある、nextポインタ自体のメモリアドレスです。**q ポインタの値も YN **です。だから一致するのです。たぶん質問者はそこがわかっていない。
76
+
77
+
78
+
79
+ > (*p)->next)はポインタpのnextでそのnextに入っているアドレスがポインタqのアドレス
80
+
81
+ と等しい
82
+
83
+
84
+
85
+ 「ポインタqのアドレスと等しい」というところに混乱がある。図にも表せていないようだ。
76
86
 
77
87
 
78
88
 

5

追加説明の字句をわずか修正

2018/02/11 04:38

投稿

rubato6809
rubato6809

スコア1380

test CHANGED
@@ -72,7 +72,7 @@
72
72
 
73
73
  ・・・と書いたら問題ありまくりな感じなので図1を見ながら少しだけ解説を加えます。
74
74
 
75
- そもそも &((*p)->next) は YN という番地です。Y番地の構造体にある、nextポインタ自体のメモリアドレスです。q ポインタの値も YN です。だから一致するのです。たぶん質問者はそこがわかっていない。
75
+ そもそも** &((*p)->next) の値は YN という番地**です。Y番地の構造体にある、nextポインタ自体のメモリアドレスです。q ポインタの値も YN です。だから一致するのです。たぶん質問者はそこがわかっていない。
76
76
 
77
77
 
78
78
 

4

&((*p)->next) == q の説明追加

2018/02/11 04:26

投稿

rubato6809
rubato6809

スコア1380

test CHANGED
@@ -67,6 +67,12 @@
67
67
  さらに、その2つの next ポインタの値を代入した r と s が指している Y と Z が交換対象である。交換しようとしているのが Y と Z である事と、p, q が指している場所を、勘違いしないようにしましょう。
68
68
 
69
69
  つまり ``` if (&((*p)->next) == q) ``` の条件は**隣り合う2つを交換する場合**を意味します。ここ、問題ありませんよね?
70
+
71
+
72
+
73
+ ・・・と書いたら問題ありまくりな感じなので図1を見ながら少しだけ解説を加えます。
74
+
75
+ そもそも &((*p)->next) とは YN という番地です。Y番地の構造体にある、nextポインタ自体のメモリアドレスです。q ポインタの値も YN です。だから一致するのです。たぶん質問者はそこがわかっていない。
70
76
 
71
77
 
72
78
 

3

質問者のコメントを回答に追加。

2018/02/11 04:16

投稿

rubato6809
rubato6809

スコア1380

test CHANGED
@@ -46,7 +46,11 @@
46
46
 
47
47
 
48
48
 
49
- X番地、Y番地、Z番地にある3つの構造体が X ⇒ Y ⇒ Z というつながりになっている状態です。構造体の先頭アドレスに加えて、構造体中の next, before ポインタのアドレスを XN, XB, YN, YB ... としました。p ポインタの値は XN だけど、XN は(Xの)next ポインタのアドレスであって、next ポインタの値ではない事を念押しします。
49
+ X番地、Y番地、Z番地にある3つの構造体が X ⇒ Y ⇒ Z というつながりになっている状態です。構造体の先頭アドレスに加えて、構造体中の next, before ポインタのアドレスを XN, XB, YN, YB ... としました。p ポインタの値は XN であり、XN は(Xの)next ポインタのアドレスであって、next ポインタの値ではない事を念押しします。
50
+
51
+ **p の値は next ポインタのアドレスである、即ち next ポインタをポイントしている。**
52
+
53
+
50
54
 
51
55
  全てのデータはメモリ上にある=**全てのデータにメモリアドレスがある**。ポインタが活躍するプログラムを読み書きする時、メモリの値なのか、メモリのアドレスなのか、きちんと把握することは大事です。だから図を描いてみると良いのです。
52
56
 

2

てにをは修正

2018/02/11 00:43

投稿

rubato6809
rubato6809

スコア1380

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

1

回答追加。else以下の解説

2018/02/11 00:25

投稿

rubato6809
rubato6809

スコア1380

test CHANGED
@@ -62,10 +62,108 @@
62
62
 
63
63
  さらに、その2つの next ポインタの値を代入した r と s が指している Y と Z が交換対象である。交換しようとしているのが Y と Z である事と、p, q が指している場所を、勘違いしないようにしましょう。
64
64
 
65
+ つまり ``` if (&((*p)->next) == q) ``` の条件は**隣り合う2つを交換する場合**を意味します。ここ、問題ありませんよね?
65
66
 
66
67
 
67
- ``` if (&((*p)->next) == q) ``` の条件は**隣り合う2つを交換する場合**を意味します。ここ、問題ありませんよね?
68
68
 
69
- 従って X ⇒ Y ⇒ Z というつながりを、X ⇒ Z ⇒ Y というつながりに変えようとしています。各変数の値・各ポインタの値がそれぞれどうなればよいか、考えてみてください。
69
+ X ⇒ Y ⇒ Z というつながりを、X ⇒ Z ⇒ Y というつながりに変えようとしています。各変数の値・各ポインタの値がそれぞれどうなればよいか、考えてみてください。
70
70
 
71
+
72
+
73
+ 次に、``` if (&((*p)->next) == q) ``` が成り立たない場合とは、**離れた構造体を交換する場合**です。
74
+
75
+ 操作対象の構造体がひとつあると、その前後の構造体の next, before が変更を受けるので、都合3つの構造体に影響します。exchange()は2つを交換するから、最大で6個の構造体が影響を受けるという事。その6つを A ⇒ B ⇒ C ⇒ D ⇒ E ⇒ F とします。交換対象は B と E です。p, q, r, s の各ポインタがどこをポイントしているか、ご確認ください。
76
+
77
+ 図2
78
+
79
+
80
+
81
+ ![図2,6つの構造体](27415013992132696137fefbc5329894.jpeg)
82
+
83
+
84
+
85
+ B と E を交換したら、どうなるか。 A ⇒ B ⇒ C ⇒ D ⇒ E ⇒ F は A ⇒ E ⇒ C ⇒ D ⇒ B ⇒ F というつながりにすることが目的ですが、リスト構造ではデータそのもの(構造体、その中の name[], address[]など)を動かさず、ポインタを付け替えて順番を変えるのです。次は交換後の図です。
86
+
87
+ 図3
88
+
89
+
90
+
91
+ ![図3、交換後](641b9ebeaaf37de52254ac44f98d8350.jpeg)
92
+
93
+
94
+
95
+ 例えば、A番地の構造体のnextポインタ(AN番地)が「B→E」とあるのは、この操作でnextポインタの値がBからEに変更される事を表します。
96
+
97
+ この、ポインタの付け替えをしているのが else 以下のコードです。
98
+
99
+ ```C
100
+
101
+ r = *p; s = *q;
102
+
103
+ if (&((*p)->next) == q) {
104
+
105
+ // 隣り合う2つを交換する(省略)
106
+
107
+ } else {
108
+
109
+ // 離れた2つを交換する
110
+
111
+ if (s->next != 0) {
112
+
113
+ s->next->before = r;
114
+
115
+ }
116
+
117
+ if (r->next != 0) {
118
+
119
+ r->next->before = s;
120
+
121
+ }
122
+
123
+
124
+
125
+ // 2つの before ポインタを交換する
126
+
127
+ t = s->before;
128
+
129
+ s->before = r->before;
130
+
131
+ r->before = t;
132
+
133
+
134
+
135
+ // 2つの next ポインタを交換する
136
+
137
+ t = r->next;
138
+
139
+ r->next = s->next;
140
+
141
+ s->next = t;
142
+
143
+
144
+
145
+ *p = s;
146
+
147
+ *q = r;
148
+
149
+ }
150
+
151
+ ```
152
+
153
+ t ポインタを使った3行のパターンが2つあります。これは X と Y を交換する時、一時変数 t を使って
154
+
155
+ ```C
156
+
157
+ t = X;
158
+
159
+ X = Y;
160
+
161
+ Y = t;
162
+
163
+ ```
164
+
165
+ とするのと同じです。図3で言えば B と E の next, before 2つの交換です。他にも同じパターンで交換できる所がありそうですが、
166
+
167
+ ともかく全部で8本のポインタを書き換えるべきことが図から分かりますし、コード上にもポインタの値を書き換えるところが8行あります。質問者は自ら手を動かして、その8箇所について、図とコードの対応をとってみると良いと思う。
168
+
71
- (続きは後で
169
+ まだ