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

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

ただいまの
回答率

89.87%

C言語 線形リスト

受付中

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,876

Inabahitsuji

score 6

初歩的な質問で非情に申し訳ないのですがどなたかお力添えをして頂けないでしょうか。
下記のコードの機能を新規作成、追加、ソートに変更したいのですが一向にうまくいかず、詰まっている状態です。

「機能説明」
・新規作成…番号、氏名、得点を入力
・追加…新規作成によって入力されたものをリストに格納
・ソート…番号順や得点順を昇順降順に切り替えれるもの

・追記・
質問内容の指摘ありがとうございます、こういった場で尋ねるのは初めてでどうお聞きすればいいのかさえ分からず本当に申し訳ありません…。

新しく作る「新規作成機能」は下記の「追加機能」を利用したいのですが、そのままでは上記の機能説明で言うところの「新規作成」と「追加」を同時に行ってしまうため、新規作成で入力・追加で格納するといった形にしたいのです。

構築環境
win10
visualStudio2015

「実行結果のイメージ」
メニューを入力して下さい
1.新規作成2.追加3.ソート4.終了
1
番号:xx
氏名:yy
得点:zz

新規作成が完了しました
このデータを追加する場合はメニューにて追加を選択して下さい

メニューを入力して下さい
1.新規作成2.追加3.ソート4.終了

まだ追加されていないデータがありますがよろしいですか?
1.はい2.いいえ
2

メニューを入力して下さい
1.新規作成2.追加3.ソート4.終了
2

追加が完了しました

メニューを入力して下さい
1.新規作成2.追加3.ソート4.終了
3

追加されたデータのソートを行います
1.番号順2.得点順
1

番号1 氏名xx 得点yy

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFSIZE 20
#define DATASIZE 100


    typedef struct address {
    int szNum;  //番号
    char szName[10]; //氏名
    int szPoint;  //得点
} Numress;

   int search(Numress *ap, int cnt);
   int add(Numress *ap, int cnt);
   int amend(Numress *ap, int cnt);
   int del(Numress *ap, int cnt);
   int printall(Numress *ap, int cnt);

   /* 関数のジャンプテーブル */
   int(*func[])(Numress *, int) = { search, add, amend, del, printall };

   /* 構造体の配列 -- 大きくてスタックを消費する可能性があるので静的に定義した */
   Numress ad[DATASIZE];

   //処理の選択
   int main(void)
   {
       char dummy[BUFSIZE];
       int i, cnt = 0;

       while (1) {
           puts("●メニューを入力して下さい");
           puts("1.検索\n2.追加\n3.修正\n4.削除\n5.全体表示\n6.終了");
           scanf("%d", &i); gets(dummy);
           if (1 <= i && i <= 5) cnt = func[i - 1](ad, cnt);
           else if (i == 6) break;
       }

       return 0;
   }
   int search(Numress *ap, int cnt)
   {
       char buf[BUFSIZE], dummy[BUFSIZE];
       char *target[] = { "番号", "氏名", "得点" };
       int i, j = 0, count = 0;
       Numress *t = ap;

       while (1) {
           puts("何で探しますか?\n1.番号 2.氏名 3.得点");
           scanf("%d", &i); gets(dummy);
           if (1 <= i && i <= 3) break;
       }
       printf("%sを入力して下さい:", target[--i]);
       gets(buf);

       while (j++ < cnt) {
           switch (i) {
           case 0: /* 番号 */
               if (!strcmp(t->szNum, buf)) goto print;
               break;
           case 1: /* 氏名 */
               if (!strcmp(t->szName, buf)) goto print;
               break;
           case 2: /* 得点 */
               if (!strcmp(t->szPoint, buf)) goto print;
               break;
           default: /* no condition、ここには来ない */
               break;
           }
           t++;
           continue;
       print:;
           printf("%d: 番号:%s 氏名:%s 得点:%s\n", j, t->szNum, t->szName, t->szPoint);
           t++;
           count++;
       }
       printf("%d件Hit\n", count);

       return cnt;
   }

   int add(Numress *ap, int cnt)
   {
       Numress *t = ap + cnt;

       if (cnt == DATASIZE) {
           puts("これ以上追加できません");
           return cnt;
       }
       printf("番号:");
       gets(t->szNum);
       printf("氏名:");
       gets(t->szName);
       printf("得点:");
       gets(t->szPoint);

       return cnt + 1;
   }

   int amend(Numress *ap, int cnt)
   {
       Numress *t;
       char buf[BUFSIZE];
       int i;

       puts("何番を修正しますか?");
       scanf("%d", &i); gets(buf);

       if (i > 0 && i <= cnt) {
           t = ap + i - 1; /* 配列のアドレスの計算 */
           puts("変更しない場合は単に[return]を押して下さい");
           printf("番号: %s -> ", t->szNum);
           gets(buf);
           if (strlen(buf)) strcpy(t->szNum, buf);
           printf("氏名: %s -> ", t->szName);
           gets(buf);
           if (strlen(buf)) strcpy(t->szName, buf);
           printf("得点: %s -> ", t->szPoint);
           gets(buf);
           if (strlen(buf)) strcpy(t->szPoint, buf);
       }
       else
           puts("番号が不適切です");
       return cnt;
   }
   int del(Numress *ap, int cnt)
   {
       char buf[BUFSIZE];
       int i, j;

       puts("何番を削除しますか?");
       scanf("%d", &i); gets(buf);

       if (i > 0 && i <= cnt) {
           for (j = i - 1; j < cnt - 1; j++)
               ap[j] = ap[j + 1];
           return cnt - 1;
       }

       puts("番号が不適切です");
       return cnt;
   }


   int printall(Numress *ap, int cnt)
   {
       int i = 0;

       while (i < cnt) {
           printf("%d: 番号:%s 氏名:%s 得点:%s\n", ++i, ap->szNum, ap->szName, ap->szPoint);
           ap++;
       }

       return cnt;
   }
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • cateye

    2016/03/20 06:25 編集

    >一向にうまくいかず・・・
    何がうまくいかないのでしょう? 環境(OS,コンパイラなど)を教えてください。また、gets()は推奨されません。fgets()を使いましょう。

    キャンセル

