您的位置:首页 > 编程语言 > C语言/C++

对C语言数据处理和指针的理解(二)

2010-06-08 00:00 197 查看
对C语言数据处理和指针的理解(二)
之前对C语言两个核心的方面做了不少的阐述,但是总觉得缺少点什么,想补充又感觉都是赘述,前天老师留了个作业,完成一个计算器的程序,里面用到了栈的数据结构,而这个栈很特殊,终于明白啦,缺少的就是一个具有说服力的实践程序来展现着一点。
计算器大家都知道,也就是输入一个表达式,例如:2.4*(4.5+4)+6+(-8.0)/5+6^6,输入时都是字符串,问题的关键是区分优先级,字符和数字。这些都不是今天我要说的重点,但一会儿会提供计算器程序的源代码供大家参考,也就是对今天所涉及的知识进行测试。
下面进入正题,要想完成这个程序就要用到栈,几个?两个,一个存放操作符,一个存放待操作的数据(float),但是一般情况下,结点的数据类型是自己定义的,并且是只能有一种类型的,也就是只能存放一种类型的数据,不可能把float和char作为不同的结点压入一个栈内。有的人说,我们可以编写两种栈,一种是用来存放float的,一种是用来存放char,只是做很少的改动就行啦。我同学几乎都是这么做的,这样做虽然程序的效率不会较少,但是程序所占用的空间是增大了一倍,再问一句,假如程序需要4种栈来存放4种数据类型,怎么办,做四个?运行时太浪费内存,显然这也违背了可重用代码的原则。我的意思大家肯定猜到啦,就是编写一种栈,但是可以存放任何数据类型。
我们就拿float型的操作数和char型的操作符说事。基本思想:利用指针,将指针所指向的数据的内容复制到栈空间指定的位置,这里有个关键的“中间人”,void * pdata,因为void *可以指向任何数据类型,也就是作为中转站的角色。复制几个?由你指定。假如我们要将float数据ftemp入栈,只需调用memcpy()函数(类库所提供的,可以直接调用),复制4个字节即可,该函数有三个参数:目标地址,源地址,要复制的字节数。假如要将char入栈呢?就是把目标地址和它的地址传入这个函数作为前两个参数,第三个参数是1。大家不要误解,这里是利用同一种栈,这种栈可以兼容很多数据类型,定义了两个栈变量,一个专门存放float,一个专门存放char。我们还是看程序和注释吧,这样更直观。

#include "stdlib.h"

typedef struct stack{
char * buffer; /*定义了一个指向char的指针*/
int max; /*记录栈最大空间*/
int top; /*始终指向栈顶元素*/
}Stack;

Stack * CreateStack(int max,int length) /*max是数据的个数,length是每个数据的长度*/
{
Stack * ps;

ps = (Stack *)malloc(sizeof(Stack));
if(!ps)
return 0;
ps ->buffer = (char *)malloc(length * max);/*开辟了max*length,这么大的空间,需要转化成char *类型*/
if(!ps ->buffer)
{
free(ps);
return 0;
}
ps ->top = -1; /*空栈初始值*/
ps ->max = max * length;
return ps;
}
int IsEmpty(Stack * ps)
{
return ps ->top == -1;
}
int IsFull(Stack * ps,int length)
{
return (ps ->max - ps ->top - length) / length == 0;/*不做解释,自己理解*/
}
void ClearStack(Stack * ps)
{
ps ->top = -1;
}
void DestroyStack(Stack * ps)
{
ClearStack(ps);
free(ps ->buffer);
free(ps);
}

int Push(Stack * ps,void * pdata,int length) /*void * pdata,接收要压栈数据的地址,可以接受任何指针类型,这是这个栈实现的最关键的地方,无论是float,char,还是结构体类型*/
{
if(IsFull(ps,length))
return 0;
ps ->top += length; /*先指向下一个元素的位置*/
memcpy(&ps ->buffer[ps ->top],pdata,length); /*利用memcpy函数进行数据的拷贝,如果是float,则length是4*/
return 1;
}
int Pop(Stack * ps,void * pdata,int length) /*出栈时,pdata指向的是接收出栈数据的变量的地址,不管是什么类型,我只在乎将合适长度的数据拷贝到合适的位置,然后拿来就用即可*/
{
if(IsEmpty(ps))
return 0;
memcpy(pdata,&ps ->buffer[ps ->top],length);/*将栈内的数据拷贝到了合适的位置*/
ps ->top -= length;
return 1;
}
int GetTop(Stack * ps,void * pdata,int length)
{
if(IsEmpty(ps))
return 0;
memcpy(pdata,&ps ->buffer[ps ->top],length);
return 1;
}

既然栈可以实现这样的“数据的泛化”,那么链表,队列,树等等,可以吗?答案是肯定的。
紧着着,你就可以写写这些数据结构,封装起来,制作成.lib文件,你拥有了自己的库函数,以后拿来就用!

就这些啦,然后我们看一看这个计算器程序,注:由于时间原因,我只是实现了基本的功能,sin,cos,开方,还没有写,但是为了便于扩展,将这几个操作函数作为函数指针数组呈现出来,扩展时只是将扩展的函数名假如数组即可。下面是程序,看看如何使用刚才的栈的。
计算器的基本算法:扫描整个表达式,碰见数字则入栈,碰见操作符,则与栈顶的操作符比较优先级,如果栈顶的大,则出栈,然后数据栈出两个,进行相应的运算,处于表达式中的操作符继续和栈顶的比较,如果处于表达式中的大,则入栈。直到扫描完,并且栈内无操作符为止。

