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

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

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

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

Q&A

解決済

3回答

1215閲覧

既存プログラムから関数の導入。

ooba

総合スコア3

C

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

0グッド

0クリップ

投稿2020/11/04 11:25

C言語で私が作ったプログラムを
① ファイル(R7_in.txt)からN人分の個人識別番号、名前
(family name)、身長[cm]、体重[kg]のデータを読み取り、
各配列に格納する。確認のため、読み込んだすべての
データを画面に出力する。
② 身長と体重からBMI値を計算した結果をBMI用の配列に格
納する。
③ BMI値の降順になるように、個人識別番号、名前、身長、
体重、BMI値の各配列要素の値を並び替える。
④ ファイル(R7_out.txt)に、BMI値の降順に、各行が個人識
別番号、名前、身長、体重、BMI値となるようにデータを出
力する。
の4つの関数にして新しく作りたいです。

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

既存のプログラムを関数に落とし込むことができない。

該当のソースコード

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

int main( void ){
int ID[15] = {0};
double hight[15] = {0};
double weight[15] = {0};
double BMI[15] = {0}; /* データを配列に格納する /
char name[15][25]={0};
int i, j, n = 15; /
nはデータ数 */
FILE *fpin, fpout; / ファイルポインタ変数の宣言 /
fpin = fopen("R7_in.txt", "r"); /
入力(read)用ファイルをオープン /
fpout = fopen("R7_out.txt", "w"); /
出力(write)用ファイルをオープン */

if (fpin == NULL) { /* 安全対策:入力用ファイルのオープンに失敗したときの処置 /
printf("入力ファイルのオープンに失敗しました");
return 0; /
途中でプログラムを終了させる */
}

if (fpout == NULL) { /* 安全対策:出力用ファイルのオープンに失敗したときの処置 /
printf("出力ファイルのオープンに失敗しました");
return 0; /
途中でプログラムを終了させる */
}
//数値の読み取り

i = 0; while( fscanf(fpin, "%d %s %lf %le", &ID[i],name[i], &hight[i],&weight[i] )!= EOF ) { i++; } printf("ID NAME HIGHT[cm] WEIGHT[kg]\n"); for( i=0; i<15; i++ ){ printf("%d %-8s %.1lf %.1lf\n", ID[i], name[i], hight[i],weight[i] ); }

for (i=0; i<15; i++){//i=0~14まで繰り返す
BMI[i] = weight[i]/hight[i]/hight[i]*10000;//BMIの計算
}

double a = 0, b = 0, c = 0, d = 0;
char e[25] = {0};

printf("\n");
for (i=0; i<n-1; i++) { /* i=0(最初のデータ)からi=n-2まで繰り返す /
for (j=i+1; j<n; j++) { /
j=i+1からj=n-1(最後のデータ)まで繰り返す /
if(BMI[i] < BMI[j]) { /
もしdata[i]>data[j]なら中身を交換し、data[i]を小さい状態にする /
a = BMI[i]; /
BMI[i]~name[i]の中身を一時、a~eに保存する /
b = ID[i];
c = weight[i];
d = hight[i];
strcpy(e,name[i]);
BMI[i] = BMI[j]; /
BMI[j]の中身をdata[i]に移す */
ID[i] = ID[j];
weight[i] = weight[j];
hight[i] = hight[j];
strcpy(name[i],name[j]);

BMI[j] = a; /* 保存していたaの中身をdata[j]に移す */ ID[j] = b; weight[j] = c; hight[j] = d; strcpy(name[j],e); } }

}

for (i=0; i<n; i++) {
fprintf(fpout, "%d %-8s %.1lf %.1lf %.1lf \n", ID[i],name[i], hight[i],weight[i], BMI[i] ); /* 結果を出力ファイルへ出力 */
}

fclose(fpin); /* 入力用ファイルをクローズ /
fclose(fpout); /
出力用ファイルをクローズ */

return 0;

}

###R7_in.text
1001 Aoki 172.8 63.1
1002 Enomoto 167.5 68.9
1003 Fujita 182.6 69.3
1004 Hayashi 178.7 55.7
1005 Ishii 170.1 70.2
1006 Kawai 179.3 83.1
1007 Kubo 169.6 56.4
1008 Murakami 165.2 68.7
1009 Nakajima 187.6 61.0
1010 Nomura 172.0 71.5
1011 Okada 180.8 73.8
1012 Sakamoto 177.4 63.7
1013 Shibata 178.2 67.3
1014 Takeda 172.9 78.1
1015 Wada 170.7 65.4

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

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

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

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

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