回答 2

+1

typedef struct address {
    int szNum;  //番号
    char szName[10]; //氏名
    int szPoint;  //得点
} Numress;


上記構造では線形リストは構成できません。(ポインタを他に持つなら別ですが…)
また、Numress *t = ap + cnt;など配列に対するアクセス方法になっているのは何故でしょう?
線形リストの実装方法など検索して調べてみてください。
「追記」
Cでのリスト操作については、下記サイトが比較的わかりやすいと思います。
連結リスト

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/03/20 13:37

    ご指摘ありがとうございます、ネットの解説を見ても今一つ飲み込めず理解に至らなかった結果この様な結果になってしまったのです…。
    もう一度改めて一から調べ直しますね、お手数お掛けしました。
    もし行き詰ったときにまたアドバイスを頂けると凄く嬉しいです、本当にありがとうございました。

    キャンセル

  • 2016/03/27 10:28

    ソートの処理については、表示だけであれば、各データのポインタを他に持ってポインタのみソートする方法が簡単だと思います。実データの入れ替えならばポインタはそのままに中身だけ入れ替える関数(リストは壊さずに中身だけ変える…)を定義して行えばいいと思います。当然、比較関数は必要ですが。

    キャンセル

0

記述してみました。

コード説明

質問の変更したい内容のとおり、完全に別れた機能として「新規作成」と「追加」、また、番号によるソート機能のコードを記述しました。
新規作成で一時的な構造体へ保存して、追加でメインの構造体へ移動させる、という方式をとり、その都度リストを表示させ中身を確認、一括削除できるようにしました。

点数でのソートに関しては番号でのソートと同じ要領で記述すれば実装できます。また、ソート自体に関しても他にもたくさんアルゴリズムがあるようなので、自分のプログラムにあったものを探してみるといいかとおもいます。

そして、一番大事なことですが、私はVisual Studioではコンパイルできませんでした。
当方、Eclipseですべて記述しました。

