2020面向对象程序设计寒假作业1 题解

作业描述 详情
这个作业属于哪个课程 班级链接
这个作业要求在哪里 作业要求
这个作业的目标 问答题:
1-1. 如果你不了解C++请回答以下问题:你认为C语言有什么缺陷(你觉得哪里用的不顺手)。
1-2. 如果你已经了解C++请回答以下问题:你觉得C++和C语言比有什么优点。
2. 查阅相关资料,简述一下C语言/C++的编译过程。

实践题:
1. 查看自己的C++编译器版本。
2. 使用命令行编译一份C语言/C++代码。

编程题:
编写一个程序,输入满足以下语法要求的一段文字,输出运行后的结果。
变量定义:          整数 钱包 等于 零
运算(加法):    钱包 增加 四
运算(减法):    钱包 减少 四
输出:                看看 钱包
作业正文 2020面向对象程序设计寒假作业1 题解
其他参考文献 C++ 编译过程简介

问答题

  1. 因为本人是转专业进来的学生,以前奥赛也未学习C语言,所以很抱歉,无法完成比较。

  2. 查询了相关资料后,发现C/C++的编译过程分为以下步骤:(1)预处理 (2)编译 (3)汇编 (4)链接

其中,预处理阶段将先读取原程序,再处理伪指令,接着删除所有注释,然后添加行号和文件名标识,最后保留所有的 #pragma 编译器指令。

而编译过程将预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后,产生相应的汇编代码文件。

汇编过程将编译完的汇编代码文件翻译成机器指令,并生成可重定位目标程序(*.o)文件,该文件为二进制文件,字节编码是机器指令。

最后的链接过程通过链接器将一个个目标文件(*.obj)与库文件(*.lib)链接在一起生成一个完整的可执行程序。

因为正常而言,一个比较完备、大型的可执行程序,其代码量较多,很难将所有的代码写在一个 cpp 内,或者写在一些头文件内。一是在于这么写很容易产生命名上的冲突以及宏定义上的冲突,而且加大了代码的冗余量;二则在于如此大量的代码,编译速度极慢,当程序出现错误,需要 debug 时,将会耗费大量的时间在编译上,降低了 debug 的效率;三则这样的写法不利于模块化处理程序,当程序需求需要修改时,需要耗费极大量的时间在修改代码上。

实践题

1.查看自己的C++编译器版本

Windows+R 打开运行窗口

输入 cmd 进入命令行

输入 g++ -v 查看版本

2.使用命令行编译一份 C++ 代码

Windows+R 打开运行窗口

输入 cmd 进入命令行

使用 cd 指令进入代码所在的文件夹

使用指令 g++ {file}.cpp 编译文件,生成 a.exe 文件

编程题

前言:又遇到了最讨厌的模拟题(码力低是真的伤不起啊),幸好最后被我调出来了......

预处理

首先,作为设计人员,很重要的是要清楚用户可能输入什么。而对于非法的输入一定要学会处理,而不是任由程序崩溃

所以,我先想到的就是设计错误抛出:

inline void error_output(){//错误抛出
    puts("输入格式错误");
}

其次,题目保证了输入的代表数字的汉字一定为零-十,关键字保证只有以下五类:
整数:定义新变量
等于:对变量赋值

整数...等于...:定义新变量并赋初值

增加:实现加法
减少:实现减法
看看:输出变量值

于是,我们先把他们放到map中,初始化,方便后面处理:

vector<int> var_ar;
map<string,int> read_gbk,comd,var_id;
map<int,string> output_gbk;
/*
var_ar变量值
var_id从变量名到变量地址的映射
read_gbk将汉字转为数字
output_gbk将数字转成汉字
*/

inline void pre(){//初始化设置
    read_gbk["零"]=0;
    read_gbk["一"]=1;
    read_gbk["二"]=2;
    read_gbk["三"]=3;
    read_gbk["四"]=4;
    read_gbk["五"]=5;
    read_gbk["六"]=6;
    read_gbk["七"]=7;
    read_gbk["八"]=8;
    read_gbk["九"]=9;
    read_gbk["十"]=10;

    output_gbk[0]="零";
    output_gbk[1]="一";
    output_gbk[2]="二";
    output_gbk[3]="三";
    output_gbk[4]="四";
    output_gbk[5]="五";
    output_gbk[6]="六";
    output_gbk[7]="七";
    output_gbk[8]="八";
    output_gbk[9]="九";
    output_gbk[10]="十";

    comd["增加"]=1;
    comd["减少"]=-1;
    comd["等于"]=2;
    comd["看看"]=3;
    comd["整数"]=4;
}

由于变量个数未知,我使用了vector来储存变量的值,开一个map作为变量名到它在vector中地址的映射

