双方向リストについて　構造体のポインタの扱い


以下のコードは、5つの数値を入力で受け取り、受け取った順にnumberlistとして出力しその後、逆順でnumberlistを出力するプログラムです。

NULLの扱いが悪いのか、双方向リスト自体が間違っているのかよく分かりません。

[code]

    #include <stdio.h>
    #include <stdlib.h>
    
    struct number{
      int num;
      struct number *next, *prev;
    };
    
    int main(){
      struct number *p, *head, *start, *last;
      int i;
      start=(struct number *)malloc(sizeof(struct number));
      last=(struct number *)malloc(sizeof(struct number));
      head=(struct number *)malloc(sizeof(struct number));
      head->next=head;
      head->prev=head;
      head->num=NULL;
      printf("input 5 number:");
      for(i=0;i<5;i++){
        if(i==0){
          p=(struct number *)malloc(sizeof(struct number));
          p->prev=head;
          start=p;
        }
        p->next=(struct number *)malloc(sizeof(struct number));
        (p->next)->prev=p;
        scanf("%d", &p->num);
        p=p->next;
        last=p;
      }
      last->next=head;
      p=start;
      printf("number list:");
      while(p->num!=NULL){
        if(p==last){
          printf("%d", p->num);
        }else{
        printf("%d, ", p->num);
        }
        p=p->next;
      }
      printf("\n");
      p=last;
      printf("number list:");
      while(p->num!=NULL){
        if(p==start){
          printf("%d", p->num);
        }else{
        printf("%d, ", p->num);
        }
        p=p->prev;
      }
      printf("\n");
      return 0;
    }
    
[/code]




**追記：元々の何が問題か？**  
大本の間違いは以下2点です。

  * リスト先頭/最終判定のために、無効なデータ(num=NULL)を持つダミーデータを付けようとして、最終ダミーデータの処理に失敗していること。
  * 有効な最後のデータから逆順表示しようとする際に、ダミーデータから始めていること。
[code]     先頭(ダミー)<=>データ１<=>...<=>データ５<=>最終(ダミー(になっていない))<=>先頭へ戻る

    
[/code]

カンマ問題は別にして、リスト処理を最小限の修正で済ませるには以下2点を行います。

  * `for`ループの`last=p;`の後に`last->num = NULL;`を追加する。
  * 逆順表示の開始ポインタ設定`p=last;`を`p=last->prev;`に変更する。

おそらくリストの終端を判定する方法を模索してダミーデータに行きつき、そのための処理を作ろうとして色々と冗長かつおかしな処理になったものと思われます。  
一応 **循環リスト** を意識して作られてはいるようです。  
**改訂：簡潔に作り変えるとすると、こんな風に考えられます。**

  * 変数は初期化しておきましょう。あと`head`は不要です。`last`もこの時点では不要です。
[code]     struct number *p, *head, *start, *last;

    
[/code]

  * これは不要です。
[code]     start=(struct number *)malloc(sizeof(struct number));

    last=(struct number *)malloc(sizeof(struct number));
    head=(struct number *)malloc(sizeof(struct number));
    head->next=head;
    head->prev=head;
    head->num=NULL;
    
[/code]

  * `for`ループの中は、何の処理・設定が必要で、その順序はどうやれば早くて少なくて済むか考えた方が良いでしょう。片方向リストの質問では指摘しませんでしたが、ループカウンタの`i`も`for`内でしか使わないので短くできます。  
細かいところはソースで。

  * これも不要です。
[code]     last->next=head;

    
[/code]

  * `while(p->num!=NULL){`の`num` が `NULL`(=0) でない間ループという判定は、ダミーデータが前提なので、全部有効なデータだとしてポインタが元に戻った判定に変更します。

  * 表示は、片方向リスト時の @metropolis さんの処理が簡潔ですね。

まとめると以下のようになります。

[code]

    int main() {
        struct number* p = NULL, * start = NULL;  // 初期化を必要なだけ行う
        printf("input 5 number:");
        for (int i = 0; i < 5; i++) {
            // 変数宣言およびループ内処理を整理
            p = (struct number*)malloc(sizeof(struct number));
            if (i == 0) {
                start = p;
                // 循環リストで最初は自分自身を指している
                p->prev = p;
                p->next = p;
            }
            else {
                // 循環リストの最後に挿入する処理
                p->prev = start->prev;
                p->next = start;
                start->prev = p;
                p->prev->next = p;
            }
            int result = scanf_s("%d", &p->num);  // 古いscanfの代り
        }
        // --以後は入力されたデータの表示処理--
        p = start;
        printf("number list:");
        do {
            printf("%d%s", p->num, (p->next != start ? ", " : ""));
            p = p->next;
        } while (p != start);
        printf("\n");
        struct number* last = start->prev; // 逆順表示の終了判定簡略化のため
        p = last;
        printf("number list:");
        do {
            printf("%d%s", p->num, (p->prev != last ? ", " : ""));
            p = p->prev;
        } while (p != last);
        printf("\n");
        return 0;
    }
    
[/code]

**こんな記事を参照してみてください。**  
[連結リスト①（単方向・線形） | Programming Place Plus アルゴリズムとデータ構造編【データ構造】
第３章](https://programming-
place.net/ppp/contents/algorithm/data_struct/003.html)  
[連結リスト②（双方向・循環） | Programming Place Plus アルゴリズムとデータ構造編【データ構造】
第４章](https://programming-
place.net/ppp/contents/algorithm/data_struct/004.html)

