C语言程序可以看成由一系列的外部对象构成,这些外部对象可能是变量或函数。形容词external与internal是相对的。internal用于描述定义在函数内部的函数参数及变量。外部变量定义在函数之外可以在其他函数中使用。C语言不允许在函数中定义其他函数。外部变量永久存在,自动变量在退出函数消失。互不调用对方并且需要共享数据的两个函数,最好还是用外部变量。
书上说这个计算器程序的实现很简单,但是感觉是相对而言的,当时看这个的时候由于基础有限,看的还是比较难受的,但是当看到后面基础牢了点就好很多,现在来看这个确实是比较简单的一个程序,每个函数负责一个功能,而这些函数并不复杂,难的是想通其中的关键部分。
主要设计思路在书里这也不在多说。
逆波兰计算器
这个计算器例子我想了很长时间怎么解释,从哪部分开写,最后还是不想了,从头到后来吧。
定义函数时 push前面的void声明表示psh函数不返回任何值,括号里的double表示他要处理双精度浮点数类型的参数,pop的double声明表示他返回double类型的数值,但是没有任何附带参数。
首先看getop函数,用于获取下一个运算符或操作数。
这里又需要先说一下getch函数和ungetch函数
这两个函数想明白比较简单,想理解有那么一点复杂。
getch函数
返回一个值这个值有可能是在缓冲区也有可能是直接getchar获取,首先用一个外部变量bufp当做缓冲区的下标,因为两个函数修需要共享并且不互相调用,所以需要用到外部变量。
还有就是把buf当成缓冲区。
这个函数直接返回数值。条件判断式的值就是其返回值。
用一个条件表达式,判断buf缓冲区是否有字符存在。如果有则判断式的值为buf数组的第bufp个元素,如果buf数组没有字符存在则直接用getchar读取字符。然后将值返回。
return (bufp > 0) ? buf[--bufp] : getchar();
然后是ungetch函数。
这个函数将字符压入缓冲区。如果超过最大值则报警。
如果超过最大值则打印报警字符串。
if (bufp >= BUFSIZE)
printf("ungetch: too many characters\n");
不超过就直接将字符存入缓冲区buf数组。
buf[bufp++] = c;
再继续看getop函数。
跳过空白符,这里会将第0位元素保留为空白符(求指正)。
这里的保留为空白符是因为如果首位就是数字的话,还是需要保留的.因为首位是数字.s[0]就被赋值成数字了.
然后就会覆盖后面的空白符.
while ((s[0] = c = getch()) == ' ' | c == '\t')
;
然后将第1位元素置为‘\0’
s[1] = '\0'; 这里会把数组s变成一个只有一个空白符和结束符的数组。
之后是判断部分,首先判断是不是数,如果不是数并且不是小数点则将当前的字符返回给主函数main,让主函数去判断解决方法。
if (!isdigit(c) && c != '.')
return c;
接下来的将i赋值为0。
i = 0;
然后是收集整数部分,这里是将整数部分直接存入数组s中,这里的++i是前自增运算,先自增之后才参与赋值运算(这里最开始他的值是1,我做过测试,但是也不能保证肯定,希望有人指正。)
如果下标是1开始的话,那么之前的空格就会被留下了。(虽然留下也没什么atof函数我记得也会跳过空白符)
将整数部分的数收集到数组s中。
if(isdigit(c))
while (isdigit(s[++I] = c = getch()))
;
然后是碰到小数点,继续以同样的方式收集到数组s中。这里的i依然是先自增,目的是跳过的整数部分的最后一位。
if (c == '.')
while (isdigit(s[++i] = c =getch()))
;
最后将结束符'\0'加入数组s中。
s[i] = '\0';
然后继续判断,数值读取到最后还没到EOF文件结束符的话,那么需要将最后读入的这个字符放入缓冲区(原因书上写了,无法判断下一个字符到底是不是对程序有用的字符,只能先读取之后再看,所以如果不需要的话就需要将其放入缓冲区)
if (c != EOF)
ungetch(c);
然后都运行到这里了,就需要向主函数返回一个读取的是数的信号,
return NUMBER;
函数既然写这么多了,那就继续把剩下的两个比较简单和函数写完吧。
push函数。
作用就是将参数放入一个数组(栈)中。原理同上面的buf一样。如果不超过栈的最大值那么就将参数f放入到栈中。用变量sp记录栈顶的位置。
if (sp < MAXVAL)
val[sp++] = f;
如果超过最大值则显示错误报告。
else
printf("error: stack full, can't push %g\n", f);
pop函数就是将栈顶的元素取出变成返回主函数的值,就是说把pop函数当做栈顶的值。
if (sp > 0)
return val[--sp];
之后依然是错误报告,并且返回值为0.0。
之后进入主函数。
先定义变量type,用于确定字符是何种类型。op2用于直接指定运算顺序。数组s[MAXOP]需要运算的数。宏MAXOP是操作数或运算符的最大长度(100)。宏NUMBER也是一个用于判断类型。
int type;
double op2;
s[MAXOP];
直接用大循环判断当前字符的类型。之后就是通过switch语句进行分类操作,类型是什么就执行什么样的操作。,这里书上写的比较详细而且没什么难度,代码还很长久不直接写上来了。