//逆波兰计算器
#define number '0' //在字符串中找到一个数的标志
#define maxop 100 //操作数或运算符的最大长度
char push(double);
double pop(void);
int getop(char []);
int main()
{
int type;
double op2;//操作符option
char s[maxop];
while ((type=getop(s))!=EOF)
{
switch (type)
{
case number://char类型的数据可以自动转换为int类型的数据
//当getop函数想要返回一个数值操作数时,并不能像返回运算符一样
//直接将字符串s中的内容赋值给type,所以需要一个标志来表示返回数值操作数的情况
//本程序选择'0'为这种情况的标志,随后将字符串s转化为浮点数
push(atof(s));
break;
case '+':
push(pop() + pop());
break;
case '-':
//变量op2用处:
//'+','*'运算满足交换律,所以操作数的弹出无先后顺序。
//而'-''/'则相反,运算符的左右操作数应该加以区分
op2 = pop();
push(pop() - op2);
break;
case '*':
push(pop() * pop());
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;
}
#define maxval 100
//涉及到的重要问题:
//1.使用外部变量的原因:
//在计算器程序设计的过程中的一个重要的问题是要把栈的相关信息放在那里
//一种是把它放在主函数中,把栈及其当前位置作为参数传递给被调用的pop、push函数
//但主函数其实不需要了解控制栈的变量信息,只需要进行压入和弹出操作即可
//所以可以把有关栈的信息作为外部变量,只供pop、push函数访问即可
//2.栈的相关变量内容应该放在主函数之后
//因为其相关内容只在pop、push函数访问,而主函数未引用。所以其只需放在pop、push函数的外部
//然后同时还要注意对主函数而言要将它隐藏
int strplace = 0;//栈中下一个空闲的位置(栈顶指针)
double val[maxval];//栈用顺序表来存放
char push(double f)
{
if (strplace < maxval)
{
val[strplace++] = f;
}
else
printf("error:stack has been full,can't push\n");
}
double pop(void)
{
if (strplace > 0)
{
return val[--strplace];//example:1 2 +
//'+'不需要进行pop操作,所以先进行自减操作,然后再返回
}
else
{
print("error:stack empty,can't pop\n");
return 0.0;
}
}
int getch();
void ungetch(int);
//getop函数用来获取下一个运算符或数值操作数
int getop(char s[])
{
int c , i;
//(s[0] = c = getch()) == ' ' || c = '\t'系统提示表达式必须是可修改的左值
//原因是此时||运算符右侧为赋值语句,那么其右侧逻辑值必定为1,导致循环条件逻辑值必为1,无法修改
while ((s[0] = c = getch()) == ' ' || c == '\t')
;//该while循环语句的内部执行顺序:
//首先从输入内容中读取一个字符,即c=getch()
//接着将字符c放入字符串中,s[0]=c
//所以说循环语句的条件是c == ' '||c == '\t',为了跳过空格和换行符
s[1] = '\0';//为什么要在这里设置终止符'\0'以及终止符的作用
//如果读取的字符为操作符,那么就需要将字符串s的长度设置为1,也就是将终止符'\0'放在s[1]处。而且被调用函数将参数传回的字符串必须是有效的
//终止符作用:标记字符串的结束位置,从而使得strlen()等其他对字符串进行读写操作的函数知道什么时候应该停止读取字符串s
//如果返回的是未知字符,则需要打印该字符,如果无终止符,将无法打印出该字符串
if (!isdigit(c) && c != '.')
return c;
if (isdigit(c))//收集整数部分
while (isdigit(s[++i] = c = getch()));
if(c=='.')//收集小数部分
while (isdigit(s[++i] = c = getch()));
s[i] = '\0';
if (getch() != EOF)
ungetch(c);
//getch()与ungetch()的用途:
//经常出现的一种情况是程序不能确定它锁读入的输入是否足够,除非超前读取一些输入
//在该程序中,读取一些字符以合成数字就会出现这种情况。因为它无法确定已经读取的数字是否完整。
//所以就需要多读取一些输入。但这又会出现的问题是多读取的内容不属于当前所要读入的数字
//解决方法:
//如果能够“反读”不需要的字符,也就是将多读取(已读取)的内容在压回到输入中。这就相当于没有读入该内容一样
//可以通过getch()与ungetch()这对互相协作的函数来进行该操作。getch函数用于读取下一个待处理的字符,
//ungetch函数用于把(多读取的)字符放回到输入中,更具体一些,就是将字符先放在缓存区中。当它不为空时,
//getch函数先从缓存区中读取字符,之后再调用getchar函数读取输入。
return number;
}
#define bufsize 100
//缓存区相关外部变量:
//为了判断缓存区是否为空,这里还需要一个下标变量来记录缓存区中当前字符的位置
//由于缓存区与下标变量是getch()与ungetch()两个函数需要共享的信息,同时在调用的过程中值不能发生变化
//对于其他函数也需要隐藏,所以要将它们设置为这两个函数的外部变量
char buf[bufsize];
int bufp = 0;//缓存区buf中下一个空闲位置
int getch(void)
{
return (bufp > 0) ? buf[--bufp] : getchar();
//--bufp的原因:
//因为bufp表示缓存区中下一个空闲位置,那说明该位置此时无字符,所以需要自减后再进行读取操作
}
void ungetch(int ch)
{
if (bufp >= bufsize)
printf("too many characters in buf\n");
else
buf[bufp++] = ch;
}