

入力されたアルファベットのランダムな文字列を、アスキーコードの昇順で並び替えて出力したいです。

**例**  
元の文字列: `bfGageGaheifhalenbcuafuhneixlDbfuhflfi0`  
並べ替え後: `DGGaaaaabbbceeeeffffffghhhhiiilllnnuuux`

数字が入力された時に入力を終了して出力します。

以下、自分で書いてみたのですが出力に最後の改行しか表示されません。  
コードの訂正お願いします。

追記  
現時点での明白なミスは修正しました。

head.next = &tail;の初期化によってうまくいかないとご意見をいただきましたが、具体的にどのように修正すればよいでしょうか。

[code]

    #include <stdio.h>
    #include <stdlib.h>
    
    struct ascii{
      int num;
      struct ascii *next;
    };
    
    
    int main(){
      int i;
      char c;
      struct ascii head, tail;
      head.next = &tail;
      tail.num=10000;
      for(;(i=getchar())<48 || i>57;){
        if(i==10){
          continue;
        }else{
        struct ascii *p=(struct ascii *)malloc(sizeof(struct ascii));
        p->num=i;
        struct ascii *w;
        for (w = &head; w->next != &tail;){
        if(w->next->num < p->num){
          w = w->next;
          if(w->next->num >= p->num){
        p->next=w->next;
            w->next=p;
        break;
          }
        }else{
          p->next=w->next;
        w->next=p;
        break;
        }
        }
        }
      }
      struct ascii *q;
      for(q=&head;q->next!=&tail;q=q->next){
        c=q->num;
        printf("%c", c);
      }
      printf("\n");
      return 0;
    }
    
[/code]


CC BY-SA 4.0
2019-12-10T05:55:48.990
61292
2019-12-10T18:51:04.567
2019-12-10T16:30:31.777
3060
36484
question
c ポインタ
ポインタを用いて入力されたアルファベットをアスキーコード昇順で並び替えるコード


ぱっと見 `if(i=10)` は限りなく誤りっす。 [c](/questions/tagged/c "'c' のタグが付いた質問を表示") では、等号１つ
`=` は代入で、比較が欲しいのなら等号２つ `==` っす。


CC BY-SA 4.0
2019-12-10T06:21:48.433
61293
2019-12-10T06:21:48.433
8589
61292
answer


今までのコメントや回答を含めてまとめると、以下のようになるでしょう。  
指摘や変更内容はコメントとして記述しました。

[code]

    #include <stdio.h>
    #include <stdlib.h>
    
    struct ascii {
        int num;
        struct ascii* next;
    };
    
    int main() {
        /* 以下の構造体データは全て初期化 @metropolis さん指摘関連 */
        struct ascii tail = { INT_MAX, NULL }, head = { -1, &tail };
    
        /* i は for の中でしか使わないので定義を移動する */
        for (int i = 0; (i = getchar()) < 48 || i > 57;) { /* @metropolis さん指摘 && -> || */
            if (i == 10) { /* @774RR さん指摘 = -> == */
                continue;
            }
            else {
                struct ascii* p = (struct ascii*)malloc(sizeof(struct ascii));
                p->num = i;
                /*
                   w は for の中でしか使わないので定義を移動する。
                   ループ終了判定は NULLポインタ。 @metropolis さん指摘 w->next != &tail
                   次の構造体への移動はループ制御部の終端処理へ移動。w = w->next
                */
                for (struct ascii* w = &head; w->next != NULL; w = w->next) {
    
                    /* ループ内処理は以下で十分 */
                    if (w->next->num >= p->num) {
                        p->next = w->next;
                        w->next = p;
                        break;
                    }
                }
            }
        }
        /*
            q は for の中でしか使わないので定義を移動する。
            最初が &head だと最初のprintfが無効なデータになるので、head.next で初期化。
            ループ終了判定は NULLポインタ。 @metropolis さん指摘関連 w->next != &tail と同様
        */
        for (struct ascii* q = head.next; q->next != NULL; q = q->next) {
    
            /* c は for の中でしか使わないので定義を移動する */
            char c = q->num;
            printf("%c", c);
        }
        /* 有効な入力データが1件も無かった場合のガイド表示の考慮があればなお良いですね */
        printf("\n");
        return 0;
    }
    
[/code]


CC BY-SA 4.0
2019-12-10T08:53:10.547
61297
2019-12-10T08:53:10.547
26370
61292
answer


あなたのコードは、

`head`: ダミー要素、実際のリストに含まれる要素ではない  
`tail`: 番犬要素、実際に現れうる値より大きな値(`1000`)を保持することにより終端チェックを簡略化している

と言う条件での一方向リンクトリストを作成し、新要素は常に昇順となる位置に挿入することで、常に昇順で並んだ状態にしておく、と言うものです。

初期状態

[code]

    head           tail
    +-----+-----+  +-----+-----+
    | ????|&tail|->| 1000|?????|
    +-----+-----+  +-----+-----+
    