主体

好的,预处理部分结束,我们来讲主体部分

主体功能较简单,无非以下工作:
输入语句、分析与执行语句、输出

由于语句条数未知,本人使用 while 循环来执行:

    while( getline(cin,s) ) carry(s);//读入、分析并执行指令

其中 carry(string) 函数为分析与执行语句的主体

carry(string) 函数中包括了对输入的语句进行分析

首先,合法的语句一定是含有空格的,我们把语句按空格分成前半部分和后半部分;否则记得错误的抛出

前半部分如果不是指令,则是已经申请的变量,否则也是需要抛出的错误

而这样一来,后面跟着的一定是 加、减、赋值的语句+空格+值 的形式,而值可能是已申请的变量或者数字对应的汉字

一样先分析是否含有空格,否则抛出;然后读取前半部分是否是加、减、赋值的语句,否则抛出;接着判断是否最后那个是值,否则抛出;最后进行相应的运算

当然,判断前半部分是否是那三种指令之前,更要先判断是否是合法指令。如果连合法指令都不算,就更不要说那三种指令了。

实现起来代码如下:

if( !iscomd(tmp) ){//不是指令作为开头
        if( var_id.find(tmp)==var_id.end() ){//也不是变量(例如数字),或变量还未申请,语句非法
            error_output();
            return ;
        }

        id=var_id[tmp];//变量的地址
        if( s.find(" ")==string::npos ){//不含空格,语句非法
            error_output();
            return ;
        }

        tmp=s.substr(0, s.find(" ") );
        s=s.substr( s.find(" ")+1 );

        if( !iscomd(tmp) ){//不是指令,语句非法
            error_output();
            return ;
        }
        
        int command=comd[tmp];
        if(command!=-1&&command!=1&&command!=2){//不是加减或者赋值,语句非法
            error_output();
            return ;
        }

        int num=number(s);
        if(num<0){//语句非法
            error_output();
            return ;
        }
        if(command==-1) var_ar[id]-=num;
        else if(command==1) var_ar[id]+=num;
        else if(command==2) var_ar[id]=num;
    }

而判断是否是值,我用一个函数 number 进行了模块化处理:有限判定是否是数字对应的汉字;如果不是,再判定是否是已申请的变量;最后返回的如果是非负数,则代表值

inline int number(string s){//读取用于运算的数字或变量
    int num=gbk_read(s);
    if(num>=0) return num;//先判定是否是数字
    if( var_id.find(s)!=var_id.end() ) return var_ar[ var_id[s] ];//再判定是否为变量
    return -1;//否则非法
}

而将数字对应的汉字转化为数字,我将它们最后全部整合成了两个字的形式,再处理。整合分为以下几种情况:

  1. 零-九:前补零
  2. 十:前补一
  3. 十一-十九:不变
  4. 十的整数倍:不变
  5. 其它:去掉中间的十

当然,不保证用户的输入一定合法,所以考虑了以下几种情况:

  1. 三个汉字但中间不为十,或首尾为十:非法
  2. 含前导零非一个汉字的数字:去掉所有前导零后,按1处理
  3. 一十一-一十九:同5
  4. 一一-九九:不变
  5. 一零、二零、三零...九零:不变

整理以下上述情况,就是:先去除非一个汉字的前导零,若是一个汉字,则前补零;三个汉字则依次执行情况6与5;剩下两个汉字的情况分为以下几类:

1.仅有十开头的形式
2.仅有十结尾的形式
3.十开头结尾的形式
4.其它形式

对于3需要错误抛出,4可以直接处理

为了高效地处理1与2,本人在计算第一位后,先判定是否为十,如果不为十,在乘十实现十进制下的左移;而最后一位加上对应数字对10取模即可解决2

实现代码如下:

inline int gbk_read(string s){
    // return -1 means the input string is illegal
    int num=-1;
    string tmp;
    while( s.substr(0,2)=="零"&&s.size()>2 ) s=s.substr(2);
    if( s.size()==2 ){
        if( !islegal(s) ) return -1;
        if(read_gbk[s]<10) s="零"+s;
        else s="一"+s;
    }//一位数和十,均凑成两位数
    if( s.size()==6 ){
        tmp=s.substr(2,2);
        if( !islegal(tmp) ) return -1;
        if( read_gbk[tmp]!=10 ) return -1;//查询第二个字是否为十,否则非法
        if( read_gbk[s.substr(0,2)]==10||read_gbk[s.substr(4,2)]==10 ) return -1;//查询一三是否为十,是则非法
        s=s.substr(0,2)+s.substr(4,2);//去掉中间,拼成两位数
    }
    if( s.size()==4 ){
        tmp=s.substr(0,2);
        if( !islegal(tmp) ) return -1;
        num=read_gbk[tmp];
        tmp=s.substr(2,2);
        if( !islegal(tmp) ) return -1;
        if(num<10) num*=10;//十* 的形式
        else if(tmp=="十") return -1;
        num+=read_gbk[tmp]%10;//*十 的形式
    }
    return num;
}

至此非指令开头情况已解释完毕。下文解释指令开头的情况:

指令作为开头则一定为定义变量(并申请初值)或输出的形式,否则都是需要抛出的

输出则一定是输出值,否则需要抛出。如果是的话,用一个函数专门负责将数字转化成汉字。转化分为以下情况:

  1. 0-9
  2. 10
  3. 11-19
  4. 10的其它整数倍
  5. 其它数字

总结起来,针对两位数,当十位大于1时需要输出十位数,再输出十,若个位不为零则还要输出各位

针对一位数,直接用map输出即可

本人用 gbk_output(int) 封装了以下,实现过程如下:

inline string gbk_output(int num){//数字转汉字(只输出0-99)
    string tmp="";
    if(num/10){
        if( (num/10)>1 ) tmp+=output_gbk[num/10];//不输出 一十* 的形式
        tmp+=output_gbk[10];
        if(num%10) tmp+=output_gbk[num%10];//*十的形式,不输出为 *十零 的形式
    }
    else tmp+=output_gbk[num%10];
    return tmp;
}

而如果是定义变量,则后面的语句先判定是否含有空格,若含有,则分为前半段与后半段;不含有,则干脆全部作为前半段,后半段放空

考虑前半段虽然保证不是关键字,但仍可能申请到冲突的变量名:例如数字或者已申请的变量

但考虑到上文所提,若是数字或已申请的变量,可以用 number(string) 函数取出值,所以依此判断即可

定义语句可能后跟赋值语句,思维跟上述相同,只不过地址要直接在上述步骤中保存好即可

实现过程如下:

else{//指令开头
        int command=comd[tmp];
        if(command==-1||command==1||command==2){//加减或赋值开头,语句非法
            error_output();
            return ;
        }
        if(command==3){
            int num=number(s);
            if(num<0){//输出未申请的变量,语句非法
                error_output();
                return ;
            }
            cout<<gbk_output(num)<<endl;
        }
        else{
            if( s.find(" ")==string::npos ){
                tmp=s;
                s="";
            }
            else{
                tmp=s.substr(0, s.find(" ") );
                s=s.substr( s.find(" ")+1  );
            }

            if( number(tmp)>=0 ){//变量已申请或者是数字对应的汉字,语句非法
                error_output();
                return ;
            }
            int id=var_id[tmp]=var_ar.size();
            var_ar.push_back(0);

            if(s=="") return ;//不赋初值,直接跳出

            if( s.find(" ")==string::npos ){//语句非法
                error_output();
                return ;
            }
            tmp=s.substr(0, s.find(" ") );
            s=s.substr( s.find(" ")+1 );

            if( iscomd(tmp)&&comd[tmp]==2 ){//赋初值
                int num=number(s);
                if(num>=0) var_ar[id]=num;
                else error_output();
            }
            else error_output();//语句非法
        }
    }

完整代码

#include<cstdio>
#include<string>
#include<iostream>
#include<map>
#include<vector>
using namespace std;

vector<int> var_ar;
map<string,int> read_gbk,comd,var_id;
map<int,string> output_gbk;
/*
var_ar变量值
var_id从变量名到变量地址的映射
read_gbk将汉字转为数字
output_gbk将数字转成汉字
*/

inline void pre(){//初始化设置
    read_gbk["零"]=0;
    read_gbk["一"]=1;
    read_gbk["二"]=2;
    read_gbk["三"]=3;
    read_gbk["四"]=4;
    read_gbk["五"]=5;
    read_gbk["六"]=6;
    read_gbk["七"]=7;
    read_gbk["八"]=8;
    read_gbk["九"]=9;
    read_gbk["十"]=10;

    output_gbk[0]="零";
    output_gbk[1]="一";
    output_gbk[2]="二";
    output_gbk[3]="三";
    output_gbk[4]="四";
    output_gbk[5]="五";
    output_gbk[6]="六";
    output_gbk[7]="七";
    output_gbk[8]="八";
    output_gbk[9]="九";
    output_gbk[10]="十";

    comd["增加"]=1;
    comd["减少"]=-1;
    comd["等于"]=2;
    comd["看看"]=3;
    comd["整数"]=4;
}

inline void error_output(){//错误抛出
    puts("输入格式错误");
}
inline string gbk_output(int num){//数字转汉字(只输出0-99)
    string tmp="";
    if(num/10){
        if( (num/10)>1 ) tmp+=output_gbk[num/10];//不输出 一十* 的形式
        tmp+=output_gbk[10];
        if(num%10) tmp+=output_gbk[num%10];//*十的形式,不输出为 *十零 的形式
    }
    else tmp+=output_gbk[num%10];
    return tmp;
}

inline bool islegal(string s){ return read_gbk.find(s)!=read_gbk.end(); }//查询读入是否为数字对应的汉字
inline int gbk_read(string s){
    // return -1 means the input string is illegal
    int num=-1;
    string tmp;
    if( s.size()==2 ){
        if( !islegal(s) ) return -1;
        if(read_gbk[s]<10) s="零"+s;
        else s="一"+s;
    }//一位数和十,均凑成两位数
    if( s.size()==6 ){
        tmp=s.substr(2,2);
        if( !islegal(tmp) ) return -1;
        if( read_gbk[tmp]!=10 ) return -1;//查询第二个字是否为十,否则非法
        if( read_gbk[s.substr(0,2)]==10||read_gbk[s.substr(4,2)]==10 ) return -1;//查询一三是否为十,是则非法
        s=s.substr(0,2)+s.substr(4,2);//去掉中间,拼成两位数
    }
    if( s.size()==4 ){
        tmp=s.substr(0,2);
        if( !islegal(tmp) ) return -1;
        num=read_gbk[tmp];
        tmp=s.substr(2,2);
        if( !islegal(tmp) ) return -1;
        if(num<10) num*=10;//十* 的形式
        else if(tmp=="十") return -1;
        num+=read_gbk[tmp]%10;//*十 的形式
    }
    return num;
}

inline int number(string s){//读取用于运算的数字或变量
    int num=gbk_read(s);
    if(num>=0) return num;//先判定是否是数字
    if( var_id.find(s)!=var_id.end() ) return var_ar[ var_id[s] ];//再判定是否为变量
    return -1;//否则非法
}

inline bool iscomd(string s) { return comd.find(s)!=comd.end(); }//判定是否为合法指令
inline void carry(string s){
    string tmp="";
    int id;
    if( s.find(" ")==string::npos ){//不含空格,语句非法
        error_output();
        return ;
    }

    tmp=s.substr(0, s.find(" ") );
    s=s.substr( s.find(" ")+1 );

    if( !iscomd(tmp) ){//不是指令作为开头
        if( var_id.find(tmp)==var_id.end() ){//也不是变量(例如数字),或变量还未申请,语句非法
            error_output();
            return ;
        }

        id=var_id[tmp];//变量的地址
        if( s.find(" ")==string::npos ){//不含空格,语句非法
            error_output();
            return ;
        }

        tmp=s.substr(0, s.find(" ") );
        s=s.substr( s.find(" ")+1 );

        if( !iscomd(tmp) ){//不是指令,语句非法
            error_output();
            return ;
        }
        
        int command=comd[tmp];
        if(command!=-1&&command!=1&&command!=2){//不是加减或者赋值,语句非法
            error_output();
            return ;
        }

        int num=number(s);
        if(num<0){//语句非法
            error_output();
            return ;
        }
        if(command==-1) var_ar[id]-=num;
        else if(command==1) var_ar[id]+=num;
        else if(command==2) var_ar[id]=num;
    }
    else{//指令开头
        int command=comd[tmp];
        if(command==-1||command==1||command==2){//加减或赋值开头,语句非法
            error_output();
            return ;
        }
        if(command==3){
            int num=number(s);
            if(num<0){//输出未申请的变量,语句非法
                error_output();
                return ;
            }
            cout<<gbk_output(num)<<endl;
        }
        else{
            if( s.find(" ")==string::npos ){
                tmp=s;
                s="";
            }
            else{
                tmp=s.substr(0, s.find(" ") );
                s=s.substr( s.find(" ")+1  );
            }

            if( number(tmp)>=0 ){//变量已申请或者是数字对应的汉字,语句非法
                error_output();
                return ;
            }
            int id=var_id[tmp]=var_ar.size();
            var_ar.push_back(0);

            if(s=="") return ;//不赋初值,直接跳出

            if( s.find(" ")==string::npos ){//语句非法
                error_output();
                return ;
            }
            tmp=s.substr(0, s.find(" ") );
            s=s.substr( s.find(" ")+1 );

            if( iscomd(tmp)&&comd[tmp]==2 ){//赋初值
                int num=number(s);
                if(num>=0) var_ar[id]=num;
                else error_output();
            }
            else error_output();//语句非法
        }
    }
}

int main(){
    pre();
    string s;
    while( getline(cin,s) ) carry(s);//读入、分析并执行指令
    return 0;
}
原文地址:https://www.cnblogs.com/JustinRochester/p/12217654.html