彻底理解指针第三篇

来源:互联网 发布:golang程序员工资 编辑:程序博客网 时间:2024/05/17 16:55

今天主要看了函数指针和一个递归下降语法分析程序。感觉收益剖多。 函数本身不是变量,函数名代表函数的首地址,可以用函数指针指向,初始化函数指针要小心,不要加括弧。具体请详细看下面的代码。此题目:对输入的文本进行快速排序,如果指定参数-n,则可以进行数值排序,否则默认是字典排序。

#include <stdio.h>#include <stdlib.h>#include <string.h>#define MAXLINE 1000#define MAXLEN 500char* line[MAXLINE];//进行数组拷贝void copy(char *s,char *t){for(;(*s=*t)!='\0';s++,t++);}//读取一行 int getLine(char *s,int maxSize){int i;char c;for(i=0;(i<maxSize-1) && ((c=getchar())!=EOF) && c!='\n';i++)s[i]=c;if(c=='\n'){s[i]='\n';i++;}s[i]='\0';return i;}//读取所有文本行int readLines(char **line,int maxSize){char *p;char storelines[MAXLEN];int length;int countlines=0;while((length=getLine(storelines,maxSize))>0){p=(char *)malloc(sizeof(char)*(length+1));storelines[length-1]='\0';copy(p,storelines);printf("%d\n",length);line[countlines++]=p;}return countlines;}//交换指针void swap(void **v,int i,int j){void *temp;temp=v[i];v[i]=v[j];v[j]=temp;}//对指针进行排序//参数void**,可以排序任何类型void psort(void **v,int left,int right,int (*comp)(void*,void*)){int i,last;if(left>=right)return;swap(v,left,(left+right)/2);last=left;for(i=left+1;i<=right;i++)if((*comp)(v[i],v[left])<0)swap(v,++last,i);swap(v,left,last);psort(v,left,last-1,comp);psort(v,last+1,right,comp);}//打印void writeLines(char **line,int lines){int i;for(i=0;i<lines;i++)printf("%s\n",line[i]);}//按数值顺序比较字符串//atof()把字符串转换成浮点数// atof()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,而再遇到非数字或字符串结束时('\0')才结束转换,并将结果返回int numcmp(char *s1,char *s2){double v1,v2;v1=atof(s1);v2=atof(s2);if(v1<v2)return -1;else if(v1>v2)return 1;elsereturn 0;}int main(int argc,char *argv[]){int lines;int numeric=0;if(argc>1 && strcmp(argv[1],"-n")==0)numeric=1;if((lines=readLines(line,MAXLEN))>0)if(lines<=MAXLINE){psort((void**)line,0,lines-1,(int (*)(void*,void*))(numeric?numcmp:strcmp));writeLines(line,lines);}elseprintf("too many lines\n");return 0;}

接下来是一个将C语言的声明转化为文字描述。也就是所谓的递归下降语法分析器,因

为程序中相互递归调用。

C语言语法形式:

dcl:     前面带有可选的*的dir-dcl

dir-dcl: name

           (dcl)

           dir-dcl()

          dir-dcl[可选长度]

写程序之前首先要搞懂程序的逻辑功能,对程序的输入,执行,输出有彻底的理解,才

能进行编写程序。举个小例子才可以彻底的弄明白。

这个程序的逻辑很简单,但是实现却相当的复杂。

1.读取数据类型

2.调用dcl函数处理输入的剩余部分。

3.不断的调用gettoken函数,读取下一个记号,进行判断。

4.根据判定结果显示相应的字符串。

整个程序只要理解3点就很简单:

1.gettoken()函数是读取下一个记号。每次调用它,都会读取token和tokentype。

2.getch和ungetch,这是确保输入的完整性函数,利用一个共享数组来实现

3.对于dcl和dir-dcl,两个函数的递归调用,这个根据语法来理解。

具体实现如下:不得不佩服这个程序写的真是太棒了。真不愧是C语言发明者。

/*递归语法定义,递归下降语法分析程序*//*将C语言的声明转化为文字描述*/#include <stdio.h>#include <string.h>#include <ctype.h>#define MAXTOKEN 100#define BUFSIZE 100enum{NAME,PARENS,BRACKETS};void dcl(void);void dirdcl(void);char buf[BUFSIZE];//用于xieungetch函数的缓冲区int bufp=0;//buf中下一个空间的位置int gettoken(void);int tokentype;//下一个记号的类型char token[MAXTOKEN];//下一个记号字符串char name[MAXTOKEN];//标志符名char datatype[MAXTOKEN];//数据类型为char int等char out[1000];//输出串//调用xiegetch和xieungetch,用来确定程序它已经读入的输入是否足够//读取下一个待处理的字符//这样,此后在调用xiegetch函数时,在读入新的输入之前先返回ungetch函数放回的那个字符int xiegetch(void){    return (bufp > 0) ? buf[--bufp] : getchar();}//xieungetch函数则用于把字符放回到输入中//xieungetch函数把要压回的字符放到一个共享缓冲区(字符数组)中,当该缓冲区不空时,xiegetch函数就从//缓冲区中读取字符;当缓冲区为空时,xiegetch函数调用getchar函数直接从输入中读字符void xieungetch(int c){  if (bufp >= BUFSIZE)    printf("ungetch: too many characters ");  else   buf[bufp++] = c;}//返回下一个标记的类型,标记可以是一个名字,一对圆括号,包含一个数字的一对方括号,也可以其他的单个字符(如EOF)int gettoken(void){int c;char *p=token;//p永远指向token数组首地址while((c=xiegetch())==' ' || c=='\t');if(c=='('){if((c=xiegetch())==')'){strcpy(token,"()");return tokentype=PARENS;}else{xieungetch(c);return tokentype='(';}}else if(c=='['){for(*p++=c;(*p++=xiegetch())!=']';);*p='\0';return tokentype=BRACKETS;}else if(isalpha(c)){for(*p++=c;isalnum(c=xiegetch());)*p++=c;*p='\0';xieungetch(c);return tokentype=NAME;}elsereturn tokentype=c;}int main(){while(gettoken()!=EOF)//gettoken()返回下一个标记{strcpy(datatype,token);//该行的第一个记号是数据类型char intout[0]='\0';//保证打印正常dcl();//分析该行的其余部分if(tokentype!='\n')printf("syntax error\n");printf("%s: %s %s\n",name,out,datatype);}return 0;}//分析一个直接声明void dirdcl(void){int type;if(tokentype=='('){dcl();//再递归调用原来的dir函数if(tokentype!=')')printf("error:missing)\n");}else if(tokentype == NAME)//变量名strcpy(name,token);else printf("error:expected name or (dcl)\n");//必须是名字或者括弧while((type=gettoken())==PARENS || type==BRACKETS)//gettoken返回了type和token串if(type==PARENS)strcat(out," function returning");else{strcat(out," array");strcat(out,token);strcat(out," of");}}//对一个声明符进行语法分析void dcl(void){int ns;//gettoken()已经取了下一个token了,放在token中了//gettoken()已经返回下一个tokentype了,放在tokentype中了for(ns=0;gettoken()=='*';);dirdcl();while(ns-->0)strcat(out," point to");}