c语言逆波兰计算器程序,逆波兰计算器(C语言)

源自《The C Programming Language》 P62 ex4.3:

计算例如:(1 - 2) * (4 + 5)的值,采用逆波兰表示法(即后缀表示法)

代码:

main.c

#include

#include //为了使用库函数atof

#include //使用sin, exp, pow等数学函数

#include //使用strcmp, strlen等字符串函数

#include "getop.h"

#defineMAXOP100//操作数或运算符的最大长度(待处理字符串的最大长度)

#defineNUMBER'0'//标识找到一个数

#defineNAME'n' //标示找到一个数学函数

void push(double );

double pop();

//void printStack(double []);

void clear();

void mathfnc(char []);

//extern double val[];//如果声明为extern val[]; 则报错:变量val被重定义

//extern sp;

//逆波兰计算器

int main()

{

int type;

double op2;

double op1;

//double tmp;

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);//push(pop() - pop());是错误的,虽然算法运算符中操作数的结合方式是从左到右

//但是不能确定push参数中左边的pop函数一定比右边的pop函数先执行

break;

case '/':

op2 = pop();

if(op2 != 0.0)

push(pop() / op2);

else

{

printf("error: divide 0.0!");

return -1;

}

break;

case '%':

op2 = pop();

if(op2 != 0.0)

push(fmod(pop(), op2));

else

printf("error: mod 0.0!");

break;

case '/n'://当键入换行符时,打印输出栈顶元素

/*if(sp > 0)

printStack(val);

else

printf("error: stack empty!/n");

*/

printf("the result = %.8g/n", pop());

break;

case 'p'://不出栈的情况下,打印栈顶元素

op2 = pop();

printf("the top element of stack = %f/n", op2);

push(op2);

break;

case 'd'://复制栈顶元素

op2 = pop();

//tmp = op2;

//printf("the duplication of top element = %f/n", op2);

push(op2);

push(op2);

printf("the duplication of top element = %f/n", op2);

break;

/*case 'S':

push(sin(pop()));

break;

case 'E':

push(exp(pop()));

break;

case 'P':

op2 = pop();

push(pow(pop(), op2));

break;

*/

case NAME://处理数学函数分支,这样比上面分别用每个命令来定义一个函数要通用,并容易扩展

mathfnc(s);

break;

case 's'://交换栈顶元素

op2 = pop();

op1 = pop();

push(op2);

push(op1);

break;

case 'c'://清空堆栈

clear();

break;

default:

printf("error: unknown command %s", s);

break;

}

}

return 0;

}

#defineMAXVAL100//栈val的最大深度

int sp = 0;//栈中的下一个空闲的位置

double val[MAXVAL];//值栈

void push(double f)//把f压入值栈中

{

if(sp < MAXVAL)

val[sp++] = f;

else

printf("error: stack full, can't push %g/n", f);

}

double pop()//从值栈中弹出并返回栈顶的值

{

if(sp > 0)

return val[--sp];

else

{

printf("error: stack empty, can't pop/n");

return 0.0;

}

}

/*void printStack(double* val)

{

printf("top of stack = %f/n", val[sp-1]);

}

*/

void clear()//清空值栈

{

sp = 0;

return;

}

void mathfnc(char s[])//数学函数处理的通用接口

{

double op2;

if(strcmp(s, "sin") == 0)

push(sin(pop()));

else if(strcmp(s, "cos") == 0)

push(cos(pop()));

else if(strcmp(s, "exp") == 0)

push(exp(pop()));

else if(strcmp(s, "pow") == 0)

{

op2 = pop();

push(pow(pop(), op2));

}

else

printf("error: %s not supported!/n", s);

}

getop.c

#include

#include

#include

#include "getop.h"

//extern NUMBER;

#defineNUMBER'0'

#defineNAME'n'

int getop(char s[])//获取下一个运算符或操作数

{

int i;

int c;

while((s[0] = c = getch()) == ' ' || c == '/t')

;

s[1] = '/0';

i = 0;

if(c != '-' && !islower(c) && !isdigit(c) && c != '.')//判断是否属于这四种情况,如不是,下面分别对这四种情况处理

return c;//当是运算符时,返回此运算符的ASCII值

if(c == '-')

if(isdigit(c = getch()) || c == '.')

s[++i] = c;

else

{

if(c != EOF)

ungetch(c);

return '-';

}

if(islower(c))

{

while(islower(s[++i] = c = getch()))

;

s[i] = '/0';

if(c != EOF)

ungetch(c);

if(strlen(s) > 1)

return NAME;

else

return s[0]; //错误:return c; 例:s = "v ",则

//返回空格,而本意是返回v

}

if(isdigit(c))

while(isdigit(s[++i] = c = getch()))//收集整数部分

;

if(c == '.')

while(isdigit(s[++i] = c = getch()))//收集小数部分

;

s[i] = '/0';

if(c != EOF)

ungetch(c);

return NUMBER;//当是操作数时,返回NUMBER,标识这种情况

}