pepperleaf

2020/11/04 11:41

元のプログラムをちょっと脇に置いて、 ①から④までのそれぞれについて、入力と出力がどうなるかを整理して、関数化しましょう。
ooba

2020/11/04 12:17

アドバイスありがとうございます。 関数化難しそうです…でもやってみます。
guest

回答3

0

関数名を決めて、配列を引数にするだけじゃないの?

C

1#include <stdio.h> // fopen, fclose, fscanf, fprintf, printf 2#include <string.h> // strcpy 3 4int readData(int n, int *ID, double *height, double *weight, char name[][25]) 5{ 6 FILE *fp = fopen("R7_in.txt", "r"); 7 if (fp == NULL) { 8 printf("入力ファイルのオープンに失敗しました"); 9 return 1; 10 } 11 printf("ID NAME HIGHT[cm] WEIGHT[kg]\n"); 12 for (int i = 0; i < n && fscanf(fp, "%d%s%lf%lf", 13 &ID[i], name[i], &height[i], &weight[i]) == 4; i++) 14 printf("%d %-8s %.1f %.1f\n", ID[i], name[i], height[i], weight[i]); 15 fclose(fp); 16 return 0; 17} 18 19void calcBMI(int n, double *height, double *weight, double *BMI) 20{ 21 for (int i = 0; i < n; i++) 22 BMI[i] = weight[i] / height[i] / height[i] * 10000; 23} 24 25void sortData(int n, int *ID, double *height, double *weight, double *BMI, char name[][25]) 26{ 27 for (int i = 0; i < n - 1; i++) { 28 for (int j = i + 1; j < n; j++) { 29 if (BMI[i] < BMI[j]) { 30 double a = BMI[i]; BMI[i] = BMI[j]; BMI[j] = a; 31 int b = ID[i]; ID[i] = ID[j]; ID[j] = b; 32 a = weight[i]; weight[i] = weight[j]; weight[j] = a; 33 a = height[i]; height[i] = height[j]; height[j] = a; 34 char e[25]; 35 strcpy(e, name[i]); strcpy(name[i], name[j]); strcpy(name[j], e); 36 } 37 } 38 } 39} 40 41int writeData(int n, int *ID, double *height, double *weight, double *BMI, char name[][25]) 42{ 43 FILE *fp = fopen("R7_out.txt", "w"); 44 if (fp == NULL) { 45 printf("出力ファイルのオープンに失敗しました"); 46 return 1; 47 } 48 for (int i = 0; i < n; i++) 49 fprintf(fp, "%d %-8s %.1f %.1f %.1f \n", 50 ID[i], name[i], height[i], weight[i], BMI[i]); 51 fclose(fp); 52 return 0; 53} 54 55int main(void) 56{ 57 int ID[15]; 58 double height[15], weight[15], BMI[15]; 59 char name[15][25]; 60 61 if (readData(15, ID, height, weight, name)) return 1; 62 calcBMI(15, height, weight, BMI); 63 sortData(15, ID, height, weight, BMI, name); 64 if (writeData(15, ID, height, weight, BMI, name)) return 1; 65 return 0; 66}

どこが難しいのかをコメントください。

投稿2020/11/04 18:33

編集2020/11/05 15:52
kazuma-s

総合スコア8224

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

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

ooba

2020/11/05 01:29

ご回答ありがとうございます。 とてもきれいなプログラミングで「これだ!」と思いました。 私は配列、ポインタをうまく扱うことができず、関数の作成も初心者です。 なので申し訳ないのですが、正直わからないところがわからない状態なのではっきりとはコメントできません。 配列を引数にした関数の作成行為が難しいのだと思います。 あまり良い返信ができずすみません。 ご回答ありがとうございました。
yumetodo

2020/11/05 15:18

多分C言語ポインタ完全制覇(前橋和弥)という本が今のあなたに必要な本なのかなと思います。古い方のやつでも十分なので手に入れて読んでみてください
guest

0

ベストアンサー

課題の良し悪しは別として下記でいかがでしょうか?
なるべく元の処理をくずさないように気をつけましたが。

c