そのまま以下のコードをコピペするとエラーを大量に吐きます。
解決方法としては、まず、コンパイル済みヘッダーを読み込まないようにします。
つづいて、sscanfを使用しているのでこれを使用できるようにソースコードに一行記述を加えます(ググればすぐでてきます)。
そして最後に残るエラーですが、マージソートを扱う際に可変長配列を定義している部分になりますが、Visualstudioでは拡張子をcにしてC言語としてコンパイルしても、このような記述はできないようです。ですので工夫が必要かと思われます。

以下にソースコードと、実行結果のスクショを載せました。すこしでも参考になれば幸いです。

実行結果

実行結果1
実行結果2
実行結果3
実行結果4

ソースコード

#include <stdio.h>
#include <string.h>
#define MAX 100    /*保存可能な最大人数,名前入力の最長文字数*/

/*学生リストの型を定義*/
struct student{
    int id;        /*番号*/
    char name[MAX];    /*名前*/
    int score;    /*得点*/
};

/*構造体内の配列を関数に渡すために型を宣言*/
typedef struct student type_list;

/*変数*/
type_list list_all[MAX];    /*追加された全学生の構造体配列*/
type_list list_temp[MAX];    /*まだ追加されていない新規の学生の構造体配列*/
int menu=4;    /*メニュー指示番号(1~4)*/
char line[MAX];    /*キーボードによる入力, 1行分*/
int i;    /*カウンター*/
int number_temp=0;    /*新規追加されたtemp内の件数*/
int number_all=0;    /*追加済みの学生数の数*/

/*関数宣言文*/
void newStudent();    /*新規作成用*/
void viewTemp();    /*作成済み構造体リスト中身確認用*/
void viewAll();        /*追加済み構造体リスト中身確認用*/
void deleteTemp();    /*作成済み構造体リスト初期化*/
void add();            /*新規作成分を追加(list_allに保存)*/

void sort();        /*マージソート呼び出し*/
void merge();        /*マージソート, 結合部分*/
void mergeSort();    /*マージソート, 分割部分*/

/*メイン関数*/
int main(void){

    /*終了が指示されるまで何度もメニューを回すためにループ*/
    while(1){
        puts(">>メニューを入力して下さい (半角数字1~4)");
        puts("1.新規作成 2.追加 3.ソート 4.終了 ");
        fflush(0);    /*バッファの吐き出し (Eclipse等のIDEによってはこれをやらないと出力が表示されない場合がある)*/

        fgets(line,sizeof(line),stdin);    /*キーボードの入力を読み取る*/
        sscanf(line,"%d",&menu);    /*数値として メニュー指示変数 "menu" へ格納*/

        switch(menu){
        case 1:
            newStudent();    /*新規作成*/
            break;
        case 2:
            add();            /*追加*/
            break;
        case 3:
            sort();            /*ソート*/
            break;
        case 4:
            puts(">>終了します。 Bye-Bye");
            return 0;    /*リターンによってプログラムを終了*/
        default:
            puts(">>半角数字 1~4 を一文字のみ入力してください。");    /*想定外の入力に対して警告し,メニューの最初から*/
            break;
        }
    }
    return 0;
}

void newStudent(){

    /*学生情報の入力をループ*/
    while(1){
        puts(">>学生の情報を入力してください。");
        printf("番号:");
        fflush(0);
        fgets(line,sizeof(line),stdin);    /*キーボードの入力を読み取る*/
        sscanf(line,"%d",&list_temp[number_temp].id);    /*数値として 新規学生構造体配列へ順番に格納*/

        printf("氏名:");
        fflush(0);
        fgets(line,sizeof(line),stdin);    /*キーボードの入力を読み取る*/
        sscanf(line,"%s",&list_temp[number_temp].name);    /*数値として 新規学生構造体配列へ順番に格納*/

        printf("点数:");
        fflush(0);
        fgets(line,sizeof(line),stdin);    /*キーボードの入力を読み取る*/
        sscanf(line,"%d",&list_temp[number_temp].score);    /*数値として 新規学生構造体配列へ順番に格納*/

        ++number_temp;    /*作成件数を +1*/

        //printf("%d %s %d",list_temp[number].id,list_temp[number].name,list_temp[number].score);

        puts(">>新規作成されました。指示を入力してください。");
        puts("1.続けて新規作成 2.作成された一覧を表示 3.メニューへ戻る");
        fflush(0);
        fgets(line,sizeof(line),stdin);    /*キーボードの入力を読み取る*/
        sscanf(line,"%d",&menu);    /*数値として メニュー指示変数 "menu" へ格納*/

        switch(menu){
        case 1:
            break;    /*ループを続行*/
        case 2:
            viewTemp();    /*作成された一覧を表示*/
            return;
        case 3:
            return;
        }
    }
    return;
}

