100 Days of Code-day14

在day12程序的基础上加入了模运算符(%),并注意考虑负数的情况。

//逆波兰计算器
#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'%':
				op2 = pop();
				if (op2 != 0)
					push(fmod(pop(), op2));
				else
					printf("error: zero disvisor\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
	{
		printf("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
	i = 0;
	if (!isdigit(c) && c != '.' && c != '-')//遵循从左到右的顺序进行判断
		return c;
	if (c == '-')
	{
		if (isdigit(c = getch()) || c == '.')
			//此处为了判断'-'究竟表示减号还是负号
			//若为减号,其后面为空格 比如 2 3 - 2 4 +
			//若为负号,其后面无空格 如 -1.234
			//当'-'为负号,继续读取并分别收集整数和小数部分
			//若读取的内容为数字,就不用判断其是否为'.'
			//其原因如下:
			//由&&和||连接的表达式按照从左到右的顺序进行求值
			//并且在知道结果值为真或假就会立即停止计算
			//但对于浮点数来说比较特殊,在C语言中其可以省略输入整数部分
			//所以当输入"-.12"时,就相当于输入"-0.12"
			s[++i] = c;
		else 
		{//程序要确定读取数字的完整性,所以需要多读一个字符
			if (c != EOF)
				ungetch(c);
			return '-';//返回输入的结尾
		}
	}
	//收集数据,为了收集完整的数,需要多进行一次判断,而getch函数中调用的getchar函数不具备
	//记录每次读取内容的能力。所以在读取到非数字内容时,应该将其压入到缓存区当中(也就是执行ungetch函数)
	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);
	//getch()与ungetch()的用途:
	//经常出现的一种情况是程序不能确定它锁读入的输入是否足够,除非超前读取一些输入
	//在该程序中,读取一些字符以合成数字就会出现这种情况。因为它无法确定已经读取的数字是否完整。
	//所以就需要多读取一些输入。但这又会出现的问题是多读取的内容不属于当前所要读入的数字
	//解决方法:
	//如果能够“反读”不需要的字符,也就是将多读取(已读取)的内容在压回到输入中。这就相当于没有读入该内容一样
	//可以通过getch()与ungetch()这对互相协作的函数来进行该操作。getch函数用于读取下一个待处理的字符,
	//ungetch函数用于把(多读取的)字符放回到输入中,更具体一些,就是将字符先放在缓存区中。当它不为空时,
	//getch函数先从缓存区中读取字符,之后再调用getchar函数读取输入。
	return number;
}

#define bufsize 100
//缓存区相关外部变量:
//为了判断缓存区是否为空,这里还需要一个下标变量来记录缓存区中当前字符的位置
//由于缓存区与下标变量是getch()与ungetch()两个函数需要共享的信息,同时在调用的过程中值不能发生变化
//对于其他函数也需要隐藏,所以要将它们设置为这两个函数的外部变量
char buf[bufsize];
int bufp = 0;//缓存区buf中下一个空闲位置

//当缓存区buf为空时,执行getchar函数,它从输入中读取一个字符,相当于清除掉输入中的该字符
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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值