1 2#include <stdio.h> 3#include <string.h> 4 5#define RET_OK (0) 6#define RET_NG (-1) 7 8#define NAME_MAX (25) 9#define DATA_NUM (15) 10 11#define INPUT_FILE "R7_in.txt" 12#define OUTPUT_FILE "R7_out.txt" 13 14/** 15 * @brief 1の処理関数 16 */ 17int read_physical_data_from_file(const char *filename, int *ID, char name[][NAME_MAX], double *hight, double *weight, size_t num) 18{ 19 int ret = RET_OK; 20 FILE *fp; 21 22 fp = fopen(filename, "r"); 23 if (fp == NULL) { 24 printf("fopen() failed. name=%s\n", filename); 25 ret = RET_NG; 26 goto error_end; 27 } 28 29 int i = 0; 30 while( fscanf(fp, "%d %s %lf %le", &ID[i], name[i], &hight[i], &weight[i] )!= EOF ) { 31 printf("%d %-8s %.1lf %.1lf\n", ID[i], name[i], hight[i], weight[i] ); 32 i++; 33 } 34 35 if (i != num) { 36 printf("warn: i=%d, num=%lu\n", i, num); 37 } 38 39 error_end: 40 41 return ret; 42} 43 44/** 45 * @brief 2の処理関数 46 */ 47int set_bmi_from_phyisical_data(int *ID, char name[][NAME_MAX], double *hight, double *weight, double *BMI, size_t num) 48{ 49 int ret = RET_OK; 50 int i; 51 52 for (i = 0; i < num; i++) { 53 BMI[i] = weight[i] / hight[i] / hight[i] * 10000; 54 } 55 56 return ret; 57} 58 59/** 60 * @brief 3の処理関数 61 */ 62int sort_physical_data_desc_bmi(int *ID, char name[][NAME_MAX], double *hight, double *weight, double *BMI, size_t num) 63{ 64 int i, j; 65 double a = 0, b = 0, c = 0, d = 0; 66 char e[25] = {0}; 67 int ret = RET_OK; 68 69 for (i = 0; i < num - 1; i++) { 70 for (j = i + 1; j < num; j++) { 71 if(BMI[i] < BMI[j]) { 72 a = BMI[i]; 73 b = ID[i]; 74 c = weight[i]; 75 d = hight[i]; 76 strcpy(e, name[i]); 77 BMI[i] = BMI[j]; 78 ID[i] = ID[j]; 79 weight[i] = weight[j]; 80 hight[i] = hight[j]; 81 strcpy(name[i],name[j]); 82 83 BMI[j] = a; 84 ID[j] = b; 85 weight[j] = c; 86 hight[j] = d; 87 strcpy(name[j],e); 88 } 89 } 90 } 91 92 return ret; 93} 94 95/** 96 * @brief 4の処理関数 97 */ 98int write_result_file(const char *filename, int *ID, char name[][NAME_MAX], double *hight, double *weight, double *BMI, size_t num) 99{ 100 int ret = RET_OK; 101 FILE *fp; 102 int i; 103 104 fp = fopen(filename, "w"); 105 if (fp == NULL) { 106 printf("fopen() failed. name=%s\n", filename); 107 ret = RET_NG; 108 goto error_end; 109 } 110 111 for (i = 0; i < num; i++) { 112 fprintf(fp, "%d %-8s %.1lf %.1lf %.1lf \n", ID[i], name[i], hight[i], weight[i], BMI[i] ); 113 } 114 115 error_end: 116 117 return ret; 118} 119 120 121int main( void ) 122{ 123 int ID[DATA_NUM] = {0}; 124 double hight[DATA_NUM] = {0}; 125 double weight[DATA_NUM] = {0}; 126 double BMI[DATA_NUM] = {0}; 127 char name[DATA_NUM][NAME_MAX]={0}; 128 129 int ret = RET_OK; 130 131 ret = read_physical_data_from_file(INPUT_FILE, ID, name, hight, weight, DATA_NUM); 132 if (ret != RET_OK) { 133 goto error_end; 134 } 135 136 ret = set_bmi_from_phyisical_data(ID, name, hight, weight, BMI, DATA_NUM); 137 if (ret != RET_OK) { 138 goto error_end; 139 } 140 141 ret = sort_physical_data_desc_bmi(ID, name, hight, weight, BMI, DATA_NUM); 142 if (ret != RET_OK) { 143 goto error_end; 144 } 145 146 ret = write_result_file(OUTPUT_FILE, ID, name, hight, weight, BMI, DATA_NUM); 147 if (ret != RET_OK) { 148 goto error_end; 149 } 150 151 error_end: 152 153 return 0; 154 155} 156

投稿2020/11/04 14:00

sukekeke0

総合スコア331

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

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