void viewTemp(){

    puts("--------------------");
    for(i=0;i<number_temp;i++){
        printf(">> 番号: %d, 指名: %s, 点数: %d \n",list_temp[i].id,list_temp[i].name,list_temp[i].score);
    }
    puts("--------------------");

    printf(">>新規で %d 件,作成されました。まだ追加はされておりません。\n",number_temp);
    puts(">>指示を入力してください。さらに追加する場合は一度メニューにもどり、新規作成を選択してください。");
    puts("1.メニューに戻る 2.これら新規データを消去");
    fflush(0);
    fgets(line,sizeof(line),stdin);    /*キーボードの入力を読み取る*/
    sscanf(line,"%d",&menu);    /*数値として メニュー指示変数 "menu" へ格納*/

    /*削除が指示された場合*/
    if(menu==2){
        deleteTemp();
        puts("追加せず、削除しました。");
    }
    return;

}

void viewAll(){
    puts("--------------------");
    for(i=0;i<number_all;i++){
        printf("番号: %d, 指名: %s, 点数: %d \n",list_all[i].id,list_all[i].name,list_all[i].score);
    }
    puts("--------------------");

    printf(">>現在合計で %d 件です。\n",number_all);

    return;
}

void deleteTemp(){

    for(i=0;i<number_temp;i++){
        list_temp[i].id=0;
        list_temp[i].name[0]='\0';
        list_temp[i].score=0;
    }
    number_temp=0;

    return;
}

void add(){

    /*構造体配列内の対応する各要素ごとに代入*/
    for(i=0;i<number_temp;i++){
        list_all[number_all + i].id=list_temp[i].id;
        strcpy(list_all[number_all + i].name,list_temp[i].name);    /*文字列なのでstrcpyを使用*/
        list_all[number_all + i].score=list_temp[i].score;
    }

    printf("既存の %d 件に,新規の %d 件が加わり、合計 %d 件になりました。\n",number_all, number_temp,number_all+number_temp);

    number_all+=number_temp;    /*追加済み件数を更新*/
    deleteTemp();    /*新規作成リストtemp内をクリアし、作成件数もリセット*/

    viewAll();    /*追加済みリスト確認*/

    return;
}


/*--------------------------ソート関係の関数---------------------------
*
*    採用アルゴリズム: マージソート
*
*    変数: 追加済み生徒の構造体リスト, 及びその件数
*
*    関数:sort()            ソートのメイン関数,帰納の復帰先
*         merge()        二つの分割から一つの構造体へ小さい順に並べ替える
*         mergeSort()    帰納させる関数,構造体を二つの構造体へ分割
*
*    動作: 構造体内の比較対象となる配列で比較し、番号、名前、点数すべてを毎回移動させる
*
--------------------------------------------------------------------*/


/*帰納の復帰先*/
void sort(){
    puts(">>追加済み件数を何でソートしますか?(作成されたが未追加のものはソートの対象ではありません。)");    /*構造体list_tempはソートの対象外*/
    puts("1.番号 2.得点 3.メニューへ戻る");
    fflush(0);
    fgets(line,sizeof(line),stdin);    /*キーボードの入力を読み取る*/
    sscanf(line,"%d",&menu);    /*数値として メニュー指示変数 "menu" へ格納*/

    if(menu==1){    /*番号でソート*/

        mergeSort(list_all,number_all);
        viewAll();
        puts(">>学生番号によるソートが完了しました");
        puts("");

    }else if(menu==2){    /*得点でソート*/
        puts("未実装, メニューへもどります。");
    }else{
        /*それ以外の入力に対してはメニューへ戻る*/
    }
    return;
}

