彻底理解指针第三篇

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值