- 締切済み
このプログラムを添削してください
#include<stdio.h> #include<math.h> #define n 3 double multi(double a[n],double b[n]); int main(void) { double a[n]; double b[n]; int i; double menseki; for(i=0;i<=n;i++) {scanf("%lf",a[i]); scanf("%lf",b[i]); printf("%dつめの頂点座標は(%d,%d)",i+1,a[i],b[i]); } menseki=multi(a,b); printf("面積は%d",fabs(menseki)); return(0); } double multi(double a[n],double b[n]) { double menseki; menseki=(a[0]*(b[1]-b[2])+a[1]*(b[2]-b[0])+a[2]*(b[0]-b[1]))/2; return(menseki); } 頂点の座標を入力し、ヘロンの公式を用いて三角形の面積を求めるプログラムなのですが、 正常に動作しません。お力添えお願いします。
- みんなの回答 (7)
- 専門家の回答
みんなの回答
- YKKIKS63
- ベストアンサー率44% (22/50)
すこし、時間的猶予ができたので、さらに改めて質問のコードを見ていると... scanf文の引数の書き方が間違っている? scanf(変換指定文字, 代入したい変数のアドレス) ではなかったか? とすれば、 scanf("%lf", a[i]); ・・・a[i]は変数そのもの。 scanf(%lf", &a[i]); ・・・&a[i]は... これでも頂点座標の入力表示が意図通りでないなら、(本当にうろ覚えで自信ないのですが)ということなら、[Enter]の問題の可能性あり。 ...ネットで調べると、下記URLにこの問題についての説明を発見しました。 http://www9.plala.or.jp/sgwr-t/c/sec05.html とりあえず、静的にチェック(実際にプログラムを動かすのではなく、コードレビュー等、ソースコードを眺めてチェックする)して気づいた点を、何回もにわたって思いつくままに回答してしまい、混乱を生じさせただけかもしれませんが、今の私(の記憶と、手元の情報、PC環境(Cコンパイラ動かせず)、時間的余裕)でできる範囲で回答しました。
- YKKIKS63
- ベストアンサー率44% (22/50)
本当にぽろぽろとすみません。 さらに、あやふやな記憶に基づく回答ですみません。 配列の要素が増えることを考慮して、「#defineで置き換える」ことはOKです。というより、その方がよいと思います。 (あるいは、定数で3を定義して使うという手もありますが、この場合、グローバル変数という、これまたあまり好まれない 仕組みを使うことになりますので。ケースバイケースで判断することになります。) もちろん、(おそらく、'#define'(プリプロセッサのひとつで、単に文字の置き換えをするだけだから、大文字と小文字は区別して置き換えが実施されると思いますので)、小文字のnではなく大文字のNを#defineしても期待通りに前処理(プリプロセス)されると思います。 参考までにですが、今回の場合、私なら#deifneで定義する文字を単なる'n'ではなく、例えば #define CHOUTEN 3 とかのように、あとで見てわかりやすい範囲で適度な長さの文字列で表現します。 そうすると、'CHOUTEN'という文字列部分だけが'3'に置き換わります。 ... 当方の事情により、そろそろ、PCをシャットダウンすることになりそうですので、以下、ご参考になればとおもい、気づいたことを思いつくまま書いておきます。 ●C言語では配列は必ず'0'から始まる。これはご存知ですよね。 for文のループがひとつ多いのでは? 配列はa、bともに要素数3。それに対して、for文では、0で始まり...iはいくつまでループすることになりますか? double a[3];の宣言で用意されるのは、a[0]、a[1]、a[2]まで コード上、見た目は問題ないように見えますが、実際には、a[3]、b[3]にまでデータ入力していることになります。 これは、本来用意していないメモリエリアa[3]、b[3]にデータを入力することになり、「メモリリーク」という、意図しない メモリエリアの破壊(C言語のプログラムが変数等のデータを記憶している領域の破壊ことなので、OSが壊れるとかでは ありません)の問題を発生させてしまいます。 こんかいのプログラムで動作上問題ないとしても、正しい作法として、データ入力はあくまでも、自分で用意(宣言した)範囲 に対してのみ行うようにしましょう。 ※もしかすると、次の問題を解消するために意図的に1回余分にループさせた? そうであるなら、とりあえずは、配列宣言では、3ではなく、4としましょう。 そうでないなら、一般的にC言語における配列への代入のためのループでは、 double a[α]; の宣言の場合、 for(i=0; i<α; i++) のように、"<="ではなく、"<"とするとよいと思います。 ・scanf文で「キーボードから数値を入力」していますが、手計算の結果と、プログラムの実行結果が明らかに違うとか、実行エラーが発生するとかの場合は、計算で使用する配列全てにキーボードから入力した値が入っているかを確認してみてください。 ※確認方法:scanf文の次の行に、printf文を入れて、a[i]やb[i]の値を画面に出してみる。 ※キーボードから何か入力する場合、入力操作のための[Enter]キー入力までプログラムで拾ってしまい、意図したとおり の入力操作ができない、ということは初心者講座ではよく起きてましたので。 この問題が発生した場合は、何とか、キーボード入力時の[Enter]入力を無視するようにしなければなりません。 [Enter]入力を見越して、ダミー変数に[Enter]キー入力を代入するとか、今回の場合でしたら、計6回入力するのではなく、 たとえば、カンマ(, )区切りで6個データをまとめて一回でキー入力してしまうとか。(この場合はキー入力を受ける部分の コードを変更する必要がありますが。) ・#で始まる行:C言語の本とかで調べる場合のキーワードは、「プリプロセッサ」、「前処理」等 先の回答とかで示した'%f'とか、コードにある'fabs'とかも含め、グーグルとかで、 「C言語 fabs」とか「C言語 %f」とかで検索すると、わかりやすい解説もヒットするようです。 ちなみに、関数へのデータの渡し方(引数)については、「参照渡し」とか「値渡し」とかが、情報を探す際のキーワードとして 使えます。 ついでに、ある企業でC言語を(新人に)教える立場であった経験でいうと、 各行の行頭は適当に字下げを使う方が見やすくなります。また、for(i = 0; i < n; i++)とかscanf("%d", a[i])のように、適当に スペースを入れた方が見やすいコードになりますよ。 (本サイトにコードをアップするために、字下げしていないだけなら本コメントは余計なお世話ですね。) 以上、最後までフォローできませんでしたが、ご容赦ください。 プログラムの勉強、頑張ってください。
- YKKIKS63
- ベストアンサー率44% (22/50)
ぽろぽろと細切れですみません。 printf文内で、double型変数をfabsに与えていますが、fabs()関数は、 引数の値の「絶対値」を「double型」で返す関数ではないでしょうか? よって、面積の計算結果を整数値で表示したいのであれば、fabs関数は不適切。 double型を整数型に変換する関数、というかC言語ならキャストという手もあるのですが、 企業におけるプログラミングでは、キャストはよくないものとされています。(よい悪いの理由はあるのですが省きます) 面積計算式では、double型の四則演算をしていますので、結果はdouble型を期待?であるなら、%fを使用。 ここまで書いていてもう一つ気づいた点を。 ただし、以下、昔の経験によるうろ覚えの記憶だけを頼りに書いていますので、的外れ、間違いだらけでしたら 無視してください。 C言語の本を引っ張り出せたら、あるいは、Cのコンパイル環境があればよいのですが、いずれも、今すぐには 無理な状況で回答しておりますので。(それなら回答するなといわれそうですが、昔C言語を教える立場であったこと もあり、自分でコードをかいて、悩んでいる方を見ると、おせっかいを焼きたくなりまして...) multi関数の引数表記がおかしいと思います。 "double a[n],double b[n]"の記述で、配列aとbのすべてのデータ値をmulti関数に渡したいようですが、 C言語の文法ではこのような記述はできません。 先の#defineの結果を考えると、multi関数には、a[3]、b[3]の値しか渡されていません。 正しくは、 double multi(double* a, double* b) というように、呼び出しで、multi(a,b)として、配列aとbのアドレスを渡しているので、それを受ける側の関数では 引数はポインタで受ける必要があると思います。 そうすると、multiの中では、配列a,bのすべてのデータを使えると思います。 なお、ポインタ宣言しているので普通は、*(a+1)とか書きますが、C言語の配列表記はポインタ表記の簡易版と いう側面がありますので、a[1]と書いても動作上問題ないかと。
- YKKIKS63
- ベストアンサー率44% (22/50)
#2,3回答者です。 質問者様の補足と僕の追加回答のタイミングのずれで、手間取らせてしまっていますね。 #3への補足を見て「コンパイルはできた」とのことですので、あと一つ気づいた点を。 double型の変数の値を表示しようとしていますが、"%d"って、整数型10進数のデータ値を表示するものでは なかったでしょうか? '%f'の方が適切。 またまた的外れならすみませんが、取り急ぎ気づいた点を回答いたします。
- YKKIKS63
- ベストアンサー率44% (22/50)
#2回答者です。 今、#1回答に対する補足を読み、改めてコード見ましたら、multiはユーザ定義の関数ですね。すみません。 でも、やはり、#define文が気になります。 おそらくこのdefineで、配列部分を全て、[3]としたいのであろうと思いますが、[n]の部分を[3]に置き換えることが できるということは、例えば、retuenは「retur3」となるということです。 「#define A B」は、コンパイル前に、ソースコード中のすべてのAという単語を機械的にBという単語に置き換えた上で コンパイルする、というものです。 よって、このソースコードの場合、ソースコード中のすべての'n'という単語(今回はたまたま1文字ですが)をすべて'3' という文字に置き換えてからコンパイルすることになります。
補足
では例えばこのnをNに変更すれば、というわけではないのですよね? (実行してみましたが駄目でした) 配列内の変数をdefineで定義すること自体さけたほうがいいのでしょうか?
- YKKIKS63
- ベストアンサー率44% (22/50)
#1の回答者様同様、multi()がどんなものかよくわからないのですが。 C言語はだいぶご無沙汰であり、全くの的外れかもしれないのですが、 「#define n 3」 こんなことをすると、ソースコード中の'n'という文字がすべて'3'に置き換わってしまい、その後 コンパイルすることになるから、コンパイルエラーになるのでは? 「正常に動作しない」というのが「コンパイルエラー」を指しているのであれば一度検証してみてください。 なお、質問する際には、どんなエラーになっているのか、エラーにはならないが、期待通りの結果にならない等々 詳しく説明された方がよろしいかと思います。
補足
言葉足らずですいません。 multi関数は#1に補足した通り自分で製作した任意の関数です。 正常に動作しないというのは、コンパイル後実行される結果で 頂点座標の()内に有効な値が表示されないことを指します。
- himajin100000
- ベストアンサー率54% (1660/3060)
うーんmulti関数の意味がわからん。(正常に動作しないのは当然じゃね?) http://ideone.com/iTDvj
補足
一般的なC言語の書籍の関数の項で、任意の関数を製作する箇所があるじゃないですか。意味が分からないとはどの部分でしょうか?
補足
解答通りに修正しましたが症状変わらずです・・・・・・ 入力表示した座標が有効な数字にならず、1075970048等 が表示されてしまいます。 またお時間ありましたらご教授いただけると助かります。 ありがとうございました!