《C程序设计语言》(《The C Programming Language》)第二版第五章练习题

5-1:
在上面的例子中,如果符号+或-的后面紧跟的不是数字,getint函数将把符号视为数字0的有效表达式。修改该函数,讲这种形式的+或-符号重新写会到输入流中

#include <stdio.h>
#include <ctype.h>

#define BUFSIZE 100

int buf[BUFSIZE];
int bufp = 0;

int getch(void);
void ungetch(int);

int getint(int *pn)
{
	int c, sign;
	int mask = 0;

	while(isspace(c = getch()))
		;
	if(!isdigit(c) && c != EOF && c != '+' && c != '-')
	{
		ungetch(c);
		return 0;
	}

	sign = (c == '-') ? -1 : 1;

	if(c == '+' || c == '-')
	{
		mask = 1;
		c = getch();
	}

	if(!isdigit(c))
	{
		ungetch(c);
		if(mask)
			ungetch((sign == -1) ? '-' : '+');
		return 0;
	}


	for(*pn = 0; isdigit(c); c = getch())
		*pn = 10 * *pn + (c - '0');
	*pn *= sign;
	if(c != EOF)
		ungetch(c);
	return c;
}

int getch(void)
{
	return (bufp == 0) ? getchar() : buf[--bufp];
}

void ungetch(int c)
{
	if(bufp >= BUFSIZE)
		printf("ungetch: too many characters\n");
	else
		buf[bufp++] = c;
}

5-2:
模仿函数getint的实现方法,编写一个读取浮点数的函数getfloat。getfloat函数的返回值应该是什么类型

#include <stdio.h>
#include <ctype.h>

#define BUFSIZE 100

int buf[BUFSIZE];
int bufp = 0;

int getfloat(float *pn);
int getch(void);
void ungetch(int);

int main(void)
{
	float number = 0.0;

	printf("请输入待转换为数字的字符串\n");
	
	while(getfloat(&number))
		printf("字符串转换为数字为:%.2f\n", number);//此处只打印2位小数,因为将字符串转换为float类型会出现精度丢失的问题
		//例如-151.15,结果是-151.149994,这显然和我们的预期不同,除非将float类型改为double类型,这一问题才消失

	return 0;
}

int getfloat(float *pn)
{
	int c, sign;
	float power = 1.0;
	int mask = 0;

	while(isspace(c = getch()))
		;

	if(!isdigit(c) && c != EOF && c != '+' && c != '-' && c != '.')
	{
		ungetch(c);
		return 0;
	}

	sign = (c == '-') ? -1 : 1;

	if(c == '+' || c == '-')
	{
		mask = 1;
		c = getch();
	}

	if(!isdigit(c) && c != '.')
	{
		ungetch(c);
		if(mask)
			ungetch((sign == -1) ? '-' : '+');
		return 0;
	}


	for(*pn = 0.0; isdigit(c); c = getch())
		*pn = 10.0 * *pn + (float)(c - '0');

	if(c == '.')
	{
		for(c = getch(); isdigit(c); c = getch())
		{
			*pn = 10.0 * *pn + (float)(c - '0');
			power *= 10.0;
		}
	}
	*pn = (*pn / power) * (float)sign;
	if(c != EOF)
		ungetch(c);
	return c;
}

int getch(void)
{
	return (bufp == 0) ? getchar() : buf[--bufp];
}

void ungetch(int c)
{
	if(bufp >= BUFSIZE)
		printf("ungetch: too many characters\n");
	else
		buf[bufp++] = c;
}

5-3:
用指针方式实现第2章中的函数strcat,函数strcat(s, t)将t指向的字符串复制到s指向的字符串的尾部

#include <stdio.h>

#define MAXSIZE 100

void strcat(char *s, char *t);

int main(void)
{
	char string1[MAXSIZE] = "Hello world!";
	char string2[MAXSIZE] = "good morning";

	strcat(string1, string2);

	printf("%s\n", string1);
	return 0;
}

void strcat(char *s, char *t)
{
	while(*s++)//此语句等同于while(*s) {s++;} s++;
		;
	//ASCII码中空字符编码为0,当*s == '\0'时,循环终止,然后s递增,指向下一字符
	//也就是说退出上面的循环时,s其实指向的是空字符的下一字符
	s--;
	while(*s++ = *t++)//此处与上面的语句同理
		;
}

5-4:
编写函数strend(s, t)。如果字符串t在字符串s的尾部,函数返回1,否则返回0

#include <stdio.h>

#define MAXSIZE 100

int strend(char *s, char *t);

int main(void)
{
	int result;
	char string1[MAXSIZE] = "Hello world!";
	char string2[MAXSIZE] = "ld!";
	char string3[MAXSIZE] = "rld";

	result = strend(string1, string2);
	printf("%d\n", result);

	result = strend(string1, string3);
	printf("%d\n", result);
	return 0;
}

int strend(char *s, char *t)
{
	int t_len = 0;
	int s_len = 0;

	while(*s)
	{
		s++;
		s_len++;//s_len不包括空字符
	}
	s--;//s指向的是空字符的前一个字符

	while(*t)
	{
		t++;
		t_len++;
	}
	t--;

	if(s_len >= t_len)
	{
		for(; *s == *t && t_len > 0; t_len--, s--, t--)
			;
		if(t_len == 0)
			return 1;
	}
	return 0;
}

5-5:
实现库函数strncpy、strncat和strcmp,它们最多对参数字符串中的前n个字符进行操作。例如,函数strncpy(s, t, n),将t中最多前n个字符复制到s中,更详细的说明请参见附录B

#include <stdio.h>

#define MAXSIZE 100

void strncpy(char *s, char *t, int n);
void strncat(char *s, char *t, int n);
int strncmp(char *s, char *t, int n);

int main(void)
{
	int number = 4;
	char string1[MAXSIZE];
	char string2[MAXSIZE] = "Hello";
	char string3[MAXSIZE] = "World!";
	char string4[MAXSIZE] = "Hel";

	strncpy(string1, string2, number);
	printf("%s\n", string1);

	strncat(string2, string3, number);
	printf("%s\n", string2);

	printf("%d\n", strncmp(string2, string4, number));

	return 0;
}

