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

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

新規登録して質問してみよう
ただいま回答率
85.50%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

4回答

3783閲覧

C言語で名簿を作って年齢順に表示させたいのですがうまくいきません。

rinringo_88

総合スコア4

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2019/08/27 01:18

編集2022/01/12 10:55

}### 前提・実現したいこと
できるだけバブルソートの部分を入れ替えるのを繰り返すという方法で、年齢を小さい順に表示させたいです。
以下の画像に書いた考え方でバブルソートの部分をコーディングしたいです。
http://s.kota2.net/1567057495.png
http://s.kota2.net/1567057524.png

ここに質問の内容を詳しく書いてください。
プログラム初心者です。こちらのサイトで質問をするのも慣れておりませんので失礼があったら申し訳ありません。

C言語で名簿を表示させるプログラムを作成しました。
名簿の中身は以下のようになっております。

100 あ 30 男性
101 あ 20 女性
102 い 40 男性
103 う 30 男性
104 え 30 女性
105 お 20 男性
106 か 40 女性
107 き 50 男性
108 く 30 女性
109 け 40 男性
110 こ 20 女性
111 さ 50 男性
112 し 30 女性
113 す 40 男性
114 せ 20 男性
115 そ 30 女性
116 た 40 男性
117 ち 20 女性
118 つ 30 男性
119 て 50 男性
120 と 40 女性
121 な 50 男性
122 に 30 女性
123 ぬ 20 男性

名簿の年齢が小さい順に並べ替えたいのですが、当方の力不足で上手くできません。
特に、下の方のポインタ演算子の入れ替えの部分が上手いコードが思いつきません。

発生している問題・エラーメッセージ

バブルソートの部分が上手く入れ替えできず、for文が回らない。
コンパイルすると「ファイルのオープンに成功しました」という文章しか表示されない。

エラーメッセージ

該当のソースコード

C

1#include "stdafx.h" 2#include <stdio.h> 3#include <stdlib.h> 4 5struct ST_MEMBER 6{ 7 int number; //社員番号 8 char name[50]; //名前 9 int age; //年齢 10 char gender[10]; //性別 11 struct ST_MEMBER *prev; 12 struct ST_MEMBER *next; 13}; 14 15int main(void) 16{ 17 18 FILE *fp; 19 int i, j, max, ret; 20 struct ST_MEMBER *str, *str2 = 0, *str3 = 0, *temp; 21 22 if(fopen_s(&fp, "Meibo.txt", "r") != 0) //読み取りモードでファイルを開く 23 { 24 printf("ファイルのオープンに失敗しました\n"); 25 return -1; 26 } 27 28 printf("ファイルのオープンに成功しました\n"); 29 30 31 while(1) 32 { 33 //確保されたメモリの場所をあらわすポインタが得られる。 34 str = (struct ST_MEMBER *) malloc (sizeof(struct ST_MEMBER)); 35 if(!str) 36 { 37 printf("メモリが確保できませんでした。\n"); 38 return 1; 39 } 40 41 //構造体を数珠つなぎにする 42 if(str2 == 0) 43 { 44 str2 = str; 45 str2->prev = 0; 46 str2->next = 0; 47 } 48 else //0ではない場合 49 { 50 str2->next = str; 51 str->prev = str2; 52 str2 = str; 53 str2->next = 0; 54 } 55 56 ret = fscanf(fp, "%4d%16s%5d%5s\n", &str->number, str->name, &str->age, str->gender); 57 58 59 if(ret == -1) //EOF 60 break; 61 } 62 63 //先頭に戻る 64 for(i = 0; str2->prev != 0; i++) 65 str2 = str2->prev; 66 67 for(i = 0; (str2->next->age) != 0; i++) 68 { 69 70 if((str2->age) > (str2->next->age)) //年齢を小さい順に表示したいので年齢を比較する。 71 { 72 //全部の構造体を入れ替える(1行ずつ入れ替える) 73 74 str2->next = str2->next->next; 75 str2->prev = str2->next; 76 77 str->next->next = str2->next->prev; 78 str->next->prev = str2->prev; 79 80 } 81 } 82 83 84 85 for(i = 0; str2->next != 0; i++) 86 { 87 printf("%4d%16s%5d%5s\n", str2->number, str2->name, str2->age, str2->gender); 88 str2 = str2->next; 89 } 90 91 free(str); 92 93 fclose(fp); 94 95 return 0; 96} 97 98

試したこと

ここに問題に対して試したことを記載してください。
双方向リストの入れ替えについて以下の画像のように考えました。
http://s.kota2.net/1567057495.png
http://s.kota2.net/1567057524.png

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takabosoft

2019/08/27 01:24

ソースコードは ```c ここにコード ``` というように記述してください。質問欄は再編集できます。
rinringo_88

2019/08/27 01:50

ご指摘ありがとうございます。修正しました。
jimbe

2019/08/27 02:31

まず机上で双方向リストの絵を書いて, 入れ替える処理を1ステップずつ書き出して確認することをお勧めします. また, 双方向リストのソートのコードはネットに多数あると思います. 参考にされては如何でしょうか.
rinringo_88

2019/08/29 02:05

双方向リストですね。教えてくださってありがとうございます。
guest

回答4

0

たぶんバブルソートをやろうとしたと思うのですが、
読み込んで毎回リストの最後に追加するのではなく、
年齢に合わせて挿入する位置を変えていくアルゴリズムでどうでしょう?

※バブルソートが課題だったらすみません。

c

1#include "stdafx.h" 2#include <stdio.h> 3#include <stdlib.h> 4 5struct ST_MEMBER 6{ 7 int number; //社員番号 8 char name[50]; //名前 9 int age; //年齢 10 char gender[10]; //性別 11 struct ST_MEMBER *prev; 12 struct ST_MEMBER *next; 13}; 14 15int main(void) 16{ 17 18 FILE *fp; 19 int i, ret; 20 struct ST_MEMBER *temp, *list_head = NULL, *list_tail = NULL, *p; 21 22 if (fopen_s(&fp, "Meibo.txt", "r") != 0) //読み取りモードでファイルを開く 23 { 24 printf("ファイルのオープンに失敗しました\n"); 25 return -1; 26 } 27 28 printf("ファイルのオープンに成功しました\n"); 29 30 while (1) 31 { 32 //確保されたメモリの場所をあらわすポインタが得られる。 33 temp = (struct ST_MEMBER *)malloc(sizeof(struct ST_MEMBER)); 34 if (!temp) 35 { 36 printf("メモリが確保できませんでした。\n"); 37 return 1; 38 } 39 40 // ファイルから1行読み込みます。 41 ret = fscanf(fp, "%4d%16s%5d%5s\n", &temp->number, temp->name, &temp->age, temp->gender); 42 if (ret == -1) { 43 free(temp); 44 break; 45 } 46 47 // リストを構築します。 48 49 // リストが空っぽならば、先頭・最後として登録します。 50 if (list_head == NULL) 51 { 52 list_head = list_tail = temp; 53 list_head->prev = NULL; 54 list_head->next = NULL; 55 } 56 else 57 { 58 // tempより大きい要素を探し、その手前に挿入します。 59 for (p = list_head; p && temp->age > p->age; p = p->next) { 60 61 } 62 63 if (!p) { 64 // 見つからなかったため最後に追加 65 list_tail->next = temp; 66 temp->prev = list_tail; 67 temp->next = NULL; 68 list_tail = temp; 69 } 70 else 71 { 72 // pの前に挿入 73 temp->next = p; 74 temp->prev = p->prev; 75 if (p->prev) { 76 p->prev->next = temp; 77 } 78 p->prev = temp; 79 80 // 最初になる場合もある 81 if (list_head == p) { 82 list_head = temp; 83 } 84 } 85 } 86 87 } 88 89 // 出力 90 printf("---\n"); 91 for (p = list_head; p; p = p->next) { 92 printf("%4d%16s%5d%5s\n", p->number, p->name, p->age, p->gender); 93 } 94 95 96 // リストを削除します。 97 for (p = list_head; p; ) { 98 temp = p->next; 99 free(p); 100 p = temp; 101 } 102 103 104 fclose(fp); 105 106 //system("pause"); 107 return 0; 108}

出力:

text

1 123 ぬ 20 男性 2 117 ち 20 女性 3 114 せ 20 男性 4 110 こ 20 女性 5 105 お 20 男性 6 101 あ 20 女性 7 122 に 30 女性 8 118 つ 30 男性 9 115 そ 30 女性 10 112 し 30 女性 11 108 く 30 女性 12 104 え 30 女性 13 103 う 30 男性 14 100 あ 30 男性 15 120 と 40 女性 16 116 た 40 男性 17 113 す 40 男性 18 109 け 40 男性 19 106 か 40 女性 20 102 い 40 男性 21 121 な 50 男性 22 119 て 50 男性 23 111 さ 50 男性 24 107 き 50 男性

投稿2019/08/27 02:00

takabosoft

総合スコア8356

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rinringo_88

2019/08/28 01:57

詳しく書いてくださり、ありがとうございます。 今回はバブルソートで書きたいと思っています。 せっかく教えて頂いたのに申し訳ありません。
takabosoft

2019/08/28 02:27

ですよね、そんな気はしました。
guest

0

main だけでやってみました。

C

