プログラミングの問題が分かりません
教えてください・・・
C言語です
問題
データ例のように、1行目にデータ数、2行目以降はデータ数分のデータ(名前、背番号、打数、安打数)が並んでいるファイルがある。
[ データ例 carp5.dat ]
5
Seiya 51 466 156
Arai 25 454 136
Maru 9 557 162
Kikuchi 33 574 181
Tanaka 2 581 154
[ データ例 central6.dat ]
6
Yamada 1 481 146
Fukudome 8 453 141
Kikuchi 33 574 181
Tsutsugo 25 469 151
Seiya 51 466 156
Sakamoto 6 488 168
このようなデータファイルに対して以下のような処理を実行するプログラムを作成せよ.
(1).コマンドラインからデータファイル名を指定する
(2).先頭ので0タスを読み込んで、データ数分のデータ領域を動的に確保する
(3).データファイルの2行目以降は、1度読みこむたびに、makePlayerData関数を用いてデータを作成する
(4).printPlayerData関数を用いて、データを逆順に出力する
↓
/* -*-coding: utf-8-*- */
#include <stdio.h>
#include <stdlib.h>
typedef struct player{
char name[50];/*名前を代入するname*/
int number;/*背番号を代入する変数number*/
int at_bat;/*打数を代入する変数at_bat*/
int hit;/*安打数を代入する変数hit*/
double ave;/*打率を代入する変数ave*/
} PLAYER;
PLAYER makePlayerData(char *s)
{
PLAYER x;
sscanf(s, "%s %d %d %d", x.name, &x.number, &x.at_bat, &x.hit);
x.ave = ((double)x.hit / (double)x.at_bat);
return x;
}
int printPlayData(PLAYER p)
{
return printf("%s %d %d %d %f", p.name, p.number, p.at_bat, p.hit, p.ave);
}
int main(int argc, char *argv[])
{
PLAYER *head;/*先頭アドレス用*/
PLAYER *p;/*作業用*/
char buf[256];/*fgets用*/
int player_num;/*データ数(選手数)*/
int i;/*作業用*/
FILE *fp;/*ファイルポインタ*/
/*コマンドラインパラメータの数をチェック*/
if(argc != 2){
printf("Usage: %s file_name\n", argv[0]);
exit(1);
}
/*ファイルを開く*/
char *rfile = argv[1]; fp =fopen(rfile, "r");
if(fp == NULL){
printf("File %s does'nt exits.\n", argv[1]);
exit(1);
}
/*データ数(選手数)を読み込む*/
fgets(buf, sizeof(buf), fp);/*1行目だけ読み込んでくる*/
sscanf(buf, "%s", &buf[0]);/*ここ*/
/*メモリ領域の動的確保*/
head = (PLAYER *)malloc(sizeof(PLAYER)*200);
/*PLAYERデータの生成*/
p = head;
while(fgets(buf, sizeof(buf), fp) != NULL){ /*ここ*/
*p = makePlayerData(buf); /*ここ*/
p++;
}
fclose(fp);/*ファイルを閉じる*/
/*PLAYERデータの出力*/
printf("%-8s %8s %8s %8s %8s\n", "選手名", "背番号", "打数", "安打", "打率");
for(i = 0, p = head; i < player_num; i++, p--) /*ここ*/
printPlayerData(p); /*ここ*/
/*データ領域の開放*/
free(head);
return 0;
}
Execise12.1.c:(.text+0x337): `printPlayerData' に対する定義されていない参照です
collect2: error: ld returned 1 exit status
エラーはこの「定義されてない参照です」のみなのですが関数の部分が出来ておらず逆順に出力出来ませんし/*ここ*/とコメントしている部分は分からなくてとりあえずそれっぽい感じに入れてみただけでおそらくコアダンプや正しく動かないと思います
これらの部分を教えて頂きたいです・・・
大学生で今習ってるのですが、難しく凄く理解に苦しんでいます
お願いします!
プログラミングの課題で・・・
Re: プログラミングの課題で・・・
ソースコードを提示する際は、BBCodeが有効な(無効にしない)状態でBBCodeのcodeタグで囲んでいただけると、見やすくてありがたいです。
また、インデントを用いてコードを整形すると、さらに見やすくなるでしょう。
printPlayData関数が定義されているのに使用されていないので、これをprintPlayerData関数にすればいいのではないでしょうか?
また、インデントを用いてコードを整形すると、さらに見やすくなるでしょう。
書いてある通り、printPlayerDataが定義されていません。daigakusei さんが書きました:Execise12.1.c:(.text+0x337): `printPlayerData' に対する定義されていない参照です
printPlayData関数が定義されているのに使用されていないので、これをprintPlayerData関数にすればいいのではないでしょうか?
オフトピック
C言語の宿題が・・・ • C言語交流フォーラム ~ mixC++ ~
と似た課題ですね。
同じ関数名で間違えているというのは面白いです。
リンク先はExecise11.2.c、この質問はExecise12.1.cなので続きであると予想できますが、
daigakuseiさんはExecise11.2 (?)はやりましたか?
と似た課題ですね。
同じ関数名で間違えているというのは面白いです。
リンク先はExecise11.2.c、この質問はExecise12.1.cなので続きであると予想できますが、
daigakuseiさんはExecise11.2 (?)はやりましたか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: プログラミングの課題で・・・
返信ありがとうございます
獅子丸で投稿させて頂いた者ですがその名前は使わないで欲しいと友人に言われましたのでこの名前に変えました
月曜日実行しようと思ってたのですが時間がとれず実行できませんでした・・・
また課題であるのですが提出期限が過ぎており今は少しでも早く提出したいというあまり好ましくない思いでここに投稿しました
もちろん後で復習しなければならないと思っております
獅子丸で投稿させて頂いた者ですがその名前は使わないで欲しいと友人に言われましたのでこの名前に変えました
月曜日実行しようと思ってたのですが時間がとれず実行できませんでした・・・
また課題であるのですが提出期限が過ぎており今は少しでも早く提出したいというあまり好ましくない思いでここに投稿しました
もちろん後で復習しなければならないと思っております
Re: プログラミングの課題で・・・
英語的にはdoes'ntよりdoesn'tの方が適切でしょうが、C言語の文法上は全く問題ありません。
bufの内容をbufに読み込むというのはナンセンスです。
さらに、重なったオブジェクト間のコピーが発生するため、未定義動作になります。
「データ数分のデータ領域を動的に確保する」という条件を満たせていません。
まず、読み込んだデータ数(文字列)を数値に変換し、それを用いるといいでしょう。
数値を表す文字列を数値に変換するには、例えば(課題で制約が無ければ)atoi関数が使えます。
makePlayerData関数でやっているのと同様にsscanfを使ってもかまいません。
後のことを考えると、データ数をplayer_numに代入しておくといいでしょう。
データ領域の確保を正しく実装した場合、
不正な(実際のデータ数が1行目に書かれているデータ数より多い)ファイルが入力された場合に
範囲外のアクセス(未定義動作)が発生します。
ただし、有効な入力しか与えられないのであれば問題ないでしょう。
pを領域の最初の場所に設定し、そこから後ろに下げてしまっているので、
pの指す場所が領域の範囲外になってしまい、未定義動作になります。
また、player_numは初期化されていない自動変数なので、値は不定であり、
これを計算に使うことも未定義動作になります。
- pを領域の最初ではなく最後の要素、すなわちhead + player_num - 1に初期化する
- player_numにデータ数(前述の通りatoiやsscanfなどで文字列から数値に変換できます)を代入しておく
ただし、これでも最後にpが最初の要素の前に行ってしまい、定義上は未定義動作になります。
printPlayData関数をprintPlayerData関数にした場合、PLAYER型のデータを渡すべき場所にPLAYER*型のデータを渡そうとしているので、コンパイルが通らないでしょう。
上の未定義動作の回避とあわせて、
for(i = 0, p = head + player_num; i < player_num; i++, p--) /*ここ*/
printPlayerData(*(p - 1)); /*ここ*/
(ポインタに「最後の要素の1個次」を指させることは認められています。ただし、「最後の要素の1個次」を指しているポインタをデリファレンスしてはいけません。)
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: プログラミングの課題で・・・
/* -*-coding: utf-8-*- */
#include <stdio.h>
#include <stdlib.h>
typedef struct player{
char name[50];/*名前を代入するname*/
int number;/*背番号を代入する変数number*/
int at_bat;/*打数を代入する変数at_bat*/
int hit;/*安打数を代入する変数hit*/
double ave;/*打率を代入する変数ave*/
} PLAYER;
PLAYER makePlayerData(char *s)
{
PLAYER x;
sscanf(s, "%s %d %d %d", x.name, &x.number, &x.at_bat, &x.hit);
x.ave = ((double)x.hit / (double)x.at_bat);
return x;
}
int printPlayerData(PLAYER p)
{
return printf("%s %d %d %d %f", p.name, p.number, p.at_bat, p.hit, p.ave);
}
int main(int argc, char *argv[])
{
PLAYER *head;/*先頭アドレス用*/
PLAYER *p;/*作業用*/
char buf[256], x;/*fgets用*/
int player_num;/*データ数(選手数)*/
int i;/*作業用*/
FILE *fp;/*ファイルポインタ*/
/*コマンドラインパラメータの数をチェック*/
if(argc != 2){
printf("Usage: %s file_name\n", argv[0]);
exit(1);
}
/*ファイルを開く*/
char *rfile = argv[1]; fp =fopen(rfile, "r");
if(fp == NULL){
printf("File %s doesn't exits.\n", argv[1]);
exit(1);
}
/*データ数(選手数)を読み込む*/
fgets(buf, sizeof(buf), fp);/*1行目だけ読み込んでくる*/
x = atoi(buf);
sscanf(buf, "%s", &x);/*ここ*/
/*メモリ領域の動的確保*/
head = (PLAYER *)malloc(sizeof(PLAYER)*200);
/*PLAYERデータの生成*/
p = head;
while(fgets(buf, sizeof(buf), fp) != NULL){/*ここ*/
*p = makePlayerData(buf);/*ここ*/
p++;
}
fclose(fp);/*ファイルを閉じる*/
/*PLAYERデータの出力*/
printf("%-8s %8s %8s %8s %8s\n", "選手名", "背番号", "打数", "安打", "打率");
player_num = 0;
for(i = 0, p = head + player_num; i < player_num; i++, p--)/*ここ*/
printPlayerData(*(p - 1));/*ここ*/
/*データ領域の開放*/
free(head);
return 0;
}
このように直してみました
b20144@IPCVDI-A215-L:~/pro1$ cc Execise12.1.c
b20144@IPCVDI-A215-L:~/pro1$ ./a.out carp5.dat
選手名 背番号 打数 安打 打率
エラーは消えたのですがファイルが読み込めてない感じです
またここはsscanfのみで答えなければいけなくatoiは使用するスペースが課題には書いておらず今はatoiを用いたら出来るのかなと思い使いました
ファイルを読み込むためには出力の部分が間違ってるのでしょうか・・・?
#include <stdio.h>
#include <stdlib.h>
typedef struct player{
char name[50];/*名前を代入するname*/
int number;/*背番号を代入する変数number*/
int at_bat;/*打数を代入する変数at_bat*/
int hit;/*安打数を代入する変数hit*/
double ave;/*打率を代入する変数ave*/
} PLAYER;
PLAYER makePlayerData(char *s)
{
PLAYER x;
sscanf(s, "%s %d %d %d", x.name, &x.number, &x.at_bat, &x.hit);
x.ave = ((double)x.hit / (double)x.at_bat);
return x;
}
int printPlayerData(PLAYER p)
{
return printf("%s %d %d %d %f", p.name, p.number, p.at_bat, p.hit, p.ave);
}
int main(int argc, char *argv[])
{
PLAYER *head;/*先頭アドレス用*/
PLAYER *p;/*作業用*/
char buf[256], x;/*fgets用*/
int player_num;/*データ数(選手数)*/
int i;/*作業用*/
FILE *fp;/*ファイルポインタ*/
/*コマンドラインパラメータの数をチェック*/
if(argc != 2){
printf("Usage: %s file_name\n", argv[0]);
exit(1);
}
/*ファイルを開く*/
char *rfile = argv[1]; fp =fopen(rfile, "r");
if(fp == NULL){
printf("File %s doesn't exits.\n", argv[1]);
exit(1);
}
/*データ数(選手数)を読み込む*/
fgets(buf, sizeof(buf), fp);/*1行目だけ読み込んでくる*/
x = atoi(buf);
sscanf(buf, "%s", &x);/*ここ*/
/*メモリ領域の動的確保*/
head = (PLAYER *)malloc(sizeof(PLAYER)*200);
/*PLAYERデータの生成*/
p = head;
while(fgets(buf, sizeof(buf), fp) != NULL){/*ここ*/
*p = makePlayerData(buf);/*ここ*/
p++;
}
fclose(fp);/*ファイルを閉じる*/
/*PLAYERデータの出力*/
printf("%-8s %8s %8s %8s %8s\n", "選手名", "背番号", "打数", "安打", "打率");
player_num = 0;
for(i = 0, p = head + player_num; i < player_num; i++, p--)/*ここ*/
printPlayerData(*(p - 1));/*ここ*/
/*データ領域の開放*/
free(head);
return 0;
}
このように直してみました
b20144@IPCVDI-A215-L:~/pro1$ cc Execise12.1.c
b20144@IPCVDI-A215-L:~/pro1$ ./a.out carp5.dat
選手名 背番号 打数 安打 打率
エラーは消えたのですがファイルが読み込めてない感じです
またここはsscanfのみで答えなければいけなくatoiは使用するスペースが課題には書いておらず今はatoiを用いたら出来るのかなと思い使いました
ファイルを読み込むためには出力の部分が間違ってるのでしょうか・・・?
Re: プログラミングの課題で・・・
なんで「読み込む」のに「出力の部分が間違ってる」と思うのですか?daigakusei さんが書きました:ファイルを読み込むためには出力の部分が間違ってるのでしょうか・・・?
やるべきことは、このようにdaigakusei さんが書きました: /*データ数(選手数)を読み込む*/
fgets(buf, sizeof(buf), fp);/*1行目だけ読み込んでくる*/
x = atoi(buf);
sscanf(buf, "%s", &x);/*ここ*/
「どうせ後の処理で使用しないx」に「読み込んだ文字列をそのまま」書き込むことではありません。
「後の処理で使用するplayer_num」に「読み込んだ文字列を数値に変換して」書き込むのです。
atoiは「atoiを用いたら出来る」などという魔法のアイテムではありません。sscanfのみという指定があるのに無視して使う必要もありません。
さらに、このコードは1文字分の領域しかないxに(改行や終端も含めた)複数文字を書き込むので、未定義動作です。
数値を表す文字列から数値への変換は、makePlayerData関数で行われているのと同様にsscanfの書式%dを用いてできます。
ここではplayer_num個出力しています。daigakusei さんが書きました: /*PLAYERデータの出力*/
printf("%-8s %8s %8s %8s %8s\n", "選手名", "背番号", "打数", "安打", "打率");
player_num = 0;
for(i = 0, p = head + player_num; i < player_num; i++, p--)/*ここ*/
printPlayerData(*(p - 1));/*ここ*/
player_numに0を代入しているため、0個出力します。
すなわち、出力しません。
「ファイルが読み込めてない感じ」であっても、本当にファイルが読み込めてないのでしょうか?daigakusei さんが書きました:ファイルが読み込めてない感じです
問題の切り分けをするために、デバッガでデータが読み込めているかを確認するといいでしょう。
デバッガが使えない環境であれば、例えばループとは関係なくprintfで最初のデータを出力してみる、などの確認方法が考えられます。
[hr]
トピックを解決せず放置してはいけません。
まずはC言語の宿題が・・・を解決させてからこっちの課題に取り組んだ方がいいのではないでしょうか?
フォーラムルールより引用
3. 禁止行為について
以下の行為を禁止行為として定めます。
(中略)
[C言語何でも質問掲示板でのみ適用される事項]
(中略)
質問後、お礼を言わずにトピックを閉じる、または去る行為
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)