[/code]

最初の`bfG`を読み込んだ後

[code]

    head           [0]            [1]            [2]            tail
    +-----+-----+  +-----+-----+  +-----+-----+  +-----+-----+  +-----+-----+
    | ????|&[0] |->| 'G' |&[1] |->| 'b' |&[2] |->| 'f' |&tail|->| 1000|?????|
    +-----+-----+  +-----+-----+  +-----+-----+  +-----+-----+  +-----+-----+
    
[/code]

(このような構造はリンクトリストとしてはよくあるものです。したがって、「`head.next =
&tail;`の初期化によってうまくいかない」訳ではありません。)

この状態で'a'を読み込んだ場合、[1]の前、つまり[0]の後ろに新要素として追加しないといけません。

あなたのコードで間違っているのは、この部分で、リストが空の時、つまり`head.next = &tail;`の直後の状態では`for (w = &head;
w->next !=
&tail;){`...`}`のfor文が一度も実行されないため、リストは空のまま、次の文字がやってきても同じで、最初が空だと永久に要素が増やされることはない、と言うコードになってしまっています。

  * 新要素を挿入する位置を見つける
  * その位置に新要素を追加する

と言う二種類の処理を無理にfor文の中に押し込めてしまっているので、そのようなことが発生しています。両者はきちんと分けて考えた方が良いでしょう。

[code]

                //新要素を挿入する位置を見つける
                //「新要素の入る位置」はリンク内の1個手前の要素が必要なので、後半の全要素出力とちょっと違う
                struct ascii *w;
                for( w = &head; w->next != &tail; w = w->next ) {
                    if( w->next->num >= p->num ){
                        break;
                    }
                }
                //その位置に新要素を追加する
                p->next = w->next;
                w->next = p;
    
[/code]

このようにすると、for文が終了した時点で`w`が新要素を追加する位置(上の例で言うと[0])を指すことになります。また、リストが空なら`w`は先頭のダミー要素を指すので全く同じ処理で初要素を追加することができます。

また、あなたのコードではリスト内容を出力する部分にも問題があります。最初に書いたように、あなたのデータ構造では`head`はダミー要素なのにその内容を出力しますし、「`next`が`&tail`を指していない限りループ」(`q->next!=&tail`)だと、リストの最後の要素が出力されません。

その部分はこう書かないといけません。

[code]

        for( q = head.next; q != &tail; q = q->next ) {
            c = q->num;
            printf("%c", c);
        }
    
[/code]

* * *

念のため、両修正を取り込んだ後のコード全体を示しておきます。

[code]

    #include <stdio.h>
    #include <stdlib.h>
    
    struct ascii {
        int num;
        struct ascii *next;
    };
    
    int main() {
        int i;
        char c;
        struct ascii head, tail;
        head.next = &tail;
        tail.num = 10000;
        for( ; (i=getchar()) < 48 || i > 57; ){
            if( i == 10 ) {
                continue;
            } else {
                struct ascii *p = (struct ascii *)malloc(sizeof(struct ascii));
                p->num = i;
                //新要素を挿入する位置を見つける
                //「新要素の入る位置」はリンク内の1個手前の要素が必要なので、後半の全要素出力とちょっと違う
                struct ascii *w;
                for( w = &head; w->next != &tail; w = w->next ) {
                    if( w->next->num >= p->num ){
                        break;
                    }
                }
                //その位置に新要素を追加する
                p->next = w->next;
                w->next = p;
            }
        }
        struct ascii *q;
        //全要素出力
        //先頭はダミー要素なのでその次から(`q = head.next`)、番犬要素の手前までリストの一部なので`q != &tail`
        for( q = head.next; q != &tail; q = q->next ) {
            c = q->num;
            printf("%c", c);
        }
        printf("\n");
        return 0;
    }
    
[/code]

(余談ですが、Cで2スペインデントは殆ど見ないですね。演算子の両側に空白を置くと言うCでは非常によく普及したスペース配置のルールも守られていないのでコードが大変読みづらくなっています。もしかしたら授業(?)の先生(?)がそんなコーディングをするのかもしれませんが、できるだけ普通のCコミュニティのスタイルに合わせた方が良いですね。)

どこが本質的に必要な修正かを示すために、変数宣言などは元コードのままとしましたが、kunifさんの回答にあるように、for文の制御変数なんかは、できるだけfor文内部で宣言すると言うのがモダンなCのコーディングスタイルです。

* * *

余談が長くなりましたが、リンクトリストの処理では、空の場合、先頭要素の場合、末尾要素の場合などに特殊処理が必要になることがよくあります。ステップ実行トレース実行などが出来るデバッガが使えない環境でも、「空の時にどうなるか」は動きを脳内トレース出来るかと思いますので、そういう特殊ケースでどんな動作をするか、確認する癖をつけられると良いかと思います。


CC BY-SA 4.0
2019-12-10T18:51:04.567
61319
2019-12-10T18:51:04.567
13972
61292
answer