void strncpy(char *s, char *t, int n)
{
	
	if(n <= 0)
	{
		printf("please enter a positive number\n");
		return;
	}
	for(; (*s++ = *t++) && n > 0; n--)
		;
	*--s = '\0';
}

void strncat(char *s, char *t, int n)
{
	if(n <= 0)
	{
		printf("please enter a positive number\n");
		return;
	}
	while(*s++)
		;
	s--;
	for(; (*s++ = *t++) && n > 0; n--)
		;
	*--s = '\0';
}

int strncmp(char *s, char *t, int n)
{
	if(n <= 0)
	{
		printf("please enter a positive number\n");
		return EOF;
	}
	for(; (*s == *t) && n > 0 && *t != '\0'; n--)
	{
		s++;
		t++;
	}
	if(n == 0 || *t == '\0' )
		return 0;
	else if(*s > *t)
		return 1;
	else
		return -1;
}

5-6:
采用指针而非数组索引方式改写前面章节和练习中的某些程序,例如getline(第1、4章),atoi、itoa以及它们的变形形式(第2、3、4章),reserve(第三章),strindex、getop(第4章)等等

#include <stdio.h>
#include <string.h>
#include <ctype.h>

int getline(char *s, int lim)
{
	int c;
	char *start = s;

	while(--lim > 0 && (c = getchar()) != EOF && c != '\n')
	{
		*s++ = c;
	}

	if(c == '\n')
		*s++ = c;

	*s = '\0';
	return (s - start);
}

int atoi(char *s)
{
	int n, sign;
	while(isspace(*s))
		s++;
	sign = (*s != '-' && *s != '+') ? 1 : ((*s++ == '+') ? 1 : -1);

	for(n = 0; isdigit(*s); s++)
		n = 10 * n + (*s - '0');
	return n * sign;
}

void itoa(int n, char *s)
{
	unsigned int u;
	char *start = s;
	u = (n < 0) ? -n : n;

	do{
		*s++ = (u % 10) + '0';
	}while((u /= 10) > 0);

	if(n < 0)
		*s++ = '-';
	
	*s = '\0';
	reverse(start);
}

void reverse(char *s)
{
	int temp;
	char *end = s;
	while(*end)
		end++;
	end--;
	for(; end - s > 0; end--, s++)
	{
		temp = *s;
		*s = *end;
		*end = temp;
	}
}

void itob(int n, char *s, int b)
{
	unsigned int u;
	char *start = s;
	u = (n < 0) ? -n : n;

	do
	{
		if(u % b > 9)
			*s++ = (u % b) - 10 + 'A';
		else
			*s++ = (u % b) + '0'; 
	}while((u /= b) > 0);

	if(n < 0)
		*s++ = '-';
	
	*s = '\0';
	reverse(start);
}

double atof(char s[])
{
	double val;
	double power = 1.0;
	double sign;
	int mask;      //标记e后面的符号
	int power_e;   //记录指数大小
	double result; //结果值

	while(isspace(*s))
		s++;
	
	sign = (*s != '+' && *s != '-') ? 1 : ((*s++ == '+') ? 1 : -1);
	
	for(val = 0; isdigit(*s); s++)
		val = 10.0 * val + (*s - '0');
	
	if(*s == '.')
	{
		s++;
		for(; isdigit(*s); s++)
		{
			val = 10.0 * val + (*s - '0');
			power *= 10.0;
		}
	}

	result = sign * val / power;
	if(*s == 'e' || *s == 'E')
	{
		s++;
		if(*s == '-')
			mask = 1;
		else if(*s == '+' || isdigit(*s))
			mask = 0;
		else
			return result;

		if(*s == '-' || *s == '+')
			s++;

		for(power_e = 0; isdigit(*s); s++)
			power_e = 10 * power_e + (*s - '0');

		if(mask)//e后面符号为-时,表示除以power_e个10.0
		{
			while(power_e > 0)
			{
				result /= 10.0;
				power_e--;
			}
			return result;
		}
		else//e后面符号为+或没有符号时,表示乘以power_e个10.0
		{
			while(power_e > 0)
			{
				result *= 10.0;
				power_e--;
			}
			return result;
		}
	}
	else//数字后面没有e或E
		return result;
}

int strindex(char *s, char *t)
{
	int length_s = strlen(s) - 1;
	int length_t = strlen(t) - 1;
	char *end_s = s + length_s - 1;
	char *end_t = t + length_t - 1;
	char *p, *q;

	for(; end_s >= s; end_s--)
	{
		for(p = end_s, q = end_t; (q >= t) && (*p == *q); p--, q--)
			;
		if(q < t)
			return (p - s + 2);//返回字符出现在字符串中的实际位置
	}
	return -1;
}

int getop(char s[])
{
	int i, c;
	while(*s = c = getch()) == ' ' || c == '\t')
		;
	*(s++) = '\0';
	if(!isdigit(c) && c != '.')
		return c;
	i = 0;
	if(isdigit(c))
		while(isdigit(*s++ = c = getch()))
			;

	if(c == '.')
		while(isdigit(*s++ = c = getch()))
			;

	*s = '\0';
	if(c != EOF)
		ungetch(c);
	return NUMBER;
}

5-7:
重写函数readlines,将输入的文本行储存到有main函数提供的一个数组中,而不是存储到调用alloc分配的存储空间中。该函数的运行速度比改写前快多少?

/*两个程序运行时间的差值为调用alloc函数的时间*/
#include <stdio.h>
#define MAXLINES 5000
#define MAXLEN 1000

int readlines(char line[][MAXLEN], int nlines);

int main(void)
{
	int nlines;
	char lines[MAXLINES][MAXLEN];//最多可存储MAXLINES条长度为MAXLINE的字符串的数组

	if((nlines = readlines(lines, MAXLINES)) >= 0)
	{
		qsort(lines, 0, nlines - 1);
		writelines(lines, nlines);
		return 0;
	}
	else
	{
		printf("error: input too big to sort\n");
		return 1;
	}
}

int readlines(char lines[][MAXLEN], int maxlines)
{
	int len, nlines;

	nlines = 0;
	while((len = getline(lines[nlines], MAXLEN)) > 0)
	{
		if(nlines >= maxlines)
			return -1;
		else
		{
			lines[nlines][len - 1] = '\0';
		}
		nlines++;
	}
	return nlines;
}

