C语言实现简易计算器(可作加减乘除)
C语言实现简易计算器(加减乘除)
计算器作为课设项目,已完成答辩,先将代码和思路(注释中)上传一篇博客
已增添、修改、整理至无错且可正常运行
虽使用了栈,但初学者可在初步了解栈和结构语法后理解代码
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define IsDouble 0
#define IsChar 1
//_______________________________________________________________________________________________________________________________________________________
//1.支持浮点数和字符的栈
typedef struct {
char * buffer;
int typesize;
int top;
int max;
} stack;
stack * CreateStack(int max, int typesize);//创建一个栈
void DestroyStack(stack *);//释放栈的空间
void ClearStack(stack *);//清空栈
int Push(stack *, void *);//入栈
int Pop(stack *, void *);//出栈
int GetSize(stack *s);//得到栈的大小
int GetTop(stack *, void *);//找到栈顶
int IsEmpty(stack *);//判断是否为空栈,空则下溢
int IsFull(stack *);//判断栈是否已满 ,满则溢出
stack * CreateStack(int max, int typesize){
stack * s = (stack*)malloc(sizeof(stack));//为栈s malloc内存
if (!s) return 0;
//为结构中buffer元素malloc内存
s->buffer = (char *)malloc(sizeof(char) * max * typesize);
if (!s->buffer) return 0;
//初始化结构中的栈顶,最大值,类型大小
s->top = -1;
s->max = max;
s->typesize = typesize;
return s;
}
void DestroyStack(stack* s){
free(s->buffer);//先释放buffer的空间
free(s);//在释放s的空间
}
void ClearStack(stack * s){
s->top = -1;//清空栈(栈头位置归零)
}
int Push(stack * s, void * data){
if (IsFull(s)) return 0;//如果栈已满则return 0,防止溢出
//栈未满则将栈头移动打动下一位置,并将data中的元素拷入栈中buffer的第top位置
s->top++;
memcpy(s->buffer + s->top*s->typesize, data, s->typesize);
//入栈成功return 1
return 1;
}
int Pop(stack * s, void * data){
if (IsEmpty(s)) return 0;//出栈判断栈是否为空,若为空则return 0
//栈未空则将buffer中top位置的字符拷入data记录,并让栈头向前移动一个位置
memcpy(data, s->buffer + s->top*s->typesize, s->typesize);
s->top--;
//成功则return 1
return 1;
}
int GetSize(stack *s){
return s -> top+1;//栈头位置+1得到大小
}
int GetTop(stack *s, void * data){
if (IsEmpty(s)) return 0;//如果栈空return 0
//栈不为空则将top位置的字符拷回data记录,得到栈头
memcpy(data, s->buffer + s->top*s->typesize, s->typesize);
//成功则return 1;
return 1;
}
int IsEmpty(stack * s){
return s->top == -1;//如果top为-1则栈空
}
int IsFull(stack * s){
return s->top == s->max-1;//如果top为max-1则栈满
}
//___________________________________________________________________________________________________________________________________________________
//2.定义一个cal类型,其中data存数时sign为IsDouble,存字符时,sign为Ischar
typedef struct {
double data;
char sign;
} cal;
//3.查找对应符号(找到则返回该符号下标)(找不到则说明该部分为数字返回-1)
int SearchCode(char ch){
char * code = "+-*/()@";//@为终止符,算式输入结束
int index = 0;//
while (code[index]){
if (code[index] == ch) return index;
index++;
}
return -1;
}
//4.得到两个符号间的优先级
//与SearchCode相对应,
char GetPriority(char ch, char next){
//创建一个perferen表,第i行(列)对应SearchCode函数中code中的第i个字符
char perferen[7][7] = {
">><<<>>",
">><<<>>",
">>>><>>",
">>>><>>",
"<<<<<=E",
">>>>E>>",
"<<<<<E="
};
//找到两个形参对应的字符
int c = SearchCode(ch);
int n = SearchCode(next);
//如果找不到对应运算符(字符不是运算符而是为数字)return E
if (c==-1 || n==-1) return 'E';
//如果找到两个对应运算符则按照优先级表返回两个运算符的优先级
return perferen[c][n];
}
//5.四则运算
double add(double a, double b) { return a+b; }
double sub(double a, double b) { return a-b; }
double mul(double a, double b) { return a*b; }
double ddiv(double a, double b) { return a/b; }
//整合四种运算
double calcu(double a, char ch, double b){
double (*calculation[4])(double,double) = {add,sub,mul,ddiv};
return calculation[SearchCode(ch)](a,b);
}
//6.检测字符串
int CheckStr(char * buffer){
int n;
//遍历字符串确保算式中无非法字符若检测到非法字符return 0,若都合法则return 1
for (n = 0;buffer[n];n++){
if ((SearchCode(buffer[n]) != -1 || buffer[n] == '.' || (buffer[n] >= '0' && buffer[n] <= '9')) && buffer[n] != '@') continue;
else return 0;
}
buffer[n] = '@';//加上终止符,表示算式结束
buffer[n+1] = '\0';
return 1;
}
//7.得到数据转化为double类型存入rs
int GetDigit(char * buffer, int * n, double * rs){
char str[30];
int i,j = 0;
for (i = 0;SearchCode(buffer[*n]) == -1;i++){
str[i] = buffer[*n];//从*n位置开始,将这一串数字字符存入str
(*n)++;
}
str[i] = '\0';
for (i = 0;str[i];i++){
if (str[i] == '.') j++;
}
//如果一段小数有多个小数点或小数点在数字首尾,return 0
if (j>1 || str[i-1] == '.' || str[0] == '.') return 0;
//rs接收转化为double的数据
*rs = atof(str);
//操作成功return 1
return 1;
}
//8.将用户输入的buffer字符串转化为可供程序运算的calstr数组
int resolu(char * buffer, cal * calstr){
int i = 0, j = 0;
cal c;
while (buffer[i]){
if (SearchCode(buffer[i]) == -1){
//如果得到数据不成功则return 0
if (GetDigit(buffer,&i, &c.data) == 0) return 0;
//如果成功得到数据则在c.sign标记为浮点数
c.sign = IsDouble;
//将c存入数组calstr中
calstr[j++] = c;
}
else{
//若符号为运算符
//判断正负号
if (buffer[i] == '-' && (buffer[i-1] == '('||buffer[i-1] == '+'||buffer[i-1] == '-'||buffer[i-1] == '*'||buffer[i-1] == '/') || (i==0 && buffer[0] == '-')){
i++;
if (GetDigit(buffer,&i, &c.data) == 0) return 0;//在符号的下一位开始查找,若找不到数字return 0
//否则,给数字取相反数,c.sign标记为浮点数,存入calstr中
c.data = 0 - c.data;
c.sign = IsDouble;
calstr[j++] = c;
} else
//如果是正号,与符号处理方式同理
if (buffer[i] == '+' && (buffer[i-1] == '('||buffer[i-1] == '+'||buffer[i-1] == '-'||buffer[i-1] == '*'||buffer[i-1] == '/') || (i==0 && buffer[0] == '+')){
i++;
if (GetDigit(buffer, &i, &c.data) == 0) return 0;
c.sign = IsDouble;
calstr[j++] = c;
}
else{
//如果不是正负号,则为运算符,先强制转换为double类型存在c.data里,然后c.sign标记为char类型,存入calstr
c.data = (double)buffer[i++];
c.sign = IsChar;
calstr[j++] = c;
}
}
}
//操作蔡成功则return 1
return 1;
}
//9.计算出结果
int result(cal * calstr, double * rs){
stack * pst = CreateStack(100,sizeof(char));//运算符栈
stack * pnd = CreateStack(100,sizeof(double));//数据栈
double num1,num2;
int n = 0;
char ch = '@';
Push(pst, &ch);
//在转换得到的calstr中遍历直到终止符'@"
while(ch != '@' || !(calstr[n].sign == IsChar && (char)calstr[n].data == '@')){
//如果calstr的n位上是浮点数,则将这个data压栈进入数据栈pnd中
if (calstr[n].sign == IsDouble){
Push(pnd, &(calstr[n].data));
n++;
}
//反之,如果是运算符,则要检测优先级
else{
switch( GetPriority(ch, (char)calstr[n].data)){
//如果运算符优先级较小,则让ch等于优先级大的符号并压入符号栈pst中
case '<':
ch = (char)calstr[n].data;
Push(pst, &ch);
n++;
break;
//如果结果为等号,让符号出栈暂存到ch中
case '=':
if (!Pop(pst, &ch)) return 0;
n++;
break;
//如果ch优先级较高,则将前两个数字及运算符出栈,分别储存至num2,ch,num1中,进行运算,得到的结果再次压栈进入pnd中
case '>':
if (!(Pop(pnd,&num2) && Pop(pst,&ch) && Pop(pnd,&num1))) return 0;
num1 = calcu(num1,ch,num2);
Push(pnd, &num1);
break;
//如果符号顺序出错,return 0
case 'E':
return 0;
}
}
//检测是否可以得到栈顶符号,栈空则return 0
if (!GetTop(pst, &ch)) return 0;
}
//如果栈中得到了最终结果,并且取出pnd中的最终结果到rs,return 1
if (GetSize(pnd) == 1 && GetTop(pnd,rs)){
DestroyStack(pst);
DestroyStack(pnd);
return 1;
}
//否则 return 0
else{
return 0;
}
}
//10.用户交互函数
void treatment()
{
char buffer[100];//用户输入的字符串(算式)
cal calstr[50];//计算用的数组
double rs = 0;//计算结果
printf("Enter your equation:");
gets(buffer);//让用户输入算式buffer
//用户不输入"exit"就不退出
while (!(buffer[0]=='e' && buffer[1]=='x' && buffer[2]=='i' && buffer[3]=='t')){
//检查buffer中字符君合法,成功将buffer转化为用于计算的calstr数组,成功计算出结果存入rs
if (CheckStr(buffer) && resolu(buffer,calstr) && result(calstr,&rs)){
printf("\n%lf\n",rs);
}else{
printf("\nError!\n");
}
printf("Enter \"exit\"to quit");
printf("\nEnter your equation:");
gets(buffer);//再次让用户输入算式
}
printf("\nbye\n");
}
//11.主函数
int main(){
treatment();
}
参考文献链接如下
[参考文献](c语言强化训练——简易计算器 - 石莹 - 博客园 (cnblogs.com))
C语言实现简易计算器(可作加减乘除)的更多相关文章
- 大一C语言学习笔记(10)---编程篇--制作简易计算器,支持加,减,乘,除,取余运算,要求 0 bug
博主自开学初就一直在努力为自己的未来寻找学习方向,学习编程嘛,尽量还是要抱大腿的,所以我就加入了我们学校的智能设备研究所,别的不说,那的学长们看起来是真的很靠谱,学长们的学习氛围也超级浓厚,所以我就打 ...
- 剖析简易计算器带你入门微信小程序开发
写在前面,但是重点在后面 这是教程,也不是教程. 可以先看Demo的操作动图,看看是个什么玩意儿,GitHub地址(https://github.com/dunizb/wxapp-sCalc) 自从微 ...
- Python之实现一个简易计算器
自己动手写计算器 一.功能分析 用户输入一个类似这样 3*( 4+ 50 )-(( 100 + 40 )*5/2- 3*2* 2/4+9)*((( 3 + 4)-4)-4) 这样的表达式,假设表达式里 ...
- JavaScript简易计算器
JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标 ...
- 微信小程序-简易计算器
代码地址如下:http://www.demodashi.com/demo/14210.html 一.前期准备工作 软件环境:微信开发者工具 官方下载地址:https://mp.weixin.qq.co ...
- 制作一个简易计算器——基于Android Studio实现
一个计算器Android程序的源码部分分为主干和细节两部分. 一.主干 1. 主干的构成 计算器的布局 事件(即计算器上的按钮.文本框)监听 实现计算 2. 详细解释 假设我们的项目名为Calcula ...
- Qt、C++ 简易计算器
Qt.C++实现简易计算器: 以下内容是我实现这个简易计算器整个过程,其中包括我对如何实现这个功能的思考.中途遇到的问题.走过的弯路 整个实现从易到难,计算器功能从简单到复杂,最开始设计的整个实现步骤 ...
- C语言实现简单计算器小项目
昨天刚安装上devc++,半夜想着练练C语言吧 于是就看到实验楼有一个计算器的项目 之前做过一次,这次写的主要是思路 首先我们先从原理思考jia,实现简单的计算器就要具备加减乘除这些,看普通的计算器也 ...
- C语言之简易了解程序环境
C语言之简易了解程序环境 大纲: 程序的翻译环境 预编译 编译 汇编 链接 程序的运行环境 在ANSI C的任何一种实现中,存在两个不同的环境. 第1种是翻译环境,在这个环境中源代码被转换为可执行的机 ...
随机推荐
- js之window对象(慕课网学习笔记)
javaScript定义了一个变量一个函数都会变成window中的一个成员 var a=1; alert(window.a) //会输出a的值 window基础 创建窗口.调整窗口.移动窗口.关闭窗口 ...
- 刷题-力扣-1137. 第 N 个泰波那契数
1137. 第 N 个泰波那契数 题目链接 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/n-th-tribonacci-number 著作权 ...
- 用vue的抽象组件来做一个防止img标签url为空或url地址出错的验证
看了网上文章学习了下vue的抽象组件,感觉就跟react的高阶组件一样的使用场景,只是更加面向vue的底层编程 ,网上介绍的抽象组件一般有2种用法,1 用来加防抖和节流 2 用来控制按钮是否允许点击做 ...
- Shell脚本逐行读取文本内容并拆分,根据条件筛选文件
时间:2018-11-13 整理:byzqy 需求: 最近帮朋友写了一段脚本,他的需求是根据一份产品清单,去服务器上捞取对应产品编号的测试Log,数量大概有9000~10000条左右.文本内容大致如下 ...
- 在已有Win7/10系统电脑中加装Ubuntu18.04(安装双系统)
准备一台有Win7/10的电脑. 为Ubuntu预留硬盘空间.最好在硬盘最后保留一个空闲分区.(Ubuntu会自动安装到后面的空闲分区) 用Universal USB Installer制作安装盘(U ...
- Mybatis源码解析3——核心类SqlSessionFactory,看完我悟了
这是昨晚的武汉,晚上九点钟拍的,疫情又一次来袭,曾经熙熙攘攘的夜市也变得冷冷清清,但比前几周要好很多了.希望大家都能保护好自己,保护好身边的人,生活不可能像你想象的那么好,但也不会像你想象的那么糟. ...
- CSS3 animaion 和 transition 比较
animation是CSS3的动画属性,可以设置以下六种属性. transition是CSS3的过度属性,可以设置以下四种属性. 从属性上分析,animation可以设定循环次数. 其次,两者的触发条 ...
- VUE004. provide与inject的使用(祖先组件隔多层传静态值给子孙组件)
provide和inject可以通过祖先组件隔三层四层甚至隔着九层妖塔传值给子孙组件. 需要注意的是这样的传值方式是非响应式的,需要结合自身的应用场景,比如将上传的限制条件通过父组件传值给子组件的子组 ...
- oh,我的老伙计,你看看这近五十个dapr视频
oh,我的老伙计,你看看这近五十个 dapr 视频.这不就是你想要的视频资料吗?快来捡走吧! 开始了,但是没完全开始 Dapr 是一个可移植的.事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的. ...
- 使用vsCode开发vue项目格式化通用配置
{ "editor.tabSize": 2, "editor.fontSize": 18, "editor.wordWrap": ...