1//#include "stdafx.h" 2#include <stdio.h> 3#include <stdlib.h> 4 5struct ST_MEMBER { 6 int number; //社員番号 7 char name[50]; //名前 8 int age; //年齢 9 char gender[10]; //性別 10 struct ST_MEMBER *prev; 11 struct ST_MEMBER *next; 12}; 13 14int main(void) 15{ 16 struct ST_MEMBER buf, *p, *q; 17 buf.prev = buf.next = &buf; 18 19 FILE *fp; 20 if (fopen_s(&fp, "Meibo.txt", "r")) { 21 printf("ファイルのオープンに失敗しました\n"); return -1; 22 } 23 printf("ファイルのオープンに成功しました\n"); 24 25 while (fscanf(fp, "%d%49s%d%9s", 26 &buf.number, buf.name, &buf.age, buf.gender) == 4) { 27 p = (struct ST_MEMBER *) malloc(sizeof(struct ST_MEMBER)); 28 if (!p) { printf("メモリが確保できませんでした。\n"); return 1; } 29 *p = buf; 30 p->prev = buf.prev, p->next = &buf; 31 buf.prev->next = p, buf.prev = p; 32 } 33 fclose(fp); // 読み込み終了 34 35 int swapped; // 交換の有無のフラグ 36 do { // バブルソート開始 37 swapped = 0; // 交換なし 38 for (p = buf.next; (q = p->next) != &buf; ) { 39 if (p->age > q->age) { // 年齢が逆順の場合、交換する 40 p->prev->next = q, q->next->prev = p; 41 q->prev = p->prev, p->next = q->next; 42 p->prev = q, q->next = p; 43 swapped = 1; // 交換あり 44 } 45 else p = q; 46 } 47 } while (swapped); // 交換がある間は繰り返す 48 49 for (p = buf.next; p != &buf; p = p->next) // 結果の表示 50 printf("%4d%16s%5d%5s\n", p->number, p->name, p->age, p->gender); 51 52 for (p = buf.next; p != &buf; p = q) 53 q = p->next, free(p); // malloc したものを free 54}

解決したかどうかではなく、
このコードが理解できたかどうかをお答えください。

追記
バグが見つかったので修正しました。

投稿2019/08/28 08:14

編集2019/08/28 12:01
kazuma-s

総合スコア8224

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rinringo_88

2019/08/29 05:49

詳しく教えてくださってありがとうございます。 ざっと見ただけだと正直理解できませんでした。。
guest

0

個人的にはリスト構造とバブルソートは合わない気がします。
使えなくはないでしょうけど、(最終的にやりたいことと比較すると)処理が大がかりすぎて...

それよりは、追加時に「適切な位置」を割り出してそこにセット。
したほうがやりやすいかと。

ProgrammingPlace Plusの旧サイトにあったやつで学んだんですが、「NULLでなく、かつ、p->dataのデータが追加したい値より小さい間、whileでループ」的な処理を含んで、ソート済みの状態にしてありました。(もう旧サイトのほうが無くなっているっぽいけど。)

こういう風に追加時に決定したほうがいいかなぁと。

(車が多いエリアで)1km先を目的地としているのに、わざわざフェリーに乗って向かうような感じですね。
( "だったら、歩くかタクシー乗れや!" って突っ込みたくなる。(怒ってはいないが) )

もちろん、リスト構造 + バブルソート はできなくはないと思いますが、めんどくさそう。

投稿2019/08/28 03:59

BeatStar

総合スコア4958

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rinringo_88

2019/08/29 02:01

ご回答ありがとうございます。 なるほど。リスト構造+バブルソートは効率が悪いのですね…。
jimbe

2019/08/29 03:31

例えばですが, 配列のように直接データを並べている場合, 入れ替えは 3ステップで済みます( t=a[i], a[i]=a[i+1], a[i+1]=t; ) が, リストの場合ポインタの入れ替えには kazuma-s さんのコードでも 6ステップかかっています. また 2重ループの外側のループの度に内側のループは回数を減らせるのですが, データ数によって処理を分けるのはリスト構造"的"では無いので, 私の回答でも書きましたが, 毎回全部ループしています. つまり可能性としては2倍比較してしまっています. (もちろん"リスト構造的でなければならない"ことは無いので, 単純に先に件数を数えてループに使用すれば良いだけではあります.) 一方, BeatStar さんや takabosoft さんのソート方法はいわゆる「挿入ソート」でして, つまり並んでいる中で何処に入れるかを探して挿入するというものです. そして, 挿入ソートの動作はデータの追加と同じですので, 件の場合はファイルから読み込んでリストに追加する際に「ついでに」ソートも出来るということです. この挿入ソートは, 配列等では「挿入」という動作が大変(既存のデータを全て後ろにずらしたり, 領域の大きさが足りなければ新たに大きな領域を準備してコピーしたりしなければならない)なので, あまり使われないと思います. (もちろんこちらも状況に寄っては十分使えます.)
guest