yumetodo

2020/11/04 14:15

いやさすがにRET_OKってbool使いません?
sukekeke0

2020/11/04 14:55 編集

さすがにの理由はよくわかりませんが、そのdefine名はいくらなんでもダメだろ?ってことでしょうかね。サクッとエイヤーで作った案ですので大目に見てください。 もしよろしければ、質問者のために別の案を提示してあげてください。
fana

2020/11/05 04:29

割とどうでもいい話ですが > なるべく元の処理をくずさないように といったことを意識するならば, 質問者が用いていない goto を使わずに普通にreturnした方が優しい気がします.
guest

0

まずその関数分けの仕方がまったく良くないんですが、そう分けると決めたのはあなたですかね?それともなんかの課題?

例えば

ファイル(R7_in.txt)からN人分の個人識別番号、名前

(family name)、身長[cm]、体重[kg]のデータを読み取り、
各配列に格納する。確認のため、読み込んだすべての
データを画面に出力する。

ってすでにいろいろなことをやりすぎていますし、その格納する配列ってどこにあるのよ、って話になります。こういう分け方だとグローバル変数を増やしがちでよくありません。

まず、データの読み取りと画面出力は分けるべきです。

例えばデータの読み取りなら、ファイル名とか書き出す配列へのポインタは引数で与えられるべきです。つまり

c

1#include <stddef.h> 2#include <stdbool.h> 3#include <stdint.h> 4typedef struct { 5 uint64_t individual_number; 6 const char* name; 7 uint32_t height; 8 uint32_t weight; 9} data_t; 10bool data_read_from_file(data_t** result, size_t result_length, const char* file_name);

のような関数であるべきです。

関数の分け方から見直せませんかね?


せっかくなので、元のプログラムを適切に関数分けして、さらにもっとまじめにエラーハンドリングして、可変長の入力に対応させてみました。文字数の関係でソースコードと実行結果はリンク先を参照してください。

まずはC言語で書いたもの。エラー処理の関係であまり関数を分けられていないです。

ソースコードと実行結果

次にC++で書いたもの。エラー処理をたくさん書かなくてもいい感じになるのでスッキリしますね。C++20で追加されたstd::formatを使っているのですが、まだ殆どの処理系で実装されてないと思うので、その場合はfmtlib/fmtを呼び出すようにしてます。

ソースコード

実行結果

投稿2020/11/04 11:48

編集2020/11/05 04:05
yumetodo

総合スコア5850

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

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

ooba

2020/11/04 12:01

ご回答ありがとうございます。 こちらは学校の課題です。条件が以下のようになっております。 1. 個人識別番号、名前(family name)、身長[cm]、体 重[kg]用の配列は全てmain関数のローカル変数と して宣言する。 2. ①~④の処理はそれぞれ関数にまとめ、関数呼び 出しにより各処理を実行する構成とする(関数は4 つ必要)。関数名は各自で決めること。関数内で必 要な変数の宣言を忘れないように注意すること。 となっており、残念ですが関数の分け方は決まっているようで見直しは難しいです。
yumetodo

2020/11/04 12:35

どうしてこう、よくない関数分けを教えるかなぁ・・・。 まあしかしその条件だと①の関数のプロトタイプ宣言は上のdata_read_from_fileと同じでいいですね。まあ関数名はもうちょっと考えるとして。 https://qiita.com/yumetodo/items/5886b2c0c421c24c909b#%E9%96%A2%E6%95%B0%E3%81%A8%E3%81%AF%E7%BE%A9%E5%8B%99%E6%95%99%E8%82%B2%E3%81%AB%E7%AB%8B%E3%81%A1%E8%BF%94%E3%81%A3%E3%81%A6 こんなのも読んでみて下さいな。
yumetodo

2020/11/04 12:36

え、ところで③のソートってqsortみたいなの使うんじゃなくて自力でやらなきゃいけないんですか・・・。
ooba

2020/11/04 13:08

使ってはいけないのような制限はないですが、qsort?などは初めて聞く単語でよくわからないので、私たちのほうではそうなると思います
archiver

2020/11/04 14:57

色々と突っ込みたくなる「課題」ですね。課題の作成者って、Cのプログラムを書いたことが無いんじゃないかというくらいに。 この課題、個人識別番号、名前、身長、体重とBMI値のデータを構造体にまとめちゃダメなの?構造体にまとめた方が楽だと思うんだけど(ってか、普通はまとめると思う。構造体はまだ習ってないのかな?)。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問