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

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

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

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

Q&A

解決済

3回答

1763閲覧

構造体のポインタ配列とメモリ確保について

masuter0413

総合スコア50

C

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

0グッド

0クリップ

投稿2018/10/20 14:49

現在次のような課題が出ています。自分で参考書などを読んで途中までかけましたが分からない部分が多数あります。
100名分の構造体の配列を予め用意するのは非常にメモリ の無駄使い。そこで、学生の構造体は必要になった時点で、 メモリから確保するようにしてみよう。但し、学生の構造体へのポインタを 保持することは必要であるから、100個のポインタ配列を利用することにする。
作成するプログラムは次のようなものである。
学生の名前(ローマ字)、学生番号 、中間試験の点数(100点満点)、 期末試験の点数(100点満点)、課題の点数(40点満点)、最終成績(100点満点)を 格納する構造体を考え、成績を計算し、表示するプログラムを考える。 但し、最終成績は以下の式で計算される。 を 格納する構造体を考え、成績を計算し、表示するプログラムを考える。
最終成績=中間+期末/2 *0.6 +kadai

c

1#include<stdio.h> 2#include<stdlib.h> 3#define NUM 100 4struct Student { 5 char name[50]; /*名前*/ 6 char no[10]; /*学生番号*/ 7 int c_test; /*中間*/ 8 int k_test; /*期末*/ 9 int kadai; /*課題*/ 10 int seiseki; /*最終成績*/ 11}; 12int dataRead( struct Student * ); 13void Seiseki( struct Student * ); 14void Print( struct Student * ); 15 16main(){ 17 struct Student *pg[NUM]; /* 学生NUM名まで*/ 18 int i,num; 19 20 for(i=0; i<NUM; i++){ 21 pg[i] = ... /* メモリを確保 */ 22 if(dataRead(pg[i])==EOF){ /* 標準入力が終了したら */ 23 ... 24 break; 25 }else{ 26 Seiseki(pg[i]); 27 } 28 } 29 for(i=0; i<num; i++){ 30 Print(pg[i]); 31 free(pg[i]); 32 } 33} 34 35int dataRead( struct Student *g ){ 36 return ...; /* 1行データを読み込む */ 37} 38void Seiseki( struct Student *g ){ 39 g->seiseki= ...; /* 1名の最終成績を計算 */ 40 return; 41} 42void Print( struct Student *g ){ 43 printf("%s %s %d %d %d %d\n", ...); /* 1名表示 */ 44 return; 45}

c

1//自分でかけた部分 2int dataRead(struct Student *g) { 3 return gets(g); /* 1行データを読み込む */ 4} 5void Seiseki(struct Student *g) { 6 g->seiseki = ((g->c_test) + (g->k_test)) / 2 * 0.6 + (g->kadai); /* 1名の最終成績を計算 */ 7 return; 8} 9void Print(struct Student *g) { 10 printf("%s %s %d %d %d %f\n", g->name, g->no, g->c_test, g->k_test, g->kadai, (double)g->seiseki); /* 1名表示 */ 11 return; 12}

分からない点
・seisekiはint型なのに0.6をかけたらdouble型にならないのでしょうか。

・標準入力が終了したら..とありますがそのあとどんな処理をするべきでしょうか。
・メモリを確保..とありますが100回繰り返したら結局100名分の構造体配列を用意するのと一緒なのではないのでしょうか。

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

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

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

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

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

guest

回答3

0

seisekiはint型なのに0.6をかけたらdouble型にならないのでしょうか。

整数型に0.6掛けたものはdoubleになっています。
が代入時に暗黙の変換が行われ整数型に変換されます。

・標準入力が終了したら..とありますがそのあとどんな処理をするべきでしょうか。

入力が失敗した = 何名分入力したかが確定した
なので、何名分のデータがあるかを表示するために必要なことはなんだと思いますか?

メモリを確保..とありますが100回繰り返したら結局100名分の構造体配列を用意するのと一緒なのではないのでしょうか。

100名分確保したら、むしろポインタを置く場所の分だけメモリは浪費します。
しかし、果たして100名分も常に確保する必要がありますか?


あなたが書いたコードについて

return gets(g); /* 1行データを読み込む */

getsだけでは構造体にデータを入れる事はできません。
また、gに確保してあるメモリは76文字分程度になるので
getsを用いるには不安があります。

printf("%s %s %d %d %d %f\n", g->name, g->no, g->c_test, g->k_test, g->kadai, (double)g->seiseki); /* 1名表示 */

前述した通り、seisekiはint型なのでdouble型として表示しようとしても特に意味は無く
小数点以下は0として表示されるでしょう。

投稿2018/10/20 15:32

