加減剰余の演算機の作成

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

加減剰余の演算機の作成

#1

投稿記事 by 初心者 » 17年前

大学で次のような課題がでたんですがどのようにやればいいかわかりません。
もしわかる方がいれば教えてください。
[1] 授業単元: プロフラミング実習II
[2] 問題文(含コード&リンク):
コマンドライン版 加減乗除 演算機の作成
 
※必ずコマンドラインから入力データを与えてください.本課題の必須要件です.

使い方: calc 数値1 [+|-|*|[/url] 数値2

(例)calc 4 + 2 <enter>
6.00

上記の例に示すようなコマンドライン版の超簡単な加減乗除演算機を作成してください.
加減乗除ですから,+ - * / の4つのオペレータ(加減乗除演算子)を考えるだけで十分です.
括弧の処理はなくて構いません.
括弧による優先順位や四則演算の優先順位を考慮すると格段に難しい問題になります.
したがって本課題では考慮の必要はありません.

コマンドラインから与える「数値1」と「数値2」は実際には文字列として main 関数に渡されます.ご注意ください.

例にあるように,コマンド名calcと数値1の間には半角スペースを入れます.同様に数値1とオペレータの間にも半角スペースを入れます.もちろんオペレータと数値2の間にもスペースが必要です.

例では小数点以下2桁で結果を表示しています.表示方法は各自で工夫してください.

[3] 環境
 [3.1] OS: WindowsXP
 [3.2] コンパイラ名とバージョン:gcc
 [3.3] 言語: C

よろしくお願いいたします。

box

Re:加減剰余の演算機の作成

#2

投稿記事 by box » 17年前

サンプルです。
#include <stdio.h>
#include <stdlib.h>

void error_msg(char *s);

int main(int argc, char *argv[/url])
{
	double a, b, c;
	char op;
	
	if (argc != 4)
		error_msg(argv[0]);
	
	a = atof(argv[1]);
	b = atof(argv[3]);
	switch (op = argv[2][0]) {
	case '+':
		c = a + b;
		break;
	case '-':
		c = a - b;
		break;
	case '*':
		c = a * b;
		break;
	case '/':
		c = a / b;
		break;
	default:
		error_msg(argv[0]);
		break;
	}
	printf("%f %c %f = %f\n", a, op, b, c);
	return 0;
}

void error_msg(char *s)
{
	fprintf(stderr, "%s 数値1 [+|-|*|[/url] 数値2\n", s);
	exit(1);
}

keichan

Re:加減剰余の演算機の作成

#3

投稿記事 by keichan » 17年前

エラー処理をまったくしていないサンプルコードです。
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[/url])
{
    /// argcは4が入力されていることを前提として作成(エラー処理省く)

    double lp = 0.0, rp = 0.0;
    double ret = 0.0;

    lp = atof(argv[1]);
    rp = atof(argv[3]);

    switch( argv[2][0] ) {
        case '+':
            ret = lp + rp;
            break;
        case '-':
            ret = lp - rp;
            break;
        case '*':
            ret = lp * rp;
            break;
        case '/':
            ret = lp / rp;
            break;
    }

    printf("%.2f", ret);
    return 0;
}

初心者

Re:加減剰余の演算機の作成

#4

投稿記事 by 初心者 » 17年前

すみませんがまだあまりC言語について理解できてない部分もあるのでソースの説明もしていただけねいでしょうか?

keichan

Re:加減剰余の演算機の作成

#5

投稿記事 by keichan » 17年前

具体的にどの部分の解説が必要でしょうか?
わからない部分を列挙していただけると助かります。

box

Re:加減剰余の演算機の作成

#6

投稿記事 by box » 17年前

先ほどのソースにコメントを付けてみました。
#include <stdio.h>
#include <stdlib.h>

void error_msg(char *s);