#defineBUFSIZE100//缓冲区的最大长度

//int buf[BUFSIZE];//这样可以正确处理压回EOF(-1)及其他任何负数的情况

char buf[BUFSIZE];//用于ungetch函数的缓冲区

int bufp = 0;//buf中下一个空闲位置

int getch()//取一个字符(可能是要压回的字符)

{

return (bufp > 0) ? buf[--bufp] : getchar();

}

void ungetch(int c)//把字符压回到输入(缓冲区)中

{

if(bufp >= BUFSIZE)

printf("ungetch: too many characters/n");

else

buf[bufp++] = c;

}

getop.h

#ifndef_GETOP_H_

#define_GETOP_H_

//#include

//#include

//#include

//#defineMAXOP100

//#defineNUMBER'0'

int getch();

void ungetch(int);

int getop(char []);

#endif

分析:

1,  程序设计:在设计本程序时,首先进行模块划分,

main.c:main函数 实现操作数压栈,出栈,算术运算,数学运算,打印 复制 交换栈顶元素等基本操作;

push函数 实现将double型数据压入值栈val中;

pop函数 实现将值栈val中的栈顶元素出栈;

clear函数 实现清空值栈val;

mathfnc函数 实现sin,cos, exp, pow等数学操作(调用math.h中的这些库函数来处理val中的数据

并非自定义上述函数);

getop.c:  getop函数 实现从输入中获取一个操作数或操作符(* + - / % sin d 等操作符);

getch函数 实现从自定义的输入缓冲区(buf)或操作系统定义的输入缓冲区中读入一个字符;

ungetch函数 实现将字符压回到自定义的输入缓冲区(buf)中;

getop.h:  声明getop getch ungetch函数。

2,  main函数中通过while((type = getop(s)) != EOF)处理每次从待处理的输入字符串中获取的s,这是程序的主干部分

在确定type != EOF时,通过switch - case 语句分别处理当type为  NUMBER + - * / % /n p d NAME s c default

等情况。

3,  对于 -  / % 情况不能像 + * 情况直接使用push(pop() - pop()),因为不满足交换律,

虽然算法运算符中操作数的结合方式是从左到右,但是不能确定push参数中左边的pop函数一定比右边的pop函数先执行

4,  getop函数,通过while((s[0] = c = getch()) == ' ' || c == '/t') ; 来跳过s头部的空白字符(空格,水平制表符),

每次对于第一个字符c通过判断(c != '-' && !islower(c) && !isdigit(c) && c != '.')这四种情况来分别处理,

若上述条件成立,表明c是一个例如 + - * /等单字符的操作符;

然后分四种情况:c == '-', islower(c), isdigit(c), c == '.' 进行处理。

注意:将最后一个读入的不符合条件的字符压回到自定义的输入缓冲区中。

getch函数: (bufp > 0) ? buf[--bufp] : getchar(); 从自定义输入缓冲区或OS定义的输入缓冲区中读入一个字符

ungetch函数:把字符压回到自定义的输入缓冲区中

5,  在合适的位置定义变量,例如:getop.c中BUFSIZE,buf,bufp在getop函数没有用到,而只在getch及ungetch函数

中用到,故其定义的位置在getop函数之后,而在getch函数之前,这样就可以防止在getop函数中出现无意修改上述变量

的可能。

确定某些文件用到哪些头文件,例如在在getop.c中用到isdigit等判断字符的函数,故在它里面添加ctype.h头文件,而在

main.c中不用到ctype.h中的库函数,故在main.c中不添加ctype.h头文件。

6,  如果想要ungetch函数正确处理压回的EOF或其他任何负数,则将输入缓冲区buf设置为int buf[BUFSIZE],即缓冲区的

数据类型为int型而不是char型。

C语言不要求char变量是signed或unsigned类型的,当把一个char型变量转换成int型变量,结果可能为正也可能为负,

例如,十进制的-1被表示为十六进制为0XFFFF(假定为一台16位机),当把0XFFFF保存到一个char型变量里去时,实际

被保存的数字是0XFF,当把0XFF转换成一个int型数据时,它可能被转换成0X00FF(255),也可能被转换成0XFFFF(-1)

所以打算对待像其他字符那样对待EOF时,应该把输入缓冲区buf声明成一个int型数组。

注:在某些机器上,如果一个char型变量的最高(左)二进制位为1,那么把它转换成一个int型数据时,就会在它的高位上

添加一系列1,这样得到的结果为负数;

在另一些机器上,当需要把一个char型变量转换成一个int型数据时,系统会在它的高位上添加一系列0,这样不管被

转换的char型变量的最高位是1还是0,结果永远是个正数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值