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

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ルイ

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

#1

投稿記事 by ルイ » 6年前

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

コード:

#include<stdio.h>
#include<stdlib.h>
#define NUM 100
struct Student {
    char name[50]; /*名前*/
    char no[10];   /*学生番号*/
    int  c_test;   /*中間*/
    int  k_test;   /*期末*/
    int  kadai;    /*課題*/
    int  seiseki;  /*最終成績*/
};
int dataRead( struct Student * );
void Seiseki( struct Student * );
void Print( struct Student * );

main(){
    struct Student *pg[NUM];    /* 学生NUM名まで*/
    int i,num;

    for(i=0; i<NUM; i++){
         pg[i] = ...            /* メモリを確保 */
         if(dataRead(pg[i])==EOF){   /*  標準入力が終了したら */
             ...
             break;
         }else{
             Seiseki(pg[i]);
         }
     }
     for(i=0; i<num;  i++){
             Print(pg[i]);
             free(pg[i]);
     }
}

int dataRead( struct Student *g ){
     return ...;                  /* 1行データを読み込む */
} 
void Seiseki( struct Student *g ){
     g->seiseki= ...;             /* 1名の最終成績を計算 */
     return;
}
void Print( struct Student *g ){
     printf("%s %s %d %d %d %d\n", ...); /* 1名表示 */
     return;
}

コード:

//自分でかけた部分
pg[i] = (struct Student*)malloc(sizeof(struct Student)); /* メモリを確保 */
if (pg[i] == NULL) {
printf("メモリを確保できませんでした。\n");
exit(1);
}
int dataRead(struct Student *g) {
    return gets(g);                  /* 1行データを読み込む */
}
void Seiseki(struct Student *g) {
    g->seiseki = ((g->c_test) + (g->k_test)) / 2 * 0.6 + (g->kadai);             /* 1名の最終成績を計算 */
    return;
}
void Print(struct Student *g) {
    printf("%s %s %d %d %d %f\n", g->name, g->no, g->c_test, g->k_test, g->kadai, (double)g->seiseki); /* 1名表示 */
    return;
}
分からない点
・seisekiはint型なのに0.6をかけたらdouble型にならないのでしょうか。
・メモリを確保..とありますが100回繰り返したら結局100名分の構造体配列を用意するのと一緒なのではないのでしょうか。
・標準入力が終了したら以下のような処理でよろしいでしょうか。
if (dataRead(pg) == EOF) { /* 標準入力が終了したら */
num = i;
break;
}
最後に、そもそもの質問なのですが、このプログラムの動作は一気に数人分のデータを標準入力してエンターが押されたときに表示の処理に移るという仕組みなのでしょうか。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

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

#2

投稿記事 by みけCAT » 6年前

ルイ さんが書きました:
6年前
・seisekiはint型なのに0.6をかけたらdouble型にならないのでしょうか。
int型の変数にdouble型の数をかけたら確かにdouble型になります。
そして、int型の変数に格納するときは自動的にint型に変換されます。
オフトピック
ただし、double型に変換される前に割り算をしており、切り捨てられる可能性があるのは心配ですね。
ルイ さんが書きました:
6年前
・メモリを確保..とありますが100回繰り返したら結局100名分の構造体配列を用意するのと一緒なのではないのでしょうか。
一緒ではありません。
むしろポインタ配列(や動的メモリ確保システムのオーバーヘッド)の分だけ
100名分の構造体配列を用意するよりメモリ使用量は多くなります。
オフトピック
ただし、このプログラムでは100名分確保しても普通の環境では10KBにもならないはずであり、
現代のパソコンでは全く問題にならないはずです。
ルイ さんが書きました:
6年前
・標準入力が終了したら以下のような処理でよろしいでしょうか。
if (dataRead(pg[​i]) == EOF) { /* 標準入力が終了したら */
num = i;
break;
}
dataRead関数の実装によります。
現在のgets関数の戻り値(のポインタをint型に変換した値)を返す実装の場合、
gets関数は読み込みに失敗したときはNULLを返し、int型に変換すると0になる可能性が高いので、
EOFと比較して終了判定するのはよろしくないです。
オフトピック
* gets関数はバッファオーバーランのリスクを潰せないので使ってはいけないとされる
* そもそも文字列を入力するためのgets関数に構造体へのポインタを渡すのは不適切と考えられる
というのはおいといて。
ルイ さんが書きました:
6年前
最後に、そもそもの質問なのですが、このプログラムの動作は一気に数人分のデータを標準入力してエンターが押されたときに表示の処理に移るという仕組みなのでしょうか。
そのような仕組みではないと考えられます。
* 表示の処理の前に全員分のデータを読み込みます。
 「数人分」かはデータの数とどの程度を「数人」とみなすかによります。
* 「標準入力」するかはdataRead関数の実装によります。 (gets関数は標準入力から読み込みます)
* 「エンターが押されたときに表示の処理に移る」かどうかはdataRead関数の実装によります。
 現状のgets関数を用いた実装で、標準入力をキーボードから読み込んでいる場合、
 エンターではなく(一般的には)WindowsではCtrl+Z、LinuxではCtrl+Dが押されて
 入力が終了となった時に表示の処理に移るはずです。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

返信

“C言語何でも質問掲示板” へ戻る