5-8:
函数day_of_year和month_day中没有错误检查,请解决该问题

#include <stdio.h>
static char daytab[2][13] = {
	{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
	{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

int day_of_year(int year, int month, int day);
int month_day(int year, int yearday, int *pmonth, int *pday);

int main(void)
{
	int year = 1946;
	int yearday = 0;
	int month;
	int day;
	int result;

	result = month_day(year, yearday, &month, &day);
	if(result == -1)
		printf("error!\n");
	else
		printf("%d年%d天是%d月%d日\n", year, yearday, month, day);

	month = 13;
	day = 25;
	result = day_of_year(year, month, day);
	if(result == -1)
		printf("error!\n");
	else
		printf("%d月%d日是%d年%d天\n", month, day, year, result);

	return 0;
}

int day_of_year(int year, int month, int day)
{
	int i, leap;

	if(month < 1 || month > 12 || day < 1 || day > 31)//错误检查应该更加复杂,比如要判断某月的天数在不在范围内,这里简化了
		return -1;

	leap = year % 4 == 0 && year % 100 != 0 || year % 400 ==0;//前者(即4的倍数并且非100的倍数)是普通闰年,后者为世纪闰年
	for(i = 1; i < month; i++)
		day += daytab[leap][i];
	return day;
}

int month_day(int year, int yearday, int *pmonth, int *pday)
{
	int i, leap;

	leap = year % 4 == 0 && year % 100 != 0 || year % 400 ==0;

	if(yearday < 1 || (!leap && yearday > 365) || (leap && yearday > 366))//闰年天数为366天
		return -1;

	for(i = 1; yearday > daytab[leap][i]; i++)
		yearday -= daytab[leap][i];
	*pmonth = i;
	*pday = yearday;
}

5-9:
用指针方式代替数组下标方式改写函数day_of_year和month_day

int day_of_year(int year, int month, int day)
{
	int i, leap;

	if(month < 1 || month > 12 || day < 1 || day > 31)
		return -1;

	leap = year % 4 == 0 && year % 100 != 0 || year % 400 ==0;//前者(即4的倍数并且非100的倍数)是普通闰年,后者为世纪闰年
	for(i = 1; i < month; i++)
	{
		day += *(*(daytab + leap) + i);//daytab指向的是数组的第一行(即&daytab[0])
		//daytab + leap指向的是数组第leap + 1行(即&daytab[leap])
		//那么*(daytab + leap)得到daytab[leap][0]的地址,即数组第leap+1行首元素的地址
		//*(daytab + leap) + i指向的是数组的第leap+1行的第i + 1个元素(即&daytab[leap][i])
		//*(*(daytab + leap) + i)表示获取该元素的值(即daytab[leap][i])
	}
	return day;
}

int month_day(int year, int yearday, int *pmonth, int *pday)
{
	int i, leap;

	leap = year % 4 == 0 && year % 100 != 0 || year % 400 ==0;

	if(yearday < 1 || (!leap && yearday > 365) || (leap && yearday > 366))//闰年天数为366天
		return -1;

	for(i = 1; yearday > *(*(daytab + leap) + i); i++)
		yearday -= *(*(daytab + leap) + i);
	*pmonth = i;
	*pday = yearday;
}

5-10:
编写程序expr,以计算从命令行输入的逆波兰表达式的值,其中每个运算符或操作数用一个单独的参数表示。例如,命令
expr 2 3 4 + *
将计算表达式2 * (3 + 4)的值

/*程序的主体部分不需要太大变化,主要是让程序知道什么时候该停止运行,输出计算结果*/
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <stdlib.h>

#define RAD_TO_DEG (180 / (4 * atan(1)))//将弧度转换为角度的公式,即1弧度 = 180 / π 度,其中π = 4 * atan(1)
#define MAXOP 100
#define NUMBER '0'
#define MAXVAL 100
#define MAXVAR 26

int sp = 0;
double val[MAXVAL];

int getop(char s[], char t[]);
void push(double);
double pop(void);
void printf_stack_top(void);
void copy_stack_top(void);
void change_stack_top(void);
void empty_stack(void);

int main(int argc, char ** argv)
{
	int type, i;
	double op2;
	double prev;        //记录最近打印的值
	char s[MAXOP];
	double var[MAXVAR];//包含26个单个英文字母的变量数组
	int print = 0;     //记录最近是否有打印的值
	int mask = 0;      //标记变量是否已赋值

	for(i = 0; i < MAXVAR; i++)//对每个变量的值进行初始化
		var[i] = 0.0;

	if(argc < 2)
	{
		printf("error: too few parameters\n");
		return -1;
	}

	while(argc-- > 0)//此程序最重要的一步,假设命令行输入的参数为4个(包括程序名),我们需要让循环迭代4次
	//前三次传入命令行除程序名以外的三个参数,第四次需要传入作为计算终止标志的换行符
	{
		if(argc == 0)//当argc==0是,type='\n',输出计算结果
			type = '\n';
		else
			type = getop(s, *++argv);
		switch(type)
		{
		case NUMBER:
			push(atof(s));
			break;

		case '+':
			push(pop() + pop());
			break;

		case '*':
			push(pop() * pop());
			break;

		case '-':
			op2 = pop();
			push(pop() - op2);
			break;

		case '/':
			op2 = pop();
			if(op2 != 0.0)
				push(pop() / op2);
			else
				printf("error: zero divisor\n");
			break;

		case '%':
			op2 = pop();
			if(op2 != 0.0)
				push((int)pop() % (int)op2);

			else
				printf("error: zero divisor\n");
			break;

		case 'd':
			printf_stack_top();
			break;

		case 'f':
			copy_stack_top();
			break;

		case 'j':
			change_stack_top();
			break;

		case 'q':
			empty_stack();
			break;

		case 's':
			push( sin( (pop() / RAD_TO_DEG) ) );
			break;

		case 'c':
			push( cos( (pop() / RAD_TO_DEG) ) );
			break;

		case 't':
			op2 = pop();
			if(op2  != 90)
				push( tan( (pop() / RAD_TO_DEG) ) );
			else
				printf("tan90 is invalid value\n");
			break;

		case 'e':
			push( exp( pop() ) );
			break;

		case 'p':
			op2 = pop();
			push( pow( pop(), op2 ) );
			break;

		case 'v':
			if(print)
				printf("The most recently printed value is %.8g\n", prev);
			else
				printf("No recently printed values!\n");
			break;

		case '\n':
			prev = pop();
			print = 1;
			printf("\t%.8g\n", prev);
			break;

		default:
			if(type >= 'A' && type <= 'Z')
			{
				if(mask == 0)
				{
					var[type - 'A'] = pop();
					mask = 1;
				}
				else
					push(var[type - 'A']);
			}
			
			else
				printf("error: unkown command %s\n", s);
			break;
		}
	}
	return 0;
}

void push(double f)
{
	if(sp < MAXVAL)
		val[sp++] = f;
	else
		printf("error: stack full, can't push %g\n", f);
}

double pop(void)
{
	if(sp > 0)
		return val[--sp];
	else
	{
		printf("error: stack empty\n");
		return 0.0;
	}
}

int getop(char s[], char t[])
{
	int i, c;
	*s++ = c = *t++;//不需要跳过空格符

	if(!isdigit(c) && c != '.')
		return c;

	if(isdigit(c))
		while(isdigit(*s++ = c = *t++))
			;

	if(c == '.')
		while(isdigit(*s++ = c = *t++))
			;
	if(c == '\0')
	{
		*s = '\0';
		return NUMBER;
	}
	return c;
}

void printf_stack_top(void)
{
	if(sp > 0)
		printf("top of stack: %8g\n", val[sp - 1]);
	else
		printf("stack is empty\n");
}

void copy_stack_top(void)
{
	double temp = pop();

	push(temp);
	push(temp);
	printf("Done!\n");
}

void change_stack_top(void)
{
	double temp1 = pop();
	double temp2 = pop();

	push(temp1);
	push(temp2);
}

void empty_stack(void)
{
	sp = 0;
}

5-11:
修改程序entab和detab(第1章练习中编写的函数),使它们接受一组作为参数的制表符停止位。如果启动程序时不带参数,则使用默认的制表符停止位设置

#include <stdio.h>
#include <stdlib.h>

#define MAXLINE 1000
#define Tabsize 8//制表符的长度时固定的,在本系统中其长度为8个字符
 
int getline(char line[], int maxline);
void detab(char *s, char *t, int tabsize);
void entab(char *s, char *t, int tabsize);
 
int main(int argc, char *argv[])
{
	char origin[MAXLINE];
	char result[MAXLINE];
	int tabsize;

	if(argc < 2)
		tabsize = Tabsize;
	else if(argc == 2)
	{
		tabsize = atoi(argv[1]);
		if(tabsize < 1)
		{
			printf("please enter a positive number\n");
			return -1;
		}
	}
	else
	{
		printf("error!\n");
		return -1;
	}

	printf("请输入带制表符的字符串:\n");
	while(getline(origin, MAXLINE) > 0)
	{
		detab(origin, result, tabsize);
		printf("detab : \n%s\n", result);

		printf("请输入带空格的字符串:\n");
		getline(origin, MAXLINE);
		entab(origin, result, tabsize);
		printf("entab : \n%s\n", result);

		printf("继续输入带制表符的字符串\n");
	}
	return 0;
}
 
void detab(char *s, char *t, int tabsize)
{
	int all, size;//all统计字符数,size计算制表符到制表符终止位占多少字符位

	for(all = 0, size = 0; *s; s++)
	{
		if(*s == '\t')
		{
			size = tabsize - (all % tabsize);//计算需要多少个空格填充制表符所占字符长度
			while(size > 0)
			{
				*t++ = ' ';
				all++;
				size--;
			}
		}
		else
		{
			*t++ = *s;
			all++;
		}
	}
	*t = *s;
}
void entab(char *s, char *t, int tabsize)
{
	int all, space;//all统计字符数,space记录使用制表符后还需要使用多少个空格
	for(all = 0, space = 0; *s; s++)
	{
		if(*s == ' ')
		{
			if(all % tabsize != tabsize - 1)
				space++;//标记空格数

			else if(all % Tabsize == tabsize - 1)//每经过一次制表符的终止位就打印一次制表符
			{
				space = 0;//将空格数清零
				*t++ = '\t';
			}
			all++;
		}
		else
		{
			if(space > 0)//当空格没有超过制表符终止位时,打印空格
			{
				while(space > 0)
				{
					*t++ = ' ';
					space--;
				}
			}
			*t++ = *s;
			all++;
		}
	}
	*t = *s;
}

int getline(char s[], int lim)
{
	int c, i;
 
	for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
		s[i] = c;

	if (c == '\n')
	{
		s[i] = c;
		++i;
	}
 
	s[i] = '\0';
	return i;
}

5-12:
对程序entab和detab的功能做一些扩充,以接受下列缩写的命令:
entab -m +n
表示制表符从m列开始,每隔n列停止。选择(对使用者而言)比较方便的默认行为

/*此题与上一题的区别不大,可以将上一题看做m = 1,即从第1列开始,每隔n列停
止,也就是说此题正在上一题的基础上需要添加一个判断,m列之前的部分不用更改*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define MAXLINE 1000
#define Tabsize 8//制表符的长度时固定的,在本系统中其长度为8个字符
 
int getline(char line[], int maxline);
void detab(char *s, char *t, int tabsize, int m);
void entab(char *s, char *t, int tabsize, int m);
 
int main(int argc, char *argv[])
{
	char origin[MAXLINE];
	char result[MAXLINE];
	int tabsize;
	int m;

	if(argc == 1)
	{
		m = 1;
		tabsize = Tabsize;
	}
	else if(argc == 3)
	{
		while(--argc)
		{
			if(*(*++argv) == '-' && isdigit(*++(*argv)))
				m = atoi(*argv);
			else if(**argv == '+' && isdigit(*++(*argv)))
				tabsize = atoi(*argv);
			else
			{
				printf("parameter error\n");
				return -1;
			}
		}
	}
	else
	{
		printf("error!\n");
		return -1;
	}

	printf("请输入带制表符的字符串:\n");
	while(getline(origin, MAXLINE) > 0)
	{
		detab(origin, result, tabsize, m);
		printf("detab : \n%s\n", result);

		printf("请输入带空格的字符串:\n");
		getline(origin, MAXLINE);
		entab(origin, result, tabsize, m);
		printf("entab : \n%s\n", result);

		printf("继续输入带制表符的字符串\n");
	}
	return 0;
}
 
void detab(char *s, char *t, int tabsize, int m)
{
	int all, size;//all统计字符数,size计算制表符到制表符终止位占多少字符位

	for(all = 0, size = 0; *s; s++)
	{
		if(*s == '\t' && (all >= (m - 1)))
		{
			size = tabsize - (all % tabsize);//计算需要多少个空格填充制表符所占字符长度
			while(size > 0)
			{
				*t++ = ' ';
				all++;
				size--;
			}
		}
		else if(*s == '\t')//当制表符出现在m列之前时,保留制表符,同时统计制表符所占字符位
		{
			size = tabsize - (all % tabsize);
			while(size > 0)
			{
				all++;
				size--;
			}
			*t++ = '\t';
		}
		else
		{
			*t++ = *s;
			all++;
		}
	}
	*t = *s;
}
void entab(char *s, char *t, int tabsize, int m)
{
	int all, space;//all统计字符数,space记录使用制表符后还需要使用多少个空格
	for(all = 0, space = 0; *s; s++)
	{
		if(*s == ' ' && (all >= (m - 1)))
		{
			if(all % tabsize != tabsize - 1)
				space++;//标记空格数

			else if(all % Tabsize == tabsize - 1)//每经过一次制表符的终止位就打印一次制表符
			{
				space = 0;//将空格数清零
				*t++ = '\t';
			}
			all++;
		}
		else
		{
			if(space > 0)//当空格没有超过制表符终止位时,打印空格
			{
				while(space > 0)
				{
					*t++ = ' ';
					space--;
				}
			}
			*t++ = *s;
			all++;
		}
	}
	*t = *s;
}

int getline(char s[], int lim)
{
	int c, i;
 
	for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
		s[i] = c;

	if (c == '\n')
	{
		s[i] = c;
		++i;
	}
 
	s[i] = '\0';
	return i;
}

5-13:
编写函数tail,将其输入中的最后n行打印出来,默认情况下,n的值为10,但可以通过一个可选参数改变n的值,因此,命令:
tail -n
将打印其输入的最后n行。无论输入或n的值是否合理,该程序都应该能正常运行。编写的程序要充分地利用存储空间:输入行的存储方式应该同5.6节排序程序的存储方式一样,而不采用固定长度的二维数组

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAXLINES 100
#define MAXLEN 1000
#define ALLOCSIZE 10000

char *lineptr[MAXLINES];
char allocbuf[ALLOCSIZE];
char *allocp = allocbuf;

int readlines(char *lineptr[], int maxlines);
void printlines(char *lineptr[], int n, int nlines);
int getline(char s[], int lim);
char *alloc(int n);
void afree(char *p);

int main(int argc, char *argv[])
{
	int n;
	int nlines = 0;

	if(argc == 2)//合理输入的情况下
	{
		if(*(*++argv) == '-' && isdigit(*++(*argv)))
			n = atoi(*argv);
		else if(isdigit(**argv))
			n = atoi(*argv);
		else
			n = 10;
	}
	else
		n = 10;

	if((nlines = readlines(lineptr, MAXLINES)) > 0)
	{
		if(nlines < n)
			printlines(lineptr, 0, nlines);
		else
			printlines(lineptr, n, nlines);
	}
	else
	{
		printf("error: input is empty\n");
		return -1;
	}

	afree(allocbuf);

	return 0;
}

int readlines(char *lineptr[], int maxlines)
{
	int len, nlines;
	char *p, line[MAXLEN];

	nlines = 0;
	while((len = getline(line, MAXLEN)) > 0)
	{
		if(nlines >= maxlines || (p = alloc(len)) == NULL)
			return -1;
		else
		{
			line[len - 1] = '\0';
			strcpy(p, line);
			lineptr[nlines++] = p;
		}
	}
	return nlines;
}

void printlines(char *lineptr[], int n, int nlines)
{
	for(int i = nlines - n; i < nlines; i++)
		printf("%s\n", lineptr[i]);
}

int getline(char s[], int lim)
{
	int c, i;
 
	for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
		s[i] = c;

	if (c == '\n')
	{
		s[i] = c;
		++i;
	}
 
	s[i] = '\0';
	return i;
}

char *alloc(int n)
{
	if(allocp + n <= allocbuf + ALLOCSIZE)
	{
		allocp += n;
		return allocp - n;
	}
	else
		return NULL;
}

void afree(char *p)
{
	if(p >= allocbuf && p < allocbuf + ALLOCSIZE)
		allocp = p;
}

5-14、5-15、5-16:
修改排序程序,使它具有以下功能:
添加-r标记,该标记表明,以逆序(递减)方式排序。要保证-r和-n能够组合在一起使用;
添加-f标记,使得排序过程不考虑字母大小写之间的区别。例如,比较a和A时认为它们相等;
添加选项-d(代表目录顺序),该选项表明,只对字母、数字和空格进行比较。要保证该选项可以和-f组合在一起使用;

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#define MAXLINES 100
#define MAXLEN 1000
#define ALLOCSIZE 10000

char allocbuf[ALLOCSIZE];
char *lineptr[MAXLINES];
char *allocp = allocbuf;

int readlines(char *lineptr[], int maxlines);
void writelines(char *lineptr[], int nlines);
int getlines(char s[], int lim);
void qsort(void *lineptr[], int left, int right, int (*comp)(const void *, const void *), int sort_order);
int numcmp(const char *s, const char *t);
int charcmp(const char *s, const char *t);//不区分大小写排序
int listcmp(const char *s, const char *t);//按照目录顺序排序
int list_charcmp(const char *s, const char *t);//按照目录顺序,不区分大小写排序
void swap(void *v[], int i, int j);
char *alloc(int n);
void afree(char *p);

int main(int argc, char *argv[])
{
	int nlines;
	int sort_mode = 0;//排序模式
	int sort_order = -1;//排序顺序

	if(argc == 1)
	{
		sort_mode = 0;//字符排序
		sort_order = -1;//顺序排序
	}
	else if(argc == 2)
	{
		if(!strcmp(argv[1], "-n"))
			sort_mode = 1;//数值排序
		else if(!strcmp(argv[1], "-r"))
		{
			sort_mode = 0;
			sort_order = 1;//逆序排序
		}
		else if(!strcmp(argv[1], "-f"))
		{
			sort_mode = 2;//不区分大小写排序
		}
		else if(!strcmp(argv[1], "-d"))
		{
			sort_mode = 3;//按照目录顺序排序
		}
		else
		{
			printf("parameter error!\n");
			return -1;
		}

	}
	else if(argc == 3)
	{
		if(!strcmp(argv[1], "-n") && !strcmp(argv[2], "-r"))
		{
			sort_mode = 1;
			sort_order = 1;
		}
		else if(!strcmp(argv[1], "-f") && !strcmp(argv[2], "-r"))
		{
			sort_mode = 2;
			sort_order = 1;
		}
		else if(!strcmp(argv[1], "-d") && !strcmp(argv[2], "-f"))
		{
			sort_mode = 4;//按照目录顺序,不区分大小写排序
		}
		else
		{
			printf("parameter error!\n");
			return -1;
		}
	}
	else
	{
		printf("parameter error!\n");
		return -1;
	}



	if((nlines = readlines(lineptr, MAXLINES)) > 0)
	{
		qsort((void **) lineptr, 0, nlines - 1, (int (*)(const void *, const void *)) 
			((!sort_mode) ? strcmp : ((sort_mode == 1) ? numcmp : ((sort_mode == 2) ? charcmp : 
			((sort_mode == 3) ? listcmp : list_charcmp)))), sort_order);
		writelines(lineptr, nlines);
	}
	else
	{
		printf("input is empty!\n");
		return -1;
	}

	afree(allocbuf);
	return 0;
}

int readlines(char *lineptr[], int maxlines)
{
	int len, nlines;
	char *p, line[MAXLEN];

	nlines = 0;
	while((len = getlines(line, MAXLEN)) > 0)
	{
		if(nlines >= maxlines || (p = alloc(len)) == NULL)
			return -1;
		else
		{
			line[len - 1] = '\0';
			strcpy(p, line);
			lineptr[nlines++] = p;
		}
	}
	return nlines;
}

char *alloc(int n)
{
	if(allocp + n <= allocbuf + ALLOCSIZE)
	{
		allocp += n;
		return allocp - n;
	}
	else
		return NULL;
}

int getlines(char s[], int lim)
{
	int c, i;

	for(i = 0; i < lim -1 && (c = getchar()) != EOF && c != '\n'; i++)
		s[i] = c;

	if(c == '\n')
		s[i++] = '\n';

	s[i] = '\0';
	return i;

}

void qsort(void *v[], int left, int right, int (*comp)(const void *, const void *), int sort_order)
{
	int i, last;

	void swap(void *v[], int, int);

	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]) == sort_order)
			swap(v, ++last, i);
	
	swap(v, left, last);
	qsort(v, left, last - 1, comp, sort_order);
	qsort(v, last + 1, right, comp, sort_order);
}

int numcmp(const char *s, const char *t)
{
	double v1, v2;
	v1 = atof(s);
	v2 = atof(t);

	if(v1 < v2)
		return -1;
	else if(v1 > v2)
		return 1;
	else
		return 0;
}

int charcmp(const char *s, const char *t)
{
	for(; *s && *t && tolower(*s) == tolower(*t); s++, t++)
		;
	if(tolower(*s) < tolower(*t))
		return -1;
	else if(tolower(*s) > tolower(*t))
		return 1;
	else
		return 0;
}

int listcmp(const char *s, const char *t)
{
	int c1, c2;
	do
	{
		while(!isalnum(*s) && *s != ' ' && *s != '\0')
			s++;
		while(!isalnum(*t) && *t != ' ' && *t != '\0')
			t++;
		if(*s == '\0' && *t == '\0')
			return 0;
		c1 = *s;
		s++;
		c2 = *t;
		t++;
	}while(c1 == c2);

	if(c1 < c2)
		return -1;
	else
		return 1;
}

int list_charcmp(const char *s, const char *t)
{
	int c1, c2;
	do
	{
		while(!isalnum(*s) && *s != ' ' && *s != '\0')
			s++;
		while(!isalnum(*t) && *t != ' ' && *t != '\0')
			t++;
		if(*s == '\0' && *t == '\0')
			return 0;
		c1 = tolower(*s);
		s++;
		c2 = tolower(*t);
		t++;
	}while(c1 == c2);

	if(c1 < c2)
		return -1;
	else
		return 1;

}

void swap(void *v[], int i, int j)
{
	void *temp;
	temp = v[i];
	v[i] = v[j];
	v[j] = temp;
}

void writelines(char *lineptr[], int nlines)
{
	for(int i = 0; i < nlines; i++)
		printf("%s\n", lineptr[i]);
}

void afree(char *p)
{
	if(allocbuf <= p && p >= allocbuf + ALLOCSIZE)
		allocp = p;
}

5-17:
添加字段处理功能,以使得排序程序可以根据行内的不同字段进行排序,每个字段按照一个单独的选项集合进行排序

/*关于字段处理的部分有点费解,我认为的字段处理是类似于excel表那样,假设有
两行输入,行1为123456781234,行2为567812345678,字段宽度为4,假设按照第一
个字段宽度排序,行1在前,行2在后,假设按照第二个字段宽度排序,行2在前,行1
灾后,假设没有指定按照那个字段排序,那就默认采用第一个字段宽度,而如果输入
行的字段宽度小于指定的字段宽度,即假设按照第四个字段宽度,上面两行都只有三
个字段宽度,此时采用默认的第一个字段宽度
采用结构来实现上面的要求是非常方便的,但是还没学,所以还是跟着书本来
重新写一个,上面的程序再添就有点乱了,改也不好改
*/`
#include <stdio.h>
#include <string.h>

#define MAXLINES 100
#define MAXLEN 1000
#define ALLOCSIZE 10000
#define SIZE 4//定义一个字段宽度为4个字符

char allocbuf[ALLOCSIZE];
char *lineptr[MAXLINES];
char *allocp = allocbuf;

int readlines(char *lineptr[], int maxlines);
void writelines(char *lineptr[], int nlines);
int getlines(char s[], int lim);
void qsort(char *lineptr[], int left, int right, int (*comp)(const char *, const char *, int ), int size);
int sizecmp(const char *s, const char *t, int size);
void swap(char *v[], int i, int j);
char *alloc(int n);
void afree(char *p);

int main(int argc, char *argv[])
{
	int nlines;
	int size = 1;

	if(argc == 2)
	{
		if(!strcmp(argv[1], "-s"))
		{
			printf("请输入按照哪个字段宽度排序\n");
			scanf("%d", &size);
		}
	}


	if((nlines = readlines(lineptr, MAXLINES)) > 0)
	{
		qsort(lineptr, 0, nlines - 1, sizecmp, size);
		writelines(lineptr, nlines);
	}
	else
	{
		printf("input is empty!\n");
		return -1;
	}

	afree(allocbuf);
	return 0;
}

int readlines(char *lineptr[], int maxlines)
{
	int len, nlines;
	char *p, line[MAXLEN];

	nlines = 0;
	while((len = getlines(line, MAXLEN)) > 0)
	{
		if(nlines >= maxlines || (p = alloc(len)) == NULL)
			return -1;
		else
		{
			line[len - 1] = '\0';
			strcpy(p, line);
			lineptr[nlines++] = p;
		}
	}
	return nlines;
}

char *alloc(int n)
{
	if(allocp + n <= allocbuf + ALLOCSIZE)
	{
		allocp += n;
		return allocp - n;
	}
	else
		return NULL;
}

int getlines(char s[], int lim)
{
	int c, i;

	for(i = 0; i < lim -1 && (c = getchar()) != EOF && c != '\n'; i++)
		s[i] = c;

	if(c == '\n')
		s[i++] = '\n';

	s[i] = '\0';
	return i;

}

void qsort(char *v[], int left, int right, int (*comp)(const char *, const char *, int ), int size)
{
	int i, last;

	void swap(char *v[], int, int);

	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], size) < 0)
			swap(v, ++last, i);
	
	swap(v, left, last);
	qsort(v, left, last - 1, comp, size);
	qsort(v, last + 1, right, comp, size);
}

int sizecmp(const char *s, const char *t, int size)
{
	int length = (size - 1) * SIZE;
	const char *v1, *v2;
	v1 = s;
	v2 = t;

	if(length == 0)
		return strcmp(s, t);

	while(length > 0 && *v1 != '\0' && *v2 != '\0')
	{
		v1++;
		v2++;
		length--;
	}
	if(*v1 == '\0' || *v2 == '\0')
	{
		return strcmp(s, t);
	}
	else
	{
		return strcmp(v1, v2);
	}
}

void swap(char *v[], int i, int j)
{
	char *temp;
	temp = v[i];
	v[i] = v[j];
	v[j] = temp;
}

void writelines(char *lineptr[], int nlines)
{
	for(int i = 0; i < nlines; i++)
		printf("%s\n", lineptr[i]);
}

void afree(char *p)
{
	if(allocbuf <= p && p >= allocbuf + ALLOCSIZE)
		allocp = p;
}
//程序比较简陋,但是基本能实现要求

5-18、5-20:
修改dcl程序,使它能处理输入中的错误;
扩展dcl程序的功能,使它能够处理包含其它成分的声明,例如带有函数参数类型的声明、带有类似于const限定符的声明等;

/*这个程序让我头有点大,简单的梳理一下思路吧,我们假设输入行的输入为:
char (*(*x())[])() \n
程序的递归从第一个'('字符开始,dcl递进到dirdcl,再递进到dcl,遇到第二
个'('再次从dcl递进到dirdcl,再递进到dcl,dcl又递进到dirdcl,在dirdcl函数
中字符'x'被存储到name数组中,紧接着循环语句开始执行,读取到'()',out数组存
储" function returning",再读取到')',循环退出,递归开始回归,首先回归到
dcl,执行dirdcl();语句后面的部分,out数组变为" function returning pointer
 to",再次回归,到dirdcl函数,执行dcl();语句后面的部分,此时tokentype == 
 ')',条件判断为假,进入循环部分,读取输入'[]',out数组变为" function 
 returning pointer to array[] of",循环继续,读取到')',循环退出,递归回
 归到dcl,执行dirdcl();语句后面的部分,out数组变为" function returning 
 pointer to array[] of pointer to", 再次回归到dirdcl,执行dcl();语句后面
 的部分,此时tokentype == ')',条件判断为假,进入循环部分,读取输入"()",
 out数组变为" function returning pointer to array[] of pointer to 
 function returning",循环继续,读取到换行符,退出循环,递归回归到dcl函
 数,执行dirdcl();语句后面的部分,ns值为0,条件判断为假,递归执行完,回到
 主程序,打印输出

很乱.....,递归部分的简单描述如下:
dcl() -> dirdcl() -> dcl() -> dirdcl() -> dcl() -> dirdcl()
 '('                 "*("                  "*x"       
上面为递进过程相应函数中gettoken读取的字符或字符串
 dcl() <- dirdcl() <- dcl() <- dirdcl() <- dcl() <- dirdcl()
          "()\n"                 "[])"                "())" 
上面为回归过程相应函数中gettoken读取的字符或字符串

out数组接受拼接字符串发生在递归回归过程中:
  dcl() <- dirdcl() <- dcl() <- dirdcl() <- dcl() <- dirdcl()
          "func..."   "poi..."  "array..."  "poi..."  "func..."

另外需要说明的是,之所以在第一次回归到dcl函数时没有打印两次"pointer to",
是因为此时ns的值为1,并不是2,因为每次进入dcl函数时ns都被重置为0,除非两
个'*'字符是一起输入的,才会打印两次,如果它们之间有其他非空格字符,那么就
会分开打印

关于这两题,以我目前的能力,我不会....,想了很久,发现这两题要检查的条件太
多了,const后可以接数据类型,也可以在指针后面接const,而函数参数除了常见数
据类型外,还有指针、数组、函数指针....改了几遍,发现最后无法正常运行了,心
态崩了,一边学一边改吧
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define MAXTOKEN 100
#define BUFSIZE 100
#define DATATYPE "int short long char float double void"

enum {NAME, PARENS, BRACKETS};


int tokentype;//最后一次输入的符号类型
char token[MAXTOKEN];//最后一个符号字符串
char name[MAXTOKEN];//标识符名
char datatype[MAXTOKEN];//数据类型,包括char、int等
char out[1000];
char buf[BUFSIZE];
int bufp = 0;

void dcl(void);
void dirdcl(void);
int gettoken(void);

int main(void)
{
	int const_mask = 0;

	while(gettoken() != EOF)//读取第一个输入的符号
	{
		if(!strcmp(token, "const") && !const_mask)
		{
			const_mask = 1;
			strcpy(datatype, "const ");
			gettoken();
		}

		if(strstr(DATATYPE, token) != NULL)//程序只能接受单名的数据类型
			strcat(datatype, token);//正常情况下声明的第一个符号是数据类型,即int、long、char等,但是为了排除错误,加入一条判断
		else
			printf("can't identify the \"%s\" datatype\n", token);

		out[0] = '\0';
		
		dcl();        //解析声明的其余部分
		if(tokentype != '\n')
			printf("syntax error\n");

		printf("%s: %s %s\n", name, out, datatype);
		const_mask = 0;
		datatype[0] = '\0';
	}
	return 0;
}

int gettoken(void)//返回下一个符号
{
	int c, getch(void);
	void ungetch(int);
	char *p = token;//指针p每次进入函数时都是指向token数组的首元素

	while((c = getch()) == ' ' || c == '\t')
		;
	if(c == '(')
	{
		if((c = getch()) == ')')
		{
			strcpy(token, "()");//当读取的'('后面紧跟')'时,说明这是一个函数标记
			return tokentype = PARENS;
		}

		else
		{
			ungetch(c);
			return tokentype = '(';
		}
	}
	else if(c == '[')
	{
		for(*p++ = c; (*p++ = getch()) != ']'; )//从[开始读取,直到读取到]退出循环
			;
		*p = '\0';
		return tokentype = BRACKETS;//返回中括号标记
	}
	else if(isalpha(c))
	{
		for(*p++ = c; isalnum(c = getch()); )//读取整个标识符名
			*p++ = c;
		*p = '\0';
		ungetch(c);
		return tokentype = NAME;//返回变量名或者类型名标记
	}
	else
		return tokentype = c;
}

void dcl(void)//解析一个声明
{
	int ns;
	int const_mask = 0;
	for(ns = 0; gettoken() == '*' || !strcmp(token, "const"); )//计算*的数量
	{
		if(tokentype == '*')
		{
			ns++;
			const_mask = 0;
		}
		else
		{
			if(ns > 0 && !const_mask)
				const_mask = 1;
			else
				printf("\"const\" location error\n");
		}
	}
	dirdcl();
	while(ns-- > 0)
	{
		if(const_mask)
			strcat(out, " const pointer to");
		else
			strcat(out, " pointer to");
	}
}

void dirdcl(void)//解析直接声明符
{
	int type;
	if(tokentype == '(')
	{
		dcl();
		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)
		if(type == PARENS)
			strcat(out, " function returning");
		else
		{
			strcat(out, " array");
			strcat(out, token);
			strcat(out, " of");
		}
}

int getch(void)
{
	return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)
{
	if(bufp >= BUFSIZE)
		printf("ungetch: too many characters\n");
	else
		buf[bufp++] = c;
}

5-19:
修改undcl程序,使它在把文字转换为声明的过程中不会生成多余的圆括号

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#define MAXTOKEN 100
#define BUFSIZE 100
#define DATATYPE "int short long char float double void"

enum {NAME, PARENS, BRACKETS};


int tokentype;//最后一次输入的符号类型
char token[MAXTOKEN];//最后一个符号字符串
char name[MAXTOKEN];//标识符名
char datatype[MAXTOKEN];//数据类型,包括char、int等
char out[1000];
char buf[BUFSIZE];
int bufp = 0;

void undcl(void);
int gettoken(void);
void delete_parens(char *s);//删除多余的圆括号

int main(void)
{
	int type, prev_type;
	char temp[MAXTOKEN];

	while(gettoken() != EOF)
	{
		strcpy(out, token);
		prev_type = NAME;
		while((type = gettoken()) != '\n')
		{
			if(type == PARENS ||type == BRACKETS)
				strcat(out, token);
			
			else if(type == '*')
			{
				if(prev_type == '*')
					sprintf(temp, "*%s", out);
				else
					sprintf(temp, "(*%s)", out);
				strcpy(out,temp);
			}
			
			else if(type == NAME)
			{
				if(strstr(DATATYPE, token) == NULL)
					printf("can't identify \"%s\" datatype\n", token);
				
				if(prev_type == '*')
					delete_parens(out);
				
				sprintf(temp, "%s %s", token, out);
				strcpy(out, temp);
			}
			
			else
				printf("invalid input at %s\n", token);
			
			prev_type = type;
		}
		printf("%s\n", out);
	}
	return 0;
}

void delete_parens(char *s)
{
	if(*s == '(')
	{
		while(*(s + 1) != '\0')
		{
			*s++ = *(s + 1);
		}
		*--s = '\0';
	}
}

int gettoken(void)//返回下一个符号
{
	int c, getch(void);
	void ungetch(int);
	char *p = token;//指针p每次进入函数时都是指向token数组的首元素

	while((c = getch()) == ' ' || c == '\t')
		;
	if(c == '(')
	{
		if((c = getch()) == ')')
		{
			strcpy(token, "()");//当读取的'('后面紧跟')'时,说明这是一个函数标记
			return tokentype = PARENS;
		}

		else
		{
			ungetch(c);
			return tokentype = '(';
		}
	}
	else if(c == '[')
	{
		for(*p++ = c; (*p++ = getch()) != ']'; )//从[开始读取,直到读取到]退出循环
			;
		*p = '\0';
		return tokentype = BRACKETS;//返回中括号标记
	}
	else if(isalpha(c))
	{
		for(*p++ = c; isalnum(c = getch()); )//读取整个标识符名
			*p++ = c;
		*p = '\0';
		ungetch(c);
		return tokentype = NAME;//返回变量名或者类型名标记
	}
	else
		return tokentype = c;
}


int getch(void)
{
	return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)
{
	if(bufp >= BUFSIZE)
		printf("ungetch: too many characters\n");
	else
		buf[bufp++] = c;
}
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值