int main(int argc, char *argv[/url])
{
	double a, b, c;				/* 計算用の数値 */
	char op;					/* 加減乗除の演算子 */
	
	/* このプログラムは、
	 * プログラム名 数値1 演算子 数値2 <Enter>
	 * という形式で実行する。コマンドライン引数の数は、
	 * プログラム名を含めて4個。
	 * 想定どおりの形式でない場合、エラーメッセージを出力する。
	 */
	if (argc != 4)
		error_msg(argv[0]);
	
	/* コマンドライン引数の文字列には、
	 * 0番目:プログラム名
	 * 1番目:数値1
	 * 2番目:加減乗除の演算子
	 * 3番目:数値2
	 * の順に格納してある。1番目と3番目の文字列を、atof関数で
	 * 浮動小数点数に変換する。
	 */
	a = atof(argv[1]);
	b = atof(argv[3]);
	
	/* コマンドライン引数の文字列の2番目の先頭文字(先頭文字以外は無視)が
	 * 加減乗除の演算子である。演算子の内容に応じて、適切な計算を行なう。
	 * 演算子が正しくなければ、エラーメッセージを出力する。
	 * switch~case構文では、switch文のカッコ内の内容に応じて、適切なcase文の
	 * ところの内容を実行する。
	 */
	switch (op = argv[2][0]) {
	case '+':
		c = a + b;
		break;
	case '-':
		c = a - b;
		break;
	case '*':
		c = a * b;
		break;
	case '/':
		c = a / b;
		break;
	default:
		error_msg(argv[0]);
		break;
	}
	
	/* 「数値1 演算子 数値2 = 演算結果」の形式で出力する。 */
	printf("%f %c %f = %f\n", a, op, b, c);
	return 0;
}

/* エラーメッセージを出力する関数
 * メッセージ出力後、exit関数でプログラムを終了する。
 */
void error_msg(char *s)
{
	fprintf(stderr, "%s 数値1 [+|-|*|[/url] 数値2\n", s);
	exit(1);
}

シンゴ

Re:加減剰余の演算機の作成

#7

投稿記事 by シンゴ » 17年前

boxさんのソースは理解できたんですがコマンドプロンプトでやってみてもちゃんと動きません。
わかるかたがいれば教えてください。

box

Re:加減剰余の演算機の作成

#8

投稿記事 by box » 17年前

> コマンドプロンプトでやってみてもちゃんと動きません。

「ちゃんと動かない」という現象を、具体的に説明してください。
途中経過を省略して最後の結果だけ報告されても、どうしようもありません。

コマンドプロンプトでどんな風に実行したとき、どうなったので
「ちゃんと動かない」と判断されましたか?
何かエラーメッセージが出たのでしたら、すべて見せてください。

keichan

Re:加減剰余の演算機の作成

#9

投稿記事 by keichan » 17年前

>boxさんのソースは理解できたんですがコマンドプロンプトでやってみてもちゃんと動きません。
どういったオペレーションをされました?
ちゃんと動きません。ってどう動かないんですか?
具体的に書いてください。

管理人

Re:加減剰余の演算機の作成

#10

投稿記事 by 管理人 » 17年前

まず、ほとんど処理は同じですが、keichanさんのプログラムが簡単なんで、そちらを見て理解してからboxさんの
プログラムを見てはいかがでしょうか。(boxさんのプログラムはエラー処理されているので、完成させるときはこちらで)

おそらく質問者さんが、わからないで困っていると思われる部分の説明が書いてあるURLです。

まず、main関数の引数であるargcについて

http://okuyama.mt.tama.hosei.ac.jp/unix ... e67-1.html

ここや、

http://www9.plala.or.jp/sgwr-t/c/sec11-4.html

この辺で、解説がされています。
コマンドラインで入力した文字をとってくるわけですね。

atof関数は文字のデータを小数点付きで計算できる浮動小数点型に変換する関数。

コマンドラインの数値は数字ではなく、文字として扱われていますから、それを「数値データ」に変換する必要があります。

引数にしているargv[1]、argv[3]については先ほどのリンク先で勉強してみましょう。

switch関数は、条件によってcaseの内容を処理するものです。

http://www9.plala.or.jp/sgwr-t/c/sec06-5.html

%.2fというのは小数点台2位まで表示するという書き方です。

他にどこかわからないところありますか?

シンゴ

Re:加減剰余の演算機の作成

#11

投稿記事 by シンゴ » 17年前