0

一応バブルソートしています. 毎回全部を比較してますが, 件数的に問題無いと思いました.
そのままだと味気無い(?)ので, 比較関数を受け取るようにしました.

c

1#include "stdafx.h" 2#include <stdio.h> 3#include <stdlib.h> 4 5struct ST_MEMBER { 6 int number; //社員番号 7 char name[50]; //名前 8 int age; //年齢 9 char gender[10]; //性別 10 struct ST_MEMBER *prev; 11 struct ST_MEMBER *next; 12}; 13 14void printList(char *pre, struct ST_MEMBER *top) { 15 printf(pre); 16 for(struct ST_MEMBER *p=top; p!=NULL; p=p->next) { 17 printf("%d,%s,%d,%s\n", p->number, p->name, p->age, p->gender); 18 } 19} 20 21//prev の next になるように p を挿入する 22void insertItem(struct ST_MEMBER *prev, struct ST_MEMBER *p) { 23 p->next = prev->next; 24 if(p->next != NULL) p->next->prev = p; 25 p->prev = prev; 26 prev->next = p; 27} 28 29//リストから外す(p->next と p->prev は初期化しない) 30void removeItem(struct ST_MEMBER *p) { 31 if(p->next != NULL) p->next->prev = p->prev; 32 if(p->prev != NULL) p->prev->next = p->next; 33} 34 35// comparator に従ってソートする 36struct ST_MEMBER *sortList(struct ST_MEMBER *top, int (*comparator)(struct ST_MEMBER *p1, struct ST_MEMBER *p2)) { 37 struct ST_MEMBER head; //先頭を保持 38 head.next = top; 39 head.next->prev = &head; 40 41 for(int changed=1; changed;) { //入れ替えが無くなるまで 42 changed = 0; 43 for(struct ST_MEMBER *p=head.next; p!=NULL && p->next!=NULL; ) { 44 if(comparator(p, p->next)) { 45 //構造体を入れ替える 46 removeItem(p); 47 insertItem(p->next, p); 48 changed = 1; 49 } else { 50 p=p->next; 51 } 52 } 53 } 54 head.next->prev = NULL; 55 return head.next; 56} 57 58//確保した全てを開放する 59void freeList(struct ST_MEMBER *top) { 60 for(struct ST_MEMBER *p=top, *t; p!=NULL; ) { 61 p = (t = p)->next; 62 free(t); 63 } 64} 65 66struct ST_MEMBER *readFile(char *filename) { 67 FILE *fp; 68 struct ST_MEMBER head, *last = &head; 69 70 head.next = NULL; 71 72 if(fopen_s(&fp, filename, "r") != 0) { //読み取りモードでファイルを開く 73 printf("ファイルのオープンに失敗しました\n"); 74 return NULL; 75 } 76 printf("ファイルのオープンに成功しました\n"); 77 78 for(int ret=0; ret!=-1; ) { 79 struct ST_MEMBER *new = (struct ST_MEMBER *)malloc(sizeof(struct ST_MEMBER)); 80 if(!new) { 81 printf("メモリが確保できませんでした。\n"); 82 freeList(head.next); //確保した全てを開放する 83 head.next = NULL; 84 break; 85 } 86 87 ret = fscanf(fp, "%4d%16s%5d%5s\n", &new->number, new->name, &new->age, new->gender); 88 if(ret == -1) { 89 free(new); //最後に確保した領域は不要 90 break; 91 } 92 93 //既存の後ろに接続する 94 insertItem(last, new); 95 last = new; 96 } 97 98 fclose(fp); 99 100 return head.next; 101} 102 103//年齢を昇順に表示したいので年齢を比較する。 104int ageComparator(struct ST_MEMBER *p1, struct ST_MEMBER *p2) { 105 return p1->age > p2->age; 106} 107 108int main(void) { 109 struct ST_MEMBER *list; 110 111 list = readFile("Meibo.txt"); 112 if(list == NULL) return -1; 113 114 list = sortList(list, ageComparator); 115 116 printList("名簿(年齢順)\n", list); 117 118 freeList(list); 119 120 return 0; 121}

投稿2019/08/27 15:46

編集2019/08/28 06:35
jimbe

総合スコア12543

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rinringo_88

2019/08/29 01:59

詳しく教えてくださりありがとうございます。 少し難しくてコードを理解出来ていないので、これからも学習を頑張っていきたいと思います。
jimbe

2019/08/29 03:05

キモは「//構造体を入れ替える」の箇所かと思いますので, removeItem/insertItem 両関数と, 実は sortList の先頭で宣言している head 変数がポインタの付け替えの簡略化に一役買ってますので, 注目して見て頂けると幸いです.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問