1、编写程序expr,以计算从命令行输入的逆波兰表达式的值,其中每个运算符或操作数用一个单独的参数表示。例如,命令expr 2 3 4 + * 将计算表达式2×(3+4) 的值。

#include <stdio.h>
#include <stdlib.h> // for atof() #define MAXOP 100 // max size of operand or operator
#define NUMBER '0' // signal that a number was found int getop(char []);
void ungets(char []);
void push(double);
double pop(void); // reverse Polish calculator; uses command line
int main(int argc, char *argv[])
{
char s[MAXOP];
double op2; while(--argc > 0)
{
ungets(" "); // push end of argument
ungets(*++argv); // push an argument switch(getop(s))
{
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");
default:
printf("error: unknown command %s\n", s);
argc = 1;
break;
}
}
printf("\t%.8g\n", pop());
return 0;
}

这里给出的解决方案是在TCPL Reading Notes 中的逆波兰计算器的基础上得到的。它使用了push 和pop 函数。我们先利用ungets 函数把一个参数结束标记(' ',一个空格字符)和一个参数依次压入输入缓冲区。这样,我们就可以不加修改地使用getop 函数了。getop 将调用getch 读取字符并分离出下一个运算符或操作数。如果在读取参数的过程中遇到了错误,argc 将被设置为1,主函数中的while 循环while(--argc > 0) 将因条件表达式的求值结构为假而终止程序运行。如果来自命令行的是一个合法的表达式,它的计算结果就将被放在堆栈的最顶部,这个结果将在我们把输入参数全部处理完毕后被打印。

2、在TCPL Reading Notes 中第76 条复杂声明中,dcl 程序有很多限制(该程序的目的意在说明问题,并不想做的尽善尽美),它只能处理类似于char 或int 这样的简单数据类型,而无法处理函数中的参数类型或类似于const 这样的限定符。它不能处理带有不必要的空格的情况。由于没有完备的出错处理,因此它也无法处理无效的声明。这里,我们对它进行一定程度上的修改,使它能够处理输入中的错误。

#include <stdio.h>
#include <string.h>
#include <ctype.h> enum { NAME, PARENS, BRACKETS };
enum { NO, YES }; void dcl(void);
void dirdcl(void);
void errmsg(char *);
int gettoken(void); extern int tokentype; // type of last token
extern char token[MAXTOKEN]; // last token string
extern char name[MAXTOKEN]; // identifier name
extern char out[1000];
extern int prevtoken; // dcl: parse a declarator
void dcl(void)
{
int ns; for(ns = 0; gettoken() == '*'; ) // count *'s
ns++;
dirdcl();
while(ns-- > 0)
strcat(out, " pointer to");
} // dirdcl: parse a direct declarstor
void dirdcl(void)
{
int type; if(tokentype == '(') // (dcl)
{
dcl();
if(tokentype != ')')
errmsg("error: missing )\n");
}
else if(tokentype == NAME) // variable name
strcpy(name, token);
else
errmsg("error: expected name or (dcl)\n");
while((type=gettoken()) == PARENS || type == BRACKETS)
if(type == PARENS)
strcat(out, " function returning");
else
{
strcat(out, " array");
strcat(out, token);
strcat(out, " of");
}
} // gettoken.c 源文件
#include <ctype.h>
#include <string.h> enum { NAME, PARENS, BRACKETS };
enum { NO, YES }; extern int tokentype; // type of last token
extern char token[]; // last token string
int prevtoken = NO; // there is no previous token // gettoken: return next token
int gettoken(void) // return next token
{
int c, getch(void);
void ungetch(int);
char *p = token; if(prevtoken == YES)
{
prevtoken = NO;
return tokentype;
}
while((c = getch()) == ' ' || c == '\t')
;
if(c == '(')
{
if((c = getch()) == ')')
{
strcpy(token, "()");
return tokentype = PARENS;
}
else
{
ungetch(c);
return tokentype = '(';
}
}
else if(c == '[')
{
for(*p++ = c; (*p++ = getch()) != ']'; )
;
*p = '\0';
return tokentype = BRACKETS;
}
else if(isalpha(c))
{
for(*p++ = c; isalnum(c = getch()); )
*p++ = c;
*p = '\0';
ungetch(c);
return tokentype = NAME;
}
else
return tokentype = c;
}

