K&R 演習4-11 解答 (プログラミング言語C 第2版)

スポンサーリンク

鍛錬 847

K&R 演習4-11 解答 (プログラミング言語C 第2版)

ungetch を必要としないように getop を変更せよ。
ヒント:内部的な static 変数を使え。

B.W.カーニハン D.M.リッチー 石田晴久 訳 『プログラミング言語C 第2版 ANSI 規格準拠』, (共立出版, 2017), pp.101.

 
スポンサーリンク

プログラム

本書中の逆ポーランド記法を使用した電卓プログラムにおいて、ungetch()関数を必要としないように、getop()関数を変更したプログラムです。

pp.101 の時点でまだ登場していないライブラリ関数などはできる限り使用しないようにし、本書中で作成した関数を使用するようにしています。
記述方法(「 char *s 」または「 char s[] 」など)についても、できる限り pp.101 時点での記載通りにしています。

calc.h:

// preprocessor
#define NUMBER  '0'		// 数字が存在した

// prototype
int getop(char[]);
void push(double);
double pop(void);
int getch(void);
void ungetch(int);

main.c:

// include
#include <stdio.h>
#include <stdlib.h>
#include "calc.h"

// preprocessor
#define MAXOP   100		// 被演算数,演算子の最大サイズ

// main
int main(void)
{
	int type;
	double op2;
	char s[MAXOP];
	
	while ((type = getop(s)) != EOF) {
		switch (type) {
			case NUMBER:
				push(atof(s));
				break;
			case '+':
				push(pop() + pop());
				break;
			case '*':
				push(pop() * pop());
				break;
			case '-':
				op2 = pop();
				push(pop() - op2);
				break;
			case '/':
				op2 = pop();
				if (op2 != 0.0) {
					push(pop() / op2);
				}
				else {
					printf("error: zero divisor\n");
				}
				break;
			case '\n':
				printf("\t%.8g\n", pop());
				break;
			default:
				printf("error: unknown command %s\n", s);
				break;
		}
	}
	
	return 0;
}

getch.c:

// include
#include <stdio.h>

// preprocessor
#define BUFSIZE 100		// 入力文字を押し戻す際に利用するバッファのサイズ

// variable
static char buf[BUFSIZE];	// ungetc()用のバッファ
static int bufp = 0;		// バッファbufにおける次の空き位置

// =================================================
// @brief  1文字を取得する
// @param  無し
// @return buf[--bufp] -> バッファに押し戻された文字
// @return getchar() -> 入力された1文字
// @note   無し
// =================================================
int getch(void)
{
	return (bufp > 0) ? buf[--bufp] : getchar();
}

// ============================================
// @brief  文字を入力に戻す(バッファに格納する)
// @param  c [in],入力に戻す文字
// @return 無し
// @note   無し
// ============================================
void ungetch(int c)
{
	if (bufp >= BUFSIZE) {
		printf("ungetch: too many characters\n");
	}
	else {
		buf[bufp++] = c;
	}
}

getop.c:

// include
#include <stdio.h>
#include <ctype.h>
#include "calc.h"

// ==================================================
// @brief  次の演算子あるいは数値の被演算数を取得する
// @param  s [out],取得した文字列
// @return c -> 演算子や改行コード
// @return NUMBER -> フラグ(数値を取得した)
// @note   無し
// ==================================================
int getop(char s[])
{
	int i, c;
	static int buf = EOF;	// 押し戻された文字が格納されるバッファ
	
	if (buf != EOF) {
		c = buf;
		buf = EOF;
		if (c == ' ' || c == '\t') {
			while ((s[0] = c = getch()) == ' ' || c == '\t') {
				;
			}
		}
	}
	else {
		while ((s[0] = c = getch()) == ' ' || c == '\t') {
			;
		}
	}
	s[1] = '\0';
	if (!isdigit(c) && c != '.') {
		return c;
	}
	
	i = 0;
	if (isdigit(c)) {
		while (isdigit(s[++i] = c = getch())) {
			;
		}
	}
	if (c == '.') {
		while (isdigit(s[++i] = c = getch())) {
			;
		}
	}
	s[i] = '\0';
	if (c != EOF) {
		buf = c;
	}
	
	return NUMBER;
}

stack.c:

// include
#include <stdio.h>
#include "calc.h"

// preprocessor
#define MAXVAL  100		// スタックの最大深さ

// prototype
int getop(char[]);
void push(double);
double pop(void);
int getch(void);
void ungetch(int);

// variable
static int sp = 0;			// スタックポインタ
static double val[MAXVAL];	// スタック

// =====================================
// @brief  fの値をスタックにプッシュする
// @param  f [in],プッシュする値
// @return 無し
// @note   無し
// =====================================
void push(double f)
{
	if (sp < MAXVAL) {
		val[sp++] = f;
	}
	else {
		printf("error: stack full, can't push %g\n", f);
	}
}

// ==============================================
// @brief  スタックから一番上の値をポップして返す
// @param  無し
// @return val[--sp] -> スタックから取得した値
// @note   エラーの場合は return で 0.0 を返す
// ==============================================
double pop(void)
{
	if (sp > 0) {
		return val[--sp];
	}
	else {
		printf("error: stack empty\n");
		return 0.0;
	}
}

実行結果

以下は、プログラムを実行しています。

***@ubuntu:~/***/test/c$ 
***@ubuntu:~/***/test/c$ gcc -Wall -Wextra main.c getch.c getop.c stack.c -o kr_4_11
***@ubuntu:~/***/test/c$ ./kr_4_11
2 3 +
	5
5 2 -
	3
1 0.23 +
	1.23
1.11 3 *
	3.33
9.9 3 /
	3.3