すいません。コマンドプロンプトで実行すると
Usage:
calc num1 [+|-|*|[/url] num2
とは表示されるのですがそこで終わってしまい最初に書いてあるような
(例)calc 4 + 2 <enter>
6.00
とゆう感じに入力することができません。

管理人

Re:加減剰余の演算機の作成

#12

投稿記事 by 管理人 » 17年前

calc num1 [+|-|*|[/url] num2 とは表示されるのですが
ということですが、boxさんのプログラムもkeichanさんのプログラムも
そのような記述はありません。

コンパイルしているファイルが違うのじゃないのですか?

後、初心者さんとシンゴさんは同じ方ですか?

シンゴ

Re:加減剰余の演算機の作成

#13

投稿記事 by シンゴ » 17年前

いえ初心者さんとは一緒に問題を解いてただけです。
えっと違うのをコンパイルしていました。すみません。
でも、もう一度確認してやってみたら
数値1 [+|-|*|[/url] 数値2
とでただけで数値を入力できませんでした。

box

Re:加減剰余の演算機の作成

#14

投稿記事 by box » 17年前

> 数値1 [+|-|*|[/url] 数値2

このメッセージを表示したということは、
コマンドプロンプトでの実行方法が正しくないということです。

実行ファイル名がcalc.exeで、4+2の計算を行ないたければ、
コマンドプロンプトで
calc 4 + 2 <Enter>
のように実行してください。

calcと4と+と2の間に、各々1個以上の空白をあけてください。

シンゴ

Re:加減剰余の演算機の作成

#15

投稿記事 by シンゴ » 17年前

できました!!ありがとうございます。
boxさんの言うとおりやりかたが違ってました。
親切に教えてくださってありがとうございました。

c言語頑張る

加減剰余の演算機の作成

#16

投稿記事 by c言語頑張る » 17年前

はじめまして!!
このプログラムの問題に興味をもちました!!
このプログラムを見て、このプログラムは理解できたのですが、
他に別解ってのはないでしょうか??
参考までに教えてもらえないでしょうか???
是非C言語の勉強(特にコマンドライン)になると思うのでお願いします!!

box

Re:加減剰余の演算機の作成

#17

投稿記事 by box » 17年前

こんなのはどうでしょう。
#include <stdio.h>
#include <stdlib.h>

void usage(char *s);

int main(int argc, char *argv[/url])
{
	double a, b, c;
	char op;
	
	if (argc != 4)
		usage(argv[0]);	
	a = atof(argv[1]);
	b = atof(argv[3]);
	if (sscanf(argv[2], "%1[+-*[/url]", &op) != 1)
		usage(argv[0]);
	c = (op - '+') ? (op - '-') ? (op - '*') ? a / b : a * b : a - b : a + b;
	printf("%f %c %f = %f\n", a, op, b, c);
	return 0;
}

void usage(char *s)
{
	fprintf(stderr, "%s 数値1 [+|-|*|[/url] 数値2\n", s);
	exit(1);
}

Justy

Re:加減剰余の演算機の作成

#18

投稿記事 by Justy » 17年前

 sscanfの使い方が素晴らしいです(w

 では、私はこんなかんじで。
[color=#d0d0ff" face="monospace]#include <stdio.h>
#include <stdlib.h>

typedef double (*op_func)(double, double);
static double op_add(double a, double b) { return a + b; }
static double op_sub(double a, double b) { return a - b; }
static double op_mul(double a, double b) { return a * b; }
static double op_div(double a, double b) { return a / b; }
static double op_none(double a, double b){ return 0; }

static op_func get_op(char c)
{
    return c=='+'? op_add: c=='-'? op_sub: c=='*'? op_mul: c=='/'? op_div: op_none;
}

int main(int argc, char* argv[/url])
{
    if(argc >= 4)
    {
        char *p, *q, c = *argv[2];
        double l = ((p = argv[1]), strtod(p, &q)),
               r =  ((p = argv[3]), strtod(p, &q));
        printf("%f %c %f = %f\n",
                l, c, r, get_op(c)(l, r));
    }
    return 0;
}[/color]

box

Re:加減剰余の演算機の作成

#19

投稿記事 by box » 17年前

sscanfの使い方を上級者に笑われてしまったので、書き換えてみました。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void usage(char *s);

int main(int argc, char *argv[/url])
{
	double a, b, c;
	char *op, *ops = "+-*/";
	
	if (argc != 4)
		usage(argv[0]);
	a = atof(argv[1]);
	b = atof(argv[3]);
	if ((op = strpbrk(argv[2], ops)) == NULL)
		usage(argv[0]);
	c = (*op - '+') ? (*op - '-') ? (*op - '*') ? a / b : a * b : a - b : a + b;
	printf("%f %c %f = %f\n", a, *op, b, c);
	return 0;
}

void usage(char *s)
{
	fprintf(stderr, "%s 数値1 [+|-|*|[/url] 数値2\n", s);
	exit(1);
}

box

Re:加減剰余の演算機の作成

#20

投稿記事 by box » 17年前

関数ポインタを使った例です。
#include <stdio.h>
#include <stdlib.h>

typedef double (*Func)(double, double);

double Add(double a, double b);
double Sub(double a, double b);
double Mul(double a, double b);
double Div(double a, double b);
void usage(char *s);

int main(int argc, char *argv[/url])
{
	Func funcs[/url] = { Add, Sub, Mul, Div };
	double a, b, c;
	char op, ops[/url] = "+-*/";
	int n, i;
	
	if (argc != 4)
		usage(argv[0]);
	a = atof(argv[1]);
	b = atof(argv[3]);
	n = sizeof(ops) / sizeof(ops[0]);
	for (i = 0; i < n; i++)
		if ((op = argv[2][0]) == ops)
			break;
	if (i == n)
		usage(argv[0]);
	c = funcs(a, b);
	printf("%f %c %f = %f\n", a, op, b, c);
	return 0;
}

double Add(double a, double b)
{
	return a + b;
}

double Sub(double a, double b)
{
	return a - b;
}

double Mul(double a, double b)
{
	return a * b;
}

double Div(double a, double b)
{
	return a / b;
}

void usage(char *s)
{
	fprintf(stderr, "%s 数値1 [+|-|*|[/url] 数値2\n", s);
	exit(1);
}

C言語頑張る

Re:加減剰余の演算機の作成

#21

投稿記事 by C言語頑張る » 17年前

BOXさんにお願いがあるのですが、BOXさんのプログラムのソース説明を、
お願いしてもいいですか??本当に申し訳ないです。。。

box

Re:加減剰余の演算機の作成

#22

投稿記事 by box » 17年前

> BOXさんのプログラムのソース説明を、

3つ書きましたが、全部ですか?
いずれにしましても、これから寝るところであります。
何時間かお待ちください。

Re:加減剰余の演算機の作成

#23

投稿記事 by » 17年前

sscanfの使ったものと、使ってないものでお願いしてもいいですか??

管理人

Re:加減剰余の演算機の作成

#24

投稿記事 by 管理人 » 17年前

どの辺がわかりませんか?ちょっと複雑なところは

c = (*op - '+') ? (*op - '-') ? (*op - '*') ? a / b : a * b : a - b : a + b;

この辺だけだと思いますけど。
x=10;
y = (x==1) ? 1 : 2;
この構文はxが1ならyに1を代入し、違うなら2を代入すると言う意味です。
このxは1ではないので、実際2が入ります。

(*op - '*')ここまでを最初に見ましょう。
足し算でもなく、引き算でもなく、かけ算でもないときは、
割り算。
足し算でもなく、引き算でもなく、かけ算であるときは
掛け算
足し算でもなく、引き算であるときは
引き算
足し算であるときは
足し算
と言う意味です。
a = atof(argv[1]);
	b = atof(argv[3]);
	if ((op = strpbrk(argv[2], ops)) == NULL)
		usage(argv[0]);
	c = (*op - '+') ? (*op - '-') ? (*op - '*') ? a / b : a * b : a - b : a + b;
	printf("%f %c %f = %f\n", a, *op, b, c);
数字がはいっているはずの[1],[3]をatofで数値に変換し、strpbrkでopsに入っている文字を探索します。
探索した所でポインタを返し、opに格納。
そこの文字がたすなのか、ひくなのか、かけなのか、わるなのかで計算を変えます。
条件にあった計算方法で計算された結果をcに格納しています。

管理人

Re:加減剰余の演算機の作成

#25

投稿記事 by 管理人 » 17年前

ですから、エラー処理などを行わない最小のプログラムはこのように成ると思います。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[/url]){
	double a, b, c;
	char *op, *ops = "+-*/";
	a = atof(argv[1]);
	b = atof(argv[3]);
	op = strpbrk(argv[2], ops);
	c = (*op - '+') ? (*op - '-') ? (*op - '*') ? a / b : a * b : a - b : a + b;
	printf("%f\n",c);
	return 0;
}
strpbrkについては

http://www9.plala.or.jp/sgwr-t/lib/strpbrk.html

この辺を読んでみるとよくわかりますよ。

boxさんのプログラムなのに、横から失礼しましたm(_ _)m

box

Re:加減剰余の演算機の作成

#26

投稿記事 by box » 17年前

> sscanfの使ったものと、使ってないものでお願いしてもいいですか??
if (sscanf(argv[2], "%1[+-*[/url]", &op) != 1)
		usage(argv[0]);
argv[2]という、文字列のポインタから、四則演算子を取り出します。
argv[2]の先頭1文字を取り出し、char型の変数opに格納します。
argv[2]の先頭1文字の形式は、sscanfの第2引数で指定しているとおり、
+-*/のいずれかであることを想定しています。
opに正しく格納できたら、sscanfの戻り値は1(格納した変数の個数)となります。
+-*/以外を指定したなどの理由でsscanfの戻り値が
1以外の(つまり、opに正しく格納できなかった)場合は、
usageで使用法を表示して、プログラムを終了します。

sscanfのかわりにstrpbrkを使う場合の解説は、管理人さんの回答をごらんください。

ところで、今回の三項演算子の使い方は、わかりにくいかもしれません。
足し算かどうかの判定と実際に足し算を行なうところとが
大きく離れているためです。
カッコを補ってみると、少しはわかりやすくなるかもしれません。
実際にやってみましょう。
c = (op - '+') ? ((op - '-') ? ((op - '*') ? a / b : a * b) : a - b) : a + b;
あるいは、こんな風に書く方がもっとわかりやすいかもしれません。
c = (op == '+') ? a + b :
	    (op == '-') ? a - b :
	    (op == '*') ? a * b : a / b;
いずれにせよ、あまりにも技巧に走りすぎると、コードは短くなるけれど
理解するのに時間がかかってしまう、ということがあるかもしれません。

今回、いくつかサンプルを紹介しましたが、いちばんわかりやすいのは
最初に挙げた、switchで分岐する例ではないかと思います。
一見、愚直な感じがするかもしれませんが、
自分以外の人(注:「将来の自分自身」を含む。コードを書いたときの自分と
保守するときの自分は別人とみなす)が後で保守することなどを
考えると、多くの人がパッと見てすぐに理解できるコードを書いておくことが
重要であります。

管理人

Re:加減剰余の演算機の作成

#27

投稿記事 by 管理人 » 17年前

プログラムに書きなれてくると、難しい構文も別に抵抗無く使える(書いている本人は難しい構文だと意識しなくなる)ようになってくるでしょうけど、
読む人のスキルが違うとすごく見にくかったり理解しづらかったりするときありますよね。
または、ソースを書く人のくせがでたりするものですよね。

私は最初
if(a==1){
		printf("a=1");
	}
このようにいつも処理文を書いていたので
if(a==1)
	{
		printf("a=1");
	}
このような書き方を見ると何か抵抗がありました。
書いた本人に言わせると、後者の方が括弧の対応がはっきりわかってわかりやすいじゃないかと
言います。
確かにその通りだと思いますけど、理屈なしで、前者でなれちゃってるので違和感はぬぐえませんでした。

全ての人が見てわかりやすいコードっていうのはなかなか書きにくいものですよね!

閉鎖

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