#include "stack.h" /*将栈包涵进来*/
#include "stdlib.h"
#include "math.h"
#define N 5 /*预定义计算器函数*/

/*table 运算符优先级表*/
/*------------“+-*/()^#”---------------*/
int table[8][8] = {
{1,1,-1,-1,-1,1,-1,1},
{1,1,-1,-1,-1,1,-1,1},
{1,1,1,1,-1,1,-1,1},
{1,1,1,1,-1,1,-1,1},
{-1,-1,-1,-1,-1,0,-1,0},
{1,1,1,1,0,1,1,1},
{1,1,1,1,-1,1,1,1,},
{-1,-1,-1,-1,-1,-1,-1,0},
};

char * oper = "+-*/^"; /*所实现的操作函数的功能*/

float add(float,float);
float sub(float,float);
float mul(float,float);
float di(float,float);
float power(float,float);
float (* fun
)(float,float) = {add,sub,mul,di,power};
float ChangeToFloat(char ** pstart); /*将字符串转化成数字*/
int IsNum(char * pstart); /*判断是否为数字*/
int IsOper(char * pstart); /*判断是否为数字*/
int Compare(char ch1,char ch2); /*比较操作符的优先级*/
int SearchOper(char * str,char ch); /*查找是哪一个操作符,返回操作符的位置*/

main()
{
Stack * pf; /*定义指向float数据的栈*/
Stack * pc; /*定义指向char数据的栈*/
char str[25]; /*定义指向运算式的指针*/
char ch = '#'; /*初始化字符栈字符*/
char * pstart;
float ftemp1;
float ftemp2;

clrscr();
printf("Yang PengFei: Mini Calculate 1.0 Version /nCopyright(C) 2010.");
printf("/n/ninput a formula:");
gets(str);

pstart = str;
pf = CreateStack(30,4); /*创建float数据类型的栈*/
pc = CreateStack(30,1); /*创建char数据类型的栈*/
Push(pc,&ch,1); /*将'#'入栈,作为初始的工作*/

/*----------------------------初始化工作完成----------------------------------------------*/
while(*pstart != '/0')
{
if(IsNum(pstart))
{
ftemp1 = ChangeToFloat(&pstart); /*完成此函数后,此时pstart指向数字的下一个操作符*/
Push(pf,&ftemp1,4);
}
else if(IsOper(pstart))
{
if(*pstart == 40 && *(pstart + 1) == '-') /*判断出现(-13),这样类型的数据,操作数一个,操作符也是一个,要做特殊处理,即在之前将0 压入数据栈*/
{
ftemp1 = 0;
Push(pf,&ftemp1,4);
}
GetTop(pc,&ch,1); /*ch 为栈内的元素*/
if(Compare(*pstart,ch) == 1) /*栈内的操作符优先级大*/
{
Pop(pc,&ch,1);
Pop(pf,&ftemp1,4);
Pop(pf,&ftemp2,4);
ftemp1 = fun[SearchOper(oper,ch)](ftemp1,ftemp2);
Push(pf,&ftemp1,4);
}
else if(Compare(*pstart,ch) == 0)
{
Pop(pc,&ch,1);
pstart++;
}
else
{
Push(pc,pstart,1);
pstart++;
}
}
}

/*表达式已经扫描完毕,但是栈内有可能还有数据和操作符,做最后的处理*/

Pop(pc,&ch,1);
while(ch != '#' && !IsEmpty(pf))
{
Pop(pf,&ftemp1,4);
if(!Pop(pf,&ftemp2,4))
{
printf("Sorry!Maybe the string you input is error,please cheack!");
break;
}
ftemp1 = fun[SearchOper(oper,ch)](ftemp1,ftemp2);
Push(pf,&ftemp1,4);
Pop(pc,&ch,1);
}

if(ch == '#')
printf("The result is:%.2f",ftemp1);

getch();
}
float ChangeToFloat(char ** pstart)
{
float ftemp;
char a[10];
int i = 0;

while(!IsOper(*pstart) && **pstart != '/0')
{
a[i] = **pstart;
(*pstart)++;
i++;
}
a[i] = '/0';
ftemp = atof(a); /*调用的系统提供的字符串转换函数*/
return ftemp;
}
int IsNum(char * pstart)
{
if(*pstart == 46 || (*pstart >= 48 && * pstart <= 57))
return 1;
return 0;
}
int IsOper(char * pstart)
{
if(*pstart == 40 || *pstart == 41 || *pstart == 42 || *pstart == 43 || *pstart == 45 || *pstart == 47 || *pstart == 94)
return 1;
return 0;
}
int Compare(char ch1,char ch2) /*ch1为算式内的操作符,ch2为栈内操作符*/
{
char * a = "+-*/()^#";
return table[SearchOper(a,ch2)][SearchOper(a,ch1)];
}
int SearchOper(char * str,char ch)
{
int i = 0;
while(*str != ch)
{
str++;
i++;
}
return i;
}
float add(float a,float b)
{
return a + b;
}
float sub(float a,float b)
{
return b - a;
}
float mul(float a,float b)
{
return a * b;
}
float di(float a,float b)
{
return b / a;
}
float power(float a,float b)
{
return pow(b,a);
}

总结:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: