ページ 1 / 1
C言語の宿題が・・・
Posted: 2017年1月15日(日) 12:58
by 獅子丸
プログラミングの質問です
C言語です
PLAYER構造体、makePlayerData関数、printPlayerData関数を用いて
char *data = [ ] = {"Maru 9 557 162", "Kikuchi 33 574 181", "Tanaka 2 581 154"};
のようにchar 型のポインタ配列で与えられているものをPLAYER構造体の配列を用いて三人分の名前、背番号、打数、安打数、打率を処理するプログラムを作成せよ.
また、データ順とは逆順に出力すること.
↓[ プログラムリスト(main関数) ]
int main(void)
{
char *data[] = {"Maru 9 557 162", "Kikuchi 33 574 181", "Tanaka 2 581 154"};
int i;/*作業用*/
三人分のデータを処理するプログラム
・PLAYER構造体を用いること
・makePlayerData関数を呼び出すこと
・printPlayerdata関数を呼び出すこと
・三人分の処理はマクロ名PLAYER_NUMを使用して行う
この部分が分かりません・・・
↓
/* -*-coding: utf-8-*- */
#include <stdio.h>
#define PLAYER_NUM 3
/*PLAYER構造体の定義*/
typedef struct string{
char name[50];/*名前を代入するname*/
int number;/*背番号を代入する変数number*/
int at_bat;/*打数を代入する変数at_bat*/
int hit;/*安打数を代入する変数hit*/
double ave;/*打率を代入する変数ave*/
} PLAYER;
/*makePlayerData 関数の定義*/
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;
}
/*printPlayData 関数の定義*/
int printPlayData(PLAYER p)
{
return printf("%s %d %d %d %f", p.name, p.number, p.at_bat, p.hit, p.ave);
}
int main(void)
{
char *data[] = {"Maru 9 557 162", "Kikuchi 33 574 181", "Tanaka 2 581 154"};
int i;/*作業用*/
PLAYER p;
for(i = PLAYER_NUM - 1; i >= 0; i--){
p = makePlayerData(data);
printPlayerData(p);
}
printf("\n");
return 0;
}
Execise11.2.c: In function ‘main’:
Execise11.2.c:32:2: warning: passing argument 1 of ‘makePlayerData’ from incompatible pointer type [enabled by default]
p = makePlayerData(data);
^
Execise11.2.c:13:9: note: expected ‘char *’ but argument is of type ‘char **’
PLAYER makePlayerData(char *s)
^
/tmp/cciaCcvH.o: 関数 `main' 内:
Execise11.2.c:(.text+0x1b3): `printPlayerData' に対する定義されていない参照です
collect2: error: ld returned 1 exit status
このようにエラーが出てしまいました
また他にもおかしいところがあれば教えて欲しいです
お願いします!
Re: C言語の宿題が・・・
Posted: 2017年1月15日(日) 13:40
by みけCAT
ソースコードを提示する際は、BBCodeが有効な(無効にしない)状態でBBCodeのcodeタグで囲んでいただけると、見やすくてありがたいです。
また、宗教上の理由や穴埋め問題で指定された部分以外変更できないなどの事情がなければ、インデントも整えるといいでしょう。
獅子丸 さんが書きました:
Execise11.2.c:32:2: warning: passing argument 1 of ‘makePlayerData’ from incompatible pointer type [enabled by default]
p = makePlayerData(data);
^
Execise11.2.c:13:9: note: expected ‘char *’ but argument is of type ‘char **’
PLAYER makePlayerData(char *s)
^
書いてある通り、渡しているデータの型が違います。
p = makePlayerData(data);をp = makePlayerData(data
);とすると改善するでしょう。
獅子丸 さんが書きました:
/tmp/cciaCcvH.o: 関数 `main' 内:
Execise11.2.c:(.text+0x1b3): `printPlayerData' に対する定義されていない参照です
書いてある通り、printPlayerDataが定義されていません。
printPlayData関数の名前をprintPlayerDataにするといいでしょう。
獅子丸 さんが書きました:また他にもおかしいところがあれば教えて欲しいです
複数のデータを区切り文字無しで連続して出力しているのは不自然な気がしますが、
「データは1行に1人分ずつ出力すること」などの条件はないので、条件は満たしており問題はないでしょう。
Re: C言語の宿題が・・・
Posted: 2017年1月15日(日) 18:35
by box
獅子丸 さんが書きました:
また他にもおかしいところがあれば教えて欲しいです
間違いというわけではありませんが、
獅子丸 さんが書きました:
int printPlayData(PLAYER p)
{
return printf("%s %d %d %d %f", p.name, p.number, p.at_bat, p.hit, p.ave);
}
printPlayerData(p);
int型の戻り値をどこでも使っていないので、思い切ってvoid型にしてみる(関数の中身はprintfするだけにして、
何もreturnしない)、という手はあるかもしれません。
Re: C言語の宿題が・・・
Posted: 2017年1月16日(月) 00:07
by Math
まず構造体の定義が間違っている。
コード:
/*PLAYER構造体の定義*/
struct string{ //typedef struct string{
char name[50]; //name[50];/*名前を代入するname*/
int number;/*背番号を代入する変数number*/
int at_bat;/*打数を代入する変数at_bat*/
int hit;/*安打数を代入する変数hit*/
double ave;/*打率を代入する変数ave*/
} PLAYER;//--------typedef struct string PLAYERと同義
この関数は不可能。
コード:
PLAYER makePlayerData(char *s)
{
PLAYER x; //x.name
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;
}
ここの意味は
http://dixq.net/forum/viewtopic.php?f=3&t=18707を参照してください。(少し難しいですよ)
コード:
int main(void) // 2次元配列です。可変長文字配列(*data)[一次元配列] の配列です。
{ // data[0] data[1] data[2] ---配列の初期化
char *data[] = {"Maru 9 557 162", "Kikuchi 33 574 181", "Tanaka 2 581 154"};
int i;/*作業用*/
PLAYER p;
まだおかしいところがあります。
Re: C言語の宿題が・・・
Posted: 2017年1月16日(月) 00:31
by みけCAT
Math さんが書きました:まず構造体の定義が間違っている。
main関数の中身を消して
コンパイルが通ることを確認しました。
なぜそう思うのですか?
Math さんが書きました:コード:
/*PLAYER構造体の定義*/
struct string{ //typedef struct string{
char name[50]; //name[50];/*名前を代入するname*/
int number;/*背番号を代入する変数number*/
int at_bat;/*打数を代入する変数at_bat*/
int hit;/*安打数を代入する変数hit*/
double ave;/*打率を代入する変数ave*/
} PLAYER;//--------typedef struct string PLAYERと同義
これは、型struct stringおよびその型を持つ変数PLAYERを宣言するコードです。
Math さんが書きました:typedef struct string PLAYERと同義
にはなりません。
Math さんが書きました:
この関数は不可能。
コード:
PLAYER makePlayerData(char *s)
{
PLAYER x; //x.name
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;
}
確かに、この
Mathさんが投稿した関数を使ってはいけません。
なぜなら、char*型の引数が対応しないといけない書式%sに対してchar(*)[50]型のデータが渡され、
未定義動作になってしまうからです。
Math さんが書きました:まだおかしいところがあります。
そうですね。
例えば、文字列リテラルは書き換えてはいけないので、char*型ではなくconst char*型のポインタで指すようにした方が安心でしょう。
Re: C言語の宿題が・・・
Posted: 2017年1月16日(月) 00:58
by Math
VC2008でチェックしたのでウブンツウ系ではどうかわかりませんが。構造体のchar宣言がない点と関数でxを返しても関数を抜けた時点で構造体のインスタンスはなくなるはずでは?
Re: C言語の宿題が・・・
Posted: 2017年1月16日(月) 01:12
by みけCAT
Math さんが書きました:構造体のchar宣言がない点
どういう意味ですか?
「構造体のchar宣言」は必要、もしくはMathさんが必要と考えるものですか?
Math さんが書きました:関数でxを返しても関数を抜けた時点で構造体のインスタンスはなくなるはずでは?
自動変数xのインスタンスは消滅しますが、この場合xではなくxのコピーを返すので問題ありません。
Re: C言語の宿題が・・・
Posted: 2017年1月16日(月) 01:35
by Math
あ、失礼しました。私のケアレスミスです。charは間違いです。xの返却値については構造体のアドレスならコピーで返せるけれど実体は返せないのでは。VC++では関数定義時点のエラーになります。
Re: C言語の宿題が・・・
Posted: 2017年1月16日(月) 01:55
by みけCAT
Math さんが書きました:xの返却値については構造体のアドレスならコピーで返せるけれど実体は返せないのでは。VC++では関数定義時点のエラーになります。
Windows 7 x64、VC++2008では以下の修正後のコードはコンパイルが通りました。

- コンパイル・実行結果
- cgengonosyukudaiga-test-20170116.png (57.67 KiB) 閲覧数: 8979 回
どのようなエラーになるという主張ですか?
また、VC++のバージョンはどれですか?
修正後のコード
コード:
/* -*-coding: utf-8-*- */
#include <stdio.h>
#define PLAYER_NUM 3
/*PLAYER構造体の定義*/
typedef struct string{
char name[50];/*名前を代入するname*/
int number;/*背番号を代入する変数number*/
int at_bat;/*打数を代入する変数at_bat*/
int hit;/*安打数を代入する変数hit*/
double ave;/*打率を代入する変数ave*/
} PLAYER;
/*makePlayerData 関数の定義*/
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;
}
/*printPlayerData 関数の定義*/
int printPlayerData(PLAYER p)
{
return printf("%s %d %d %d %f", p.name, p.number, p.at_bat, p.hit, p.ave);
}
int main(void)
{
char *data[] = {"Maru 9 557 162", "Kikuchi 33 574 181", "Tanaka 2 581 154"};
int i;/*作業用*/
PLAYER p;
for(i = PLAYER_NUM - 1; i >= 0; i--){
p = makePlayerData(data[i]);
printPlayerData(p);
}
printf("\n");
return 0;
}
Re: C言語の宿題が・・・
Posted: 2017年1月16日(月) 07:45
by Math
VC++2008で検証したので”機種依存”があるとは思いますが。結構難しい問題があると思います。
(コードが見にくいので勘違いしてしまったし)
コード:
/*
PLAYER構造体、makePlayerData関数、printPlayerData関数を用いて
char *data = [ ] = {"Maru 9 557 162", "Kikuchi 33 574 181", "Tanaka 2 581 154"};
のようにchar 型のポインタ配列で与えられているものをPLAYER構造体の配列を用いて三人分
の名前、背番号、打数、安打数、打率を処理するプログラムを作成せよ.
また、データ順とは逆順に出力すること.
↓[ プログラムリスト(main関数) ]
*///------------------------------------------------------------------------
//三人分のデータを処理するプログラム
//・PLAYER構造体を用いること
//・makePlayerData関数を呼び出すこと
//・printPlayerdata関数を呼び出すこと
//・三人分の処理はマクロ名PLAYER_NUMを使用して行う
//------------------------------------------------
//この部分が分かりません・・・
/* -*-coding: utf-8-*- */
//------------------------------------------------
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define PLAYER_NUM 3
/*PLAYER構造体の定義*/
typedef struct string{
char name[50];/*名前を代入するname*/
int number;/*背番号を代入する変数number*/
int at_bat;/*打数を代入する変数at_bat*/
int hit;/*安打数を代入する変数hit*/
double ave;/*打率を代入する変数ave*/
} PLAYER;//--->つまりtypedef struct string PLAYER
//--------------------------------------------------------------
/*makePlayerData 関数の定義*/
PLAYER makePlayerData(char *s)//VC++2008では定義時エラーが発生
{
PLAYER x; //x.nameこれにもいるはず
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;
}//--------------------------------------------------------------
/*printPlayData 関数の定義*/
int printPlayerData(PLAYER p) //printPlayData(PLAYER p)
{
//return printf("%s %d %d %d %f", p.name, p.number, p.at_bat, p.hit, p.ave);
printf("%s %d %d %d %f", p.name, p.number, p.at_bat, p.hit, p.ave);
return 0;
}
//----------------------------------------------------------------------------------
int main(void) // 2次元配列です。可変長文字配列(*data)[一次元配列] の配列です。
{ // data[0] data[1] data[2] ---配列の初期化
char *data[] = {"Maru 9 557 162", "Kikuchi 33 574 181", "Tanaka 2 581 154"};
int i;/*作業用*/
PLAYER p;
for(i = PLAYER_NUM - 1; i >= 0; i--){
p = makePlayerData(data[i]); //p = makePlayerData(data);
printPlayerData(p);
}
printf("\n");
return 0;
}
/* --C言語ではたの言語でいう2次元配列は存在しない。配列の配列である---タダシキカクショニ多次元配列ト脚注ニアルノデソウユウらしい
[しかし、これは普通には欠陥ではない。その代わりのデータ構造として、「ディスプレイ」と呼ばれるデータ構造を定義できるからである。
ディスプレイは「配列へのポインタ配列」である。つまり、配列への参照を持つポインタ配列を定義できるのである]とある本に書いてあった...
Execise11.2.c: In function ‘main’: ----アーギュメントのポインターがコンパチブル出ないといる--------
Execise11.2.c:32:2: warning: passing argument 1 of ‘makePlayerData’ from incompatible pointer type [enabled by default]
p = makePlayerData(data);
^ ----char *(配列) でなくchar **(配列に配列)をアーギュメントに要求------
Execise11.2.c:13:9: note: expected ‘char *’ but argument is of type ‘char **’
PLAYER makePlayerData(char *s)
^
/tmp/cciaCcvH.o: 関数 `main' 内: ↓文字通り-----
Execise11.2.c:(.text+0x1b3): `printPlayerData' に対する定義されていない参照です
collect2: error: ld returned 1 exit status
*/
http://www.cc.kyoto-su.ac.jp/~yamada/pB/struct.html
http://www.isl.ne.jp/pcsp/beginC/C_Language_14.htmlを参照してください。
Re: C言語の宿題が・・・
Posted: 2017年1月16日(月) 08:51
by みけCAT
Math さんが書きました:
PLAYER makePlayerData(char *s)//VC++2008では定義時エラーが発生
どのようなエラーですか?
エラーメッセージを貼ってください。
Math さんが書きました:
//x.nameこれにもいるはず
いりません。No: 5でも書いた通り、scanf系の関数に間違った型のデータを渡すと
未定義動作になるので、やってはいけません。
文字列をscanf関数で扱うとき - Qiita
文字列の配列名は、先頭文字のアドレスを指します。この性質があるから、”%s”を指定したときは”&”が不要なのですね。
オフトピック
このQiitaの記事は、正確ではありません。
式中の配列は多くの場合自動的に先頭要素へのポインタに
変換されますが、
配列はアドレス(ポインタ)ではありません。
例えば、
コード:
char str[] = "hello, world";
という配列を使うとき、
sizeof(str)はアドレス(ポインタ)の大きさ(4、8など)ではなく配列の大きさ(終端文字を含んで13)になります。
sizeof演算子や単項&演算子のオペランドは、配列が自動的にポインタに変換されない例です。
Re: C言語の宿題が・・・
Posted: 2017年1月16日(月) 12:24
by Math
あ、すいません。xは”文字列”だから文字配列の”先頭アドレス”でした。配列とポインターの関係は微妙ですね。ポインタ―に変換出来る場合と出来ない場合があって使い慣れない分からないです。
関数のエラーは行番号と識別子として関数名が”出力”に表示(他のエラーとともに)されていたのでxをポインタ―を返すよう変えたらでなくなったので勘違いしました。
それではあとなにが問題なのか?もう出来たのかな。
Re: C言語の宿題が・・・
Posted: 2017年1月16日(月) 16:39
by Math
あれー。これで動くみたいだけど・・・ VS2008:C言語:IDE: c1.c
[結果]
コード:
Tanaka 2 581 154 0.265060Kikuchi 33 574 181 0.315331Maru 9 557 162 0.290844
続行するには何かキーを押してください . . .
[コード]
コード:
/*
PLAYER構造体、makePlayerData関数、printPlayerData関数を用いて
char *data = [ ] = {"Maru 9 557 162", "Kikuchi 33 574 181", "Tanaka 2 581 154"};
のようにchar 型のポインタ配列で与えられているものをPLAYER構造体の配列を用いて三人分
の名前、背番号、打数、安打数、打率を処理するプログラムを作成せよ.
また、データ順とは逆順に出力すること.
↓[ プログラムリスト(main関数) ]
*///------------------------------------------------------------------------
//三人分のデータを処理するプログラム
//・PLAYER構造体を用いること
//・makePlayerData関数を呼び出すこと
//・printPlayerdata関数を呼び出すこと
//・三人分の処理はマクロ名PLAYER_NUMを使用して行う
//------------------------------------------------
//この部分が分かりません・・・
/* -*-coding: utf-8-*- */
//-----------------------------------------------------------------------------
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define PLAYER_NUM 3
/*PLAYER構造体の定義*/
typedef struct string{
char name[50];/*名前を代入するname*/
int number;/*背番号を代入する変数number*/
int at_bat;/*打数を代入する変数at_bat*/
int hit;/*安打数を代入する変数hit*/
double ave;/*打率を代入する変数ave*/
} PLAYER;//--->つまりtypedef struct string PLAYER
//--------------------------------------------------------------
/*makePlayerData 関数の定義*/
PLAYER makePlayerData(char *s)//VC++2008では定義時エラーが発生
{
PLAYER x; //x.nameこれにもいるはず
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;
}//--------------------------------------------------------------
/*printPlayData 関数の定義*/
int printPlayerData(PLAYER p) //printPlayData(PLAYER p)
{
//return printf("%s %d %d %d %f", p.name, p.number, p.at_bat, p.hit, p.ave);
printf("%s %d %d %d %f", p.name, p.number, p.at_bat, p.hit, p.ave);
return 0;
}
//----------------------------------------------------------------------------------
int main(void) // 2次元配列です。可変長文字配列(*data)[一次元配列] の配列です。
{ // data[0] data[1] data[2] ---配列の初期化
char *data[] = {"Maru 9 557 162", "Kikuchi 33 574 181", "Tanaka 2 581 154"};
int i;/*作業用*/
PLAYER p;
for(i = PLAYER_NUM - 1; i >= 0; i--){
p = makePlayerData(data[i]); //p = makePlayerData(data);
printPlayerData(p);
}
printf("\n");
return 0;
}
//------------------------------------------------------------------------------------------------------------------------------
/* --C言語ではたの言語でいう2次元配列は存在しない。配列の配列である---タダシキカクショニ多次元配列ト脚注ニアルノデソウユウらしい
[しかし、これは普通には欠陥ではない。その代わりのデータ構造として、「ディスプレイ」と呼ばれるデータ構造を定義できるからである。
ディスプレイは「配列へのポインタ配列」である。つまり、配列への参照を持つポインタ配列を定義できるのである]とある本に書いてあった...
Execise11.2.c: In function ‘main’: ----アーギュメントのポインターがコンパチブル出ないといる--------
Execise11.2.c:32:2: warning: passing argument 1 of ‘makePlayerData’ from incompatible pointer type [enabled by default]
p = makePlayerData(data);
^ ----char *(配列) でなくchar **(配列に配列)をアーギュメントに要求------
Execise11.2.c:13:9: note: expected ‘char *’ but argument is of type ‘char **’
PLAYER makePlayerData(char *s)
^
/tmp/cciaCcvH.o: 関数 `main' 内: ↓文字通り-----
Execise11.2.c:(.text+0x1b3): `printPlayerData' に対する定義されていない参照です
collect2: error: ld returned 1 exit status
*/
Re: C言語の宿題が・・・
Posted: 2017年1月16日(月) 16:55
by みけCAT
Math さんが書きました:あれー。これで動くみたいだけど・・・
未定義動作を起こした場合の動作は未定義なので、たまたまうまく動いて見える場合もあります。
気持ちのいいものではありません。
Re: C言語の宿題が・・・
Posted: 2017年1月16日(月) 17:16
by Math
何度も試した...
Re: C言語の宿題が・・・
Posted: 2017年1月16日(月) 17:41
by Math
VS2015のコマンドプロンプトでもOK。理屈上は動かないと思うのになぜだろう。(コンピュータはそんなものではあるけど今回2つも思い違いがあった。しかもまだ動かないと思った(sscanfがスペース区切りの字を読めるとは思わなかった))
Re: C言語の宿題が・・・
Posted: 2017年1月16日(月) 22:02
by みけCAT
Math さんが書きました:理屈上は動かないと思う
それはなぜですか?
具体的にどこでどのような不都合が起きるため動かないと思いますか?
Re: C言語の宿題が・・・
Posted: 2017年1月16日(月) 23:10
by Math
関数の返却値はレジスタ1個分なのでインスタンス全体は返せないと思ったけど考えて見ればインスタンスのドメインのメモリが分かっていれば先頭アドレスを返せば良いので今は納得です。それとxは構造体で文字列はnameだったのでそれも納得です。
Re: C言語の宿題が・・・
Posted: 2017年1月16日(月) 23:44
by みけCAT
Math さんが書きました:関数の返却値はレジスタ1個分なのでインスタンス全体は返せないと思ったけど
実装はコンパイラやターゲットによって異なる可能性がありますが、
例えばソースコードに書かれている引数の前に引数のような感じでデータを書き込む先のアドレスを渡し、
呼び出された関数ではそのアドレスに書き込む、という方法があるようです。
テストコード
コード:
struct hoge {
int a, b, c;
};
struct hoge foo(void) {
hoge h;
h.a = 1;
h.b = 2;
h.c = 3;
return h;
}
extern struct hoge foo2(int x);
void bar(hoge* h) {
*h = foo2(1);
}
x86-64 gcc 6.3 (オプション-m32 -O2) の出力
コード:
foo():
mov eax, DWORD PTR [esp+4]
mov DWORD PTR [eax], 1
mov DWORD PTR [eax+4], 2
mov DWORD PTR [eax+8], 3
ret 4
bar(hoge*):
push ebx
sub esp, 24
mov ebx, DWORD PTR [esp+32]
mov eax, esp
sub esp, 8
push 1
push eax
call foo2(int)
mov eax, DWORD PTR [esp+12]
mov DWORD PTR [ebx], eax
mov eax, DWORD PTR [esp+16]
mov DWORD PTR [ebx+4], eax
mov eax, DWORD PTR [esp+20]
mov DWORD PTR [ebx+8], eax
add esp, 36
pop ebx
ret
https://godbolt.org/g/lLwXbu
Re: C言語の宿題が・・・
Posted: 2017年1月17日(火) 20:07
by daigakusei
有難うございました
作動することが出来ました!