void merge(type_list *ALL, type_list *L, int L_count, type_list *R, int R_count){
    int i,j,k;    /*カウンター*/

    i=0;
    j=0;
    k=0;

    /*分割右側と分割左側を比較しながらALLへ統合させる*/
    /*どちらか片方の分割が全て統合されたら、あとは評価せず、他方はそのままALLの最後へ*/
    while(i<L_count && j< R_count){

        /*分割右側と分割左側のそれぞれの最小値を比較*/
        /*小さい方を選択し,ALLへ移動,同時に名前,点数も移動*/
        if(L[i].id<R[j].id){
            ALL[k].id=L[i].id;
            strcpy(ALL[k].name,L[i].name);
            ALL[k].score=L[i].score;
            k++;
            i++;
        }else{
            ALL[k].id=R[j].id;
            strcpy(ALL[k].name,R[j].name);
            ALL[k].score=R[j].score;
            k++;
            j++;
        }
    }

    /*評価せずに終わった方をそのままALLへ結合させるコード*/
    while(i<L_count){    /*右側配列が先に全てALLへ統合された場合*/
        ALL[k].id=L[i].id;
        strcpy(ALL[k].name,L[i].name);
        ALL[k].score=L[i].score;
        k++;
        i++;
    }

    while(j<R_count){    /*左側配列が先に全てALLへ統合された場合*/
        ALL[k].id=R[j].id;
        strcpy(ALL[k].name,R[j].name);
        ALL[k].score=R[j].score;
        k++;
        j++;
    }

/*    デバッグ用 (R と L の中身を確認する)
    for(i=0;i<L_count;i++){
        printf("L[%d] - %d %s %d\n",i,L[i].id,L[i].name,L[i].score);
    }
    for(i=0;i<R_count;i++){
        printf("R[%d] - %d %s %d\n",i,R[i].id,R[i].name,R[i].score);
    }*/
}

void mergeSort(type_list *ALL, int n){
    int mid;    /*構造体配列の真ん中を定義*/
    mid = n/2;

    if(n<2) return;    /*帰納の復帰*/

    type_list L[mid];    /*分割左側を格納する構造体を定義*/
    type_list R[n-mid];    /*分割右側を格納する構造体を定義*/

    /*与えられた構造体(最初の一回目の呼び出しでは追加済み学生リスト)の2つに分割する*/
    /*左側を構造体 L へ, 右側を構造体 R へ*/
    for(i=0;i<mid;i++) {
        L[i].id=ALL[i].id;
        strcpy(L[i].name,ALL[i].name);
        L[i].score=ALL[i].score;
    }

    for(i=mid;i<n;i++){
        R[i-mid].id=ALL[i].id;
        strcpy(R[i-mid].name,ALL[i].name);
        R[i-mid].score=ALL[i].score;
    }

/*    デバッグ用 (R と L の中身を確認する)
    for(i=0;i<mid;i++){
        printf("L[%d] - %d %s %d\n",i,L[i].id,L[i].name,L[i].score);
    }
    for(i=0;i<mid;i++){
        printf("R[%d] - %d %s %d\n",i,R[i].id,R[i].name,R[i].score);
    }*/

    /*----------帰納処理----------*/
    /*分割する関数の中でその関数自体を呼び出し、分割し続ける*/
    /*比較対象が (n<2) になり、returnするまで分割し続ける。*/
    mergeSort(L,mid);    /*分割された左側をさらに分割*/
    mergeSort(R,n-mid);    /*分割された右側をさらに分割*/

    merge(ALL,L,mid,R,n-mid);    /*returnして戻ってきたものをソートして結合*/
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/12 23:09

    こちらのVisual Stdio 2017環境では、コンパイルできました。(コマンドライン)
    ただし、Warningの山で、まともに動くとは思えませんでしたが、、。
    文字列と数値の区別がついていないように思われます。

    キャンセル

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

  • ただいまの回答率 89.87%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる