c语言简单科学计算器
算符优先法的思想。
#include
#include
#include
#include
#include
#define MAX_TOKEN_LEN 100 //标记最大长度
#define EXPR_INCREMENT 20//表达式长度的增量
typedef struct {
double opnd; //操作数
char optr[11]; //运算符
int flag; //若为1,则为单目运算符,2则是双目运算符
} SElemType; //栈元素类型
typedef struct SNode { //栈
SElemType date;
struct SNode * next;
} SNode, *Stack;
struct { //用来存储一个操作数或运算符
char str[MAX_TOKEN_LEN];
int type; //类型,若为0,则为操作数,若为1则为运算符
} token;
struct { //expression,用来存储表达式
char *str;
int cur; //标记读取expr的当前位置
}expr;
Stack OPND, OPTR; //操作数栈operand,运算符栈operator
int expr_size; //表达式长度
void InitStack(Stack *S) { //初始化栈
*S = (Stack)malloc(sizeof(SNode));
if(!(*S)) {
printf("动态申请内存失败!\n");
exit(0);
} //if
(*S)->next = NULL;
} //InitStack
void DestroyStack(Stack *S) { //销毁栈
SNode *p;
while(p = *S) {
*S = p->next;
free(p);
} //while
} //DestroyStack
void Push(Stack S, SElemType e) { //入栈
SNode *p;
p = (SNode *)malloc(sizeof(SNode));
if(! p) {
printf("动态申请内存失败!\n");
exit(0);
} //if
strcpy(p->date.optr,e.optr);
p->date.opnd = e.opnd;
p->date.flag = e.flag;
p->next = S->next;
S->next = p;
} //Push
void Pop(Stack S,SElemType *e) { //出栈
SNode *p;
p = S->next;
if(! p) {
printf("栈为空,不能出栈!\n");
exit(0);
} //if
S->next = p->next;
strcpy(e->optr,p->date.optr);
e->opnd = p->date.opnd;
e->flag = p->date.flag;
free(p);
} //Pop
void get_expr() { //获取expr字符串
char *p;
int size;
expr.cur = 0;
expr_size = 100;
expr.str = (char*)malloc(expr_size * sizeof(char));
if(! expr.str) {
printf("内存分配失败!\n");
exit(0);
} //if
size = 0;
p = expr.str;
while((*p = getchar()) != '\n') {
if(*p != ' ') {
if((*p >= 'A') && (*p <= 'Z')) {
*p = *p + 32; //将大写转换成小写
} //if
p++;
size++;
if(size == expr_size-1) { //此时expr.str已满,将其扩大
expr_size += EXPR_INCREMENT;
expr.str = (char*)realloc(expr.str,expr_size * sizeof(char));
if(! expr.str) {
printf("内存分配失败!\n");
exit(0);
} //if
p = &expr.str[size]; //realloc后需重新指定p
} //if
} //if
} //while
*p++ = '#';
*p = '\0';
} //get_expr
int IsOpnd(char ch) { //判断ch是否是操作数的一部分
if((ch >= '0') && (ch <= '9') || (ch == '.')) {
return 1;
} //if
if((ch == '-') || (ch == '+')) { //如若+-前面是'#'或'(',则为正负号
if((expr.cur == 0) || (expr.str[expr.cur-1] == '(')) {
return 1;
} //if
} //if
return 0;
} //IsOpnd
void gettoken() { //获取一个标记
char *p = token.str;
*p = expr.str[expr.cur];
if(IsOpnd(*p)) {
while(IsOpnd(*++p = expr.str[++expr.cur])); //注意是分号结尾
*p = '\0';
token.type = 0; //将标记类型设定为操作数
return;
} //if
if((*p >= 'a') && (*p <= 'z')) { //接收sin,tan,ln之类的函数运算符或操作数
while((expr.str[expr.cur+1] >= 'a') && (expr.str[expr.cur+1] <= 'z')) {
*++p = expr.str[++expr.cur];
} //while
} //if
++expr.cur;
*++p = '\0';
if(!strcmp(token.str,"e")) { //e为欧拉数,既自然对数的底数
sprintf(token.str,"%.16g",exp(1));//springf是库函数,功能是将显示在屏幕上的内容储存在字符串中
token.type = 0;
return;
} //if
if(!strcmp(token.str,"pi")) { //pi为圆周率
//sprintf(token.str,"%.16g",exp(1));//springf是库函数,功能是将显示在屏幕上的内容储存在字符串中
strcpy(token.str,"3.1415926535897932");
token.type = 0;
return;
} //if
token.type = 1; //将标记类型设定为运算符
} //gettoken
char Precede(SElemType *optr1, SElemType *optr2) { //返回优先关系
char *str1,*str2;
str1 = optr1->optr;
str2 = optr2->optr;
if(!strcmp(str1,"ln") || !strcmp(str1,"lg") || !strcmp(str1,"sin") || !strcmp(str1,"cos") || !strcmp(str1,"tan")) {
optr1->flag = 1; //这些均为单目运算符
return (!strcmp(str2,"(") || !strcmp(str2,"^") || !strcmp(str2,"!")) ? '';
} //if
if(!strcmp(str1,"!")) {
optr1->flag = 1;
return '>';
} //if
optr1->flag = 2;
switch(str1[0]) {
case '+': case '-': return (!strcmp(str2,"+") ||!strcmp(str2,"-") || !strcmp(str2,")") || !strcmp(str2,"#")) ? '>' : '
case '*': case '/': return (!strcmp(str2,"+") || !strcmp(str2,"-") ||!strcmp(str2,"*") ||
!strcmp(str2,"/") || !strcmp(str2,")") || !strcmp(str2,"#")) ? '>' : '
case '(': return !strcmp(str2,")") ? '=' : '
case ')': return '>';
case '^': return (!strcmp(str2,"(") || !strcmp(str2,"!") || !strcmp(str2,"^")) ? '';
case '#': return !strcmp(str2,"#") ? '=' : '
} //switch
} //Precede
long factorial(long n) { //阶乘
return (n <= 1) ? 1 : n * factorial(n-1);
} //factorial
SElemType Operate(SElemType opnd1, SElemType optr, SElemType opnd2) { //计算
SElemType temp;
if(optr.flag == 1) {
if(!strcmp(optr.optr,"!")) temp.opnd = factorial((long)opnd2.opnd);
else if(!strcmp(optr.optr,"lg")) temp.opnd = log10(opnd2.opnd);
else if(!strcmp(optr.optr,"ln")) temp.opnd = log(opnd2.opnd);
else if(!strcmp(optr.optr,"sin")) temp.opnd = sin(opnd2.opnd);
else if(!strcmp(optr.optr,"cos")) temp.opnd = cos(opnd2.opnd);
else if(!strcmp(optr.optr,"tan")) temp.opnd = tan(opnd2.opnd);
return temp;
} //if
switch(optr.optr[0]) {
case '+': temp.opnd = opnd1.opnd + opnd2.opnd; break;
case '-': temp.opnd = opnd1.opnd - opnd2.opnd; break;
case '*': temp.opnd = opnd1.opnd * opnd2.opnd; break;
case '/': temp.opnd = opnd1.opnd / opnd2.opnd; break;
case '^': temp.opnd = pow(opnd1.opnd,opnd2.opnd);
} //switch
return temp;
} //Operate
int main() {
SElemType optr,opnd1,opnd2;
printf("\n欢迎使用简易科学计算器!\n");
printf("欧拉数为e,圆周率为pi,退出则输入quit!\n");
printf("优先级:括号高于'!'高于'^'高于lg,ln,sin,cos,tan高于*,/高于+,-\n");
printf("请输入计算表达式:\n\n");
while(1) {
get_expr();
if(!strcmp(expr.str,"quit#")) {
return 0;
} //if
InitStack(&OPTR);
InitStack(&OPND);
strcpy(optr.optr,"#");
Push(OPTR, optr);
gettoken(); //从expr.str中获取一个标记(操作数或运算符)
while(strcmp(token.str,"#") || strcmp(OPTR->next->date.optr,"#")) {
if(token.type) { //说明token存储的是运算符
strcpy(optr.optr,token.str);
switch(Precede(&(OPTR->next->date),&optr)) {
case '
strcpy(optr.optr,token.str);
Push(OPTR,optr);
gettoken();
break;
case '=': //脱括号并接收下一字符
Pop(OPTR,&optr);
gettoken();
break;
case '>': //退栈并将运算结果入栈
Pop(OPTR,&optr);
Pop(OPND,&opnd2);
if(optr.flag == 2) { //是双目运算符才需另一个操作符
Pop(OPND,&opnd1);
} //if
Push(OPND,Operate(opnd1,optr,opnd2));
} //switch
} //if
else { //说明token存储的是操作数
opnd1.opnd = atof(token.str);
Push(OPND,opnd1);//atof将token.str转换为双精度数
gettoken(); //获取下一个标记
} //else
} //while
printf("%.16g\n\n",OPND->next->date.opnd);
free(expr.str);
DestroyStack(&OPTR);
DestroyStack(&OPND);
} //while
free(expr.str);
return 0;
} //main