asm

総合スコア15149

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

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

0

ベストアンサー

本題の前にいくつか。

main()

int main(void)と書くことがC言語規格書で定められています。

`struct Student { ... };

C++だとこれでいいのですが、Cだと普通typedef struct tag_student { ... } Student;と書くように思います。

void Print( struct Student *g ){

引数の型がstruct Student *となっていますが、const struct Student *とするべきです。なぜならPrint関数は引数gが指す領域を書き換えません。constをつけないことは可読性を下げます。まあ課題の指定でこうなっているのだとは思いますが。

gets

利用してはいけません。すこしググればわかりますが、getsはどうやっても安全に利用することができません。fgetsgets_sを利用してください。

今回の課題では文字列の入力を受けてさらにパースする作業が必要になります。このときsscanfを使ってしまいがちですが、安全に数値へ変換することはできないので、errnoに気を使いつつ地道にstrtol系関数を呼ぶことになります。

C言語で安全に標準入力から数値を取得 - Qiita

・・・ということまで課題を出す側がわかって事前に指導していればいいのですが、9割9分9厘教師はC言語を理解していないので、まあsscanfを使うことになるんだろうなとは思います。やつらいい加減C言語で標準入力をやるのはラスボスに挑むに等しいと理解してくれないだろうか・・・。


seisekiはint型なのに0.6をかけたらdouble型にならないのでしょうか

鋭いです。まず

((g->c_test) + (g->k_test)) / 2

の結果はint型ですが、

((g->c_test) + (g->k_test)) / 2 * 0.6

の結果はdoubleで、

((g->c_test) + (g->k_test)) / 2 * 0.6 + (g->kadai)

もやはりdoubleです。

これをg->seisekiに代入するときにintへの暗黙変換が行われます。暗黙変換の詳細なルールは複雑怪奇極まるので深く突っ込まないことをおすすめします。


標準入力が終了したら..とありますがそのあとどんな処理をするべきでしょうか。

課題の答えになるのでヒントだけ。

breakがあることから、EOFだったらなにかして、ループを抜ける、という処理だとわかります。ループを抜けたあと、結果を表示するループに入るわけですが、このときループ回数を司っている変数numに注目してください。


メモリを確保..とありますが100回繰り返したら結局100名分の構造体配列を用意するのと一緒なのではないのでしょうか。

配列もmallocによる確保もメモリー確保には違いありませんが、どこにメモリー確保されるかが異なります。

C++のメモリーの話とストレージの有効期間の話をしようか - Qiita

ですがいい着眼点です。

struct Student *pg[NUM]; /* 学生NUM名まで*/

課題の指定でこうなっているのだとは思いますが、わざわざ構造体をmallocするのはこの場合冗長極まるでしょう。struct Student pg[NUM]でよかった。

mallocのようなメモリー確保は基本的に時間がかかるのです。今回のような小さなプログラムでは問題になりませんが、深いループの中でのメモリー確保は時として高速化の妨げとなることもあります。

投稿2018/10/20 15:25

編集2018/10/20 15:41
yumetodo

総合スコア5852

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

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

masuter0413

2018/10/21 00:56

丁寧なご回答ありがとうございます。 とりあえずメモリの確保のところはじぶんでできました。 pg[i] = (struct Student*)malloc(sizeof(struct Student)); /* メモリを確保 */ if (pg[i] == NULL) { printf("メモリを確保できませんでした。\n"); exit(1); } あと、標準入力が終了したら以下のような処理でよろしいでしょうか。 if (dataRead(pg[i]) == EOF) { /* 標準入力が終了したら */ num = i; break; } 最後に、そもそもの質問なのですが、このプログラムの動作は一気に数人分のデータを標準入力してエンターが押されたときに表示の処理に移るという仕組みなのでしょうか。
yumetodo

2018/10/21 02:34

> 最後に、そもそもの質問なのですが、このプログラムの動作は一気に数人分のデータを標準入力してエンターが押されたときに表示の処理に移るという仕組みなのでしょうか。 いや、改行区切りでEOFが来るかNUMを超えるまで入力を受けるという状態でしょう。
guest

0

・seisekiはint型なのに0.6をかけたらdouble型にならないのでしょうか。

なります。
double型の値をint型の変数に代入するのは問題ありません。小数部が切り捨てられて代入されます。

・標準入力が終了したら..とありますがそのあとどんな処理をするべきでしょうか。

それを考えるのが問題では?

・メモリを確保..とありますが100回繰り返したら結局100名分の構造体配列を用意するのと一緒なのではないのでしょうか。

はい。その通りです。

投稿2018/10/20 15:12

編集2018/10/20 15:37
otn

総合スコア85778

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問