今天主要看了函数指针和一个递归下降语法分析程序。感觉收益剖多。 函数本身不是变量,函数名代表函数的首地址,可以用函数指针指向,初始化函数指针要小心,不要加括弧。具体请详细看下面的代码。此题目:对输入的文本进行快速排序,如果指定参数-n,则可以进行数值排序,否则默认是字典排序。
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXLINE 1000 #define MAXLEN 500 char* 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; else return 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); } else printf("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 100
enum{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;
}
else
return tokentype=c;
}
int main()
{
while(gettoken()!=EOF)//gettoken()返回下一个标记
{
strcpy(datatype,token);//该行的第一个记号是数据类型char int
out[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");
}