我们对函数dirdcl 做了一些修改,它现在能够分析出两种记号——跟在dcl 调用后的一个右括号(')')或一个变量名。如果不是这两种记号,我们将调用函数errmsg 而不是printf。errmsg 会先打印一条出错信息,然后把变量prevtoken 设置为YES 以通知gettoken 说已经读入了一个记号。gettoken 开头部分有一个新的if 语句:

if(prevtoken == YES){
prevtoken = NO;
return tokentype;
}

它的意思是:如果已经有了一个记号,就不要再读入一个新记号了。这个改进版本并不是十全十美的,但它已经具备一定的出错处理能力了。

3、修改TCPL Reading Notes 中第76 条复杂声明中的undcl 程序,使它在把文字描述转换为声明的过程中不会生产多余的圆括号。

#include <stdio.h>
#include <string.h>
#include <ctype.h> #define MAXTOKEN 100 enum { NAME, PARENS, BRACKETS }; void dcl(void);
void dirdcl(void);
int gettoken(void);
int nexttoken(void); int tokentype; // type of last token
char token[MAXTOKEN]; // last token string
char out[1000]; // undcl: convert word description to declation
int main(void)
{
int type;
char temp[MAXTOKEN]; while(gettoken() != EOF)
{
strcpy(out, token);
while((type = gettoken()) != '\n')
if(type == PARENS || type == BRACKETS)
strcat(out, token);
else if(type == '*')
{
if((type = nexttoken()) == PARENS || type == BRACKETS)
sprintf(temp, "(*%s)", out);
else
sprintf(temp, "*%s", out);
}
else if(type == NAME)
{
sprintf(temp, "%s %s", token, out);
strcpy(out, temp);
}
else
printf("invalid input at %s\n", token);
printf("%s\n", out);
}
return 0;
} enum { NO, YES }; int gettoken(void); // nexttoken: get the next token and push it back
int nexttoken(void)
{
int type;
extern int prevtoken; type = gettoken();
prevtoken = YES;
return type;
}

如果"x 是一个指向char 的指针",undcl 程序的输入将是:x * char。改进前的undcl 程序的输出结果是:char (*x)。这里,输出结果中的括号是多余的。事实上,只有当下一个记号是() 或[] 时,undcl 程序才有必要在自己的输出结果中使用括号。

再比如,如果"daytab 是一个指针,它指向一个有[13] 个int 元素的数组",undcl 程序的输入将会是:daytab * [13] int。改进前的程序的输出结果:int (*daytab)[13] 就是正确的。但是,如果"daytab 是一个有[13] 个元素的指针数组,数组中的每个元素分别指向一个int",undcl 程序的输入将是:daytab [13] * int,改进前的undcl 程序的输出结果:int (*daytab[13]) 中就会有多余的圆括号。

我们对undcl 进行了修改,让它检查下一个记号是不是() 或[]。如果下一个记号是() 或[],undcl 程序就必须给它加上括号;否则,输出结果中的括号就将是多余的。也就是说,我们必须根据undcl 中程序输入中的下一个记号来决定是否需要添加括号。

我们编写了一个简单的nexttoken 函数,它将调用gettoken,记录已经读入一个记号的事实并返回该记号的类型。gettoken 是我们在上一个练习中编写的一个函数,它在读入下一个记号前会先检查是否已经有一个可用的记号了。改进后的undcl 程序将不再产生多余的括号。

Pointers and Arrays_4的更多相关文章

  1. Leetcode 笔记 117 - Populating Next Right Pointers in Each Node II

    题目链接:Populating Next Right Pointers in Each Node II | LeetCode OJ Follow up for problem "Popula ...

  2. Leetcode 笔记 116 - Populating Next Right Pointers in Each Node

    题目链接:Populating Next Right Pointers in Each Node | LeetCode OJ Given a binary tree struct TreeLinkNo ...

  3. [LeetCode] Populating Next Right Pointers in Each Node II 每个节点的右向指针之二

    Follow up for problem "Populating Next Right Pointers in Each Node". What if the given tre ...

  4. [LeetCode] Populating Next Right Pointers in Each Node 每个节点的右向指针

    Given a binary tree struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode *nex ...

  5. [c++] Smart Pointers

    内存管理方面的知识 基础实例: #include <iostream> #include <stack> #include <memory> using names ...

  6. LEETCODE —— Populating Next Right Pointers in Each Node

    Populating Next Right Pointers in Each Node Given a binary tree struct TreeLinkNode { TreeLinkNode * ...

  7. LeetCode OJ 116. Populating Next Right Pointers in Each Node

    Given a binary tree struct TreeLinkNode { TreeLinkNode *left; TreeLinkNode *right; TreeLinkNode *nex ...

  8. Pointers and Dynamic Allocation of Memory

    METHOD 1: Consider the case where we do not know the number of elements in each row at compile time, ...

  9. LeetCode - Populating Next Right Pointers in Each Node II

    题目: Follow up for problem "Populating Next Right Pointers in Each Node". What if the given ...

随机推荐

  1. day 53

    目录 orm表关系如何建立 django中间件 路由层 反向解析 路由分发 名称空间 伪静态 虚拟环境 django版本的区别 视图层 orm表关系如何建立 ​ 多对多 ​ 一对多 ​ 一对一 ​ 换 ...

  2. update当根据条件不同时 更新同一个字段的方法 或多表插入

    1.通过存储过程 循环 传值 create or replace procedure p_u isbegin for rs in (select distinct (rks) from rkbz)lo ...

  3. redis的安装,以及主从实现同步

    Redis的主从复制功能非常强大,一个master可以拥有多个slave,而一个slave又可以拥有多个slave,如此下去,形成了强大的多级服务器集群架构.下面我演示下怎样在多台服务器上进行Redi ...

  4. NPOI 1.0

    1 应用组件 using NPOI.SS.UserModel; using NPOI.HSSF.Util; 2.一个简单demo    2.1 定义单元格常用到样式的枚举 public enum st ...

  5. 警告: deleting object of polymorphic class type which has non_virtual destructor

    如果基类里有虚函数,定义了基类指针指向派生类,就会需要定义基类虚析构,这样,基类指针析构的时候,就会先析构派生类,再析构基类. 在用基类指针指向派生类时, 在基类析构函数声明为virtual的时候,d ...

  6. js计算精度问题(浮点数误差,大数计算出错)

    https://github.com/camsong/blog/issues/9 0.1+0.2 //0.30000000000000004 1-0.9 //0.09999999999999998 9 ...

  7. update批量更新某一列成其它列对应的值【原】

    update批量更新某一列成其它列对应的值 postgresql 标准sql语句 update AA set name = BB.name , AA.sex = BB.sex from BB wher ...

  8. windows中将网络共享文件夹映射为网络硬盘

    目的是: 实现局域网,不同电脑之间共享文件. 例如: 计划将A电脑 的文件夹C:\MM ,共享给局域网电脑 B. 局域网所有电脑都可访问: 1. 在A电脑中 共享文件夹..选择‘启用网络发现’   ‘ ...

  9. PrintStream 类

    5.PrintStream类(重点) (1)基本概念 java.io.PrintStream类用于打印各种数据内容.   (2)常用的方法 PrintStream(OutputStream out) ...

  10. Python2.7用sys.stdout.write实现打印刷新

    如何能在控制台实现在一行中显示进度的信息呢,就像使用pip安装时的进度那样. 如果用print则会打印成多行,下面这个小技巧可以在一行中打印: import time import sys if __ ...