C 程序设计语言——第五章练习题

C 程序设计语言第二版——第五章练习题

1. As written, getint treats a + or - not followed by a digit as a valid representation of zero. Fix it to push such a character back on the input.

分析:
感觉这个函数功能就跟 scanf () 输入 int一样。

#include<stdio.h>
#include<ctype.h>
#define SIZE 100
int getch(void);
void ungetch(int);
int getint(int *pn);
int main(void)
{
	int a[SIZE], n;
	if (getint(&n) > 0)
		printf("n = %d\n",n);
	else
		printf("Not an integer.\n");
	return 0;
}
//getch() ungetch()
#define BUFSIZE 10
int bufp = 0;
char buf[BUFSIZE];
int getch(void)
{
	return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int n)
{
	if (bufp >= BUFSIZE)
		printf("Error: too many characters.\n");
	else
		buf[bufp++] = n;
}
//getint()
int getint(int *pn)
{
	int c, c1, i, sign;
	while (isspace(c = getch()))
		;
	if (!isdigit(c) && c != '+' && c != '-' && c != EOF) {
		ungetch(c);
		return 0;
	}
	if (c == EOF)
		return c;
	sign = (c == '-') ? -1 : 1;
	if (c == '-' || c == '+')
		c1 = getch();
	for (*pn = 0, i = 0; isdigit(c1); c1 = getch(), i++)
		*pn = 10 * (*pn) + c1 - '0';
	if (i == 0) {
		ungetch(c);
		ungetch(c1);
		return 0;
	}
	*pn = *pn * sign;
	if (c1 != EOF)
		ungetch(c1);
	return c1;
}

输出:

-0932.3d
n = -932

2. Write getfloat, the floating-point analog of getint. What type does getfloat return as its function value?

分析:
觉得这个函数就跟 atof() 函数一个功能,将输入的浮点字符转换为浮点数。
正确识别返回 1,非法字符返回 0, 遇到 EOF 返回 EOF。

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<ctype.h>
int getch(void);
void ungetch(int);
int getfloat(double *pn);
int main(void)
{
	double n;
	if (getfloat(&n) == 1)
		printf("n = %.8g\n", n);
	else
		puts("Not a number");
	return 0;
}
//getch() ungetch()
#define BUFSIZE 40
int bufp = 0;
char buf[BUFSIZE];
int getch(void)
{
	return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c)
{
	if (bufp >= BUFSIZE)
		printf("Error: too many characters.\n");
	else
		buf[bufp++] = c;
}
// getfloat()
int getfloat(double *pn)
{
	int sign, sign_e;
	int hasint = 0; //判断小数点前是否有数字
	int hassign = 0;//判断是否有符号
	double intpart = 0.0;//小数的整数部分
	double decpart = 0.0;//小数的小数部分
	int exp = 0;//指数部分
	int decdigit = 1;//小数的小数点后数字位置
	int c, c1, c2;
	while (isspace(c = getch()))
		;
	if (!isdigit(c) && c != '-' &&  c != '+' && c != '.' && c != EOF) {
		ungetch(c);
		return 0;
	}
//	if (c == EOF)
//		return c;
	sign = (c == '-') ? -1 : 1;
	if (c == '-' || c == '+') {
		c1 = getch();
		if (!isdigit(c1) && c1 != '.' && c1 != EOF){
			ungetch(c);
			ungetch(c1);
			return 0;
		}
		c = c1;
		hassign = 1;
	} // c 只能是数字或点号, EOF
	//如果 c 是数字,计算小数的整数部分
	for (intpart = 0.0; isdigit(c); c = getch()) {
		intpart = 10 * intpart + c - '0';
		hasint = 1;	
	}
	// c 可能是非法字符,科学计数符 e 或 E, 小数点, EOF
	//如果是非法字符,那已经扫描到数字
	if (c != '.' && c != 'e' && c != 'E' && c != EOF) {
		ungetch(c);
		*pn = sign * intpart;
		return 1;
	}
	//如果是小数点
	if (c == '.') {
		c1 = getch();
		//如果小数点前面没数字,后面也没数字,则非法
		if (hasint == 0 && !isdigit(c1)) {
			if (hassign == 1) {
				c2 = (sign == -1) ? '-' : '+';
				ungetch(c2);
			}
			ungetch(c);
			ungetch(c1);
			return 0;
		}
		//小数点前有数字,后面非法
		else if (hasint == 1 && !isdigit(c1) && c1 != 'e' && c1 != 'E') {
			if (c1 != EOF)
				ungetch(c1);
			*pn =  sign * intpart;
			return 1;
		}
		//小数点后数字或者e(小数点前有数字)
		//计算小数部分
		for (decpart = 0.0; isdigit(c1); c1 = getch())
			decpart = (c1 - '0') / pow(10, decdigit++);
		c = c1;
	}
	//小数部分计算完成
	//如果扫描到 e
	hassign = 0;
	if (c == 'e' || c == 'E') {
		if ((c1 = getch()) == '-' || c1 == '+') 
			hassign = 1;
			sign_e = (c == '-') ? -1 : 1;
			c2 = getch();
			if (!isdigit(c2)) {
				if (hassign == 1) {
					ungetch(c);
					ungetch((sign_e == -1) ? '-' : '+');
				}
				if (c2 != EOF)
					ungetch(c2);
				*pn = sign * (intpart + decpart);
				return 1;
			}
			c = c2;
	}
	// 计算指数部分
	for (exp = 0; isdigit(c); c = getch())
		exp = exp * 10 + c - '0';
	if (c != EOF)
		ungetch(c);
	exp = sign_e * exp;
	*pn = sign * (intpart + decpart) * pow(10, exp);
	return 1;
}

输出:

-.3e2
n = -0.3

3. Write a pointer version of the function strcat that we showed in Chapter 2: strcat(s,t) copies the string t to the end of s.

分析:
添加一个参数 n 用于说明连接的最大字符串个数,检查是否超出范围。

#include<stdio.h>
#include<string.h>
#define SIZE 100
#define LEN 40
int input(int min, int max);
char *s_gets(char *s, int n);
char *mystrncat(char *s, char *t, int n);
int main(void)
{
	char s[SIZE], t[LEN];
	int len1, len2, maxlen, n;
	printf("Please enter the source string:\n");
	s_gets(s, SIZE);
	printf("Please enter the target string:\n");
	s_gets(t, LEN);
	len1 = strlen(s);
	len2 = strlen(t);
    maxlen = (SIZE - len1 -1) > len2 ? len2 : SIZE - len1 -1;
	printf("Please enter the numbers of characters to cat (0 ~ %d): ",maxlen);
	n = input(0, maxlen);
	mystrncat(s, t, n);
	printf("New string:\n%s\n",s);
	return 0;
}
char *s_gets(char *s, int n)
{
	char *ret, *find;
	ret = fgets(s, n, stdin);
	if (ret) {
		find = strchr(s, '\n');
		if (find)
			*find = '\0';
		else
			while (getchar() != '\n')
				continue;
	}
	return ret;
}
int input(int min, int max)
{
	int n, status;
	int ok = 1;
	while (ok) {
		status = scanf("%d", &n);
		if (status != 1 || n < min || n > max || getchar() != '\n' ) {
			printf("Enter again: ");
			while (getchar() != '\n')
				continue;
			continue;
		}
		else
			return n;
	}
}
char *mystrncat(char *s, char *t, int n)
{
	int i, j;
	for (i = strlen(s), j = 0; j < n; *(s+i) = *(t+j),i++, j++)
		;
	if (n < strlen(t))
		*(s+i) = '\0';
	return s;
}

4. Write the function strend(s,t), which returns 1 if the string t occurs at the end of the string s, and zero otherwise.

#include<stdio.h>
#include<string.h>
#define LEN 50
char *s_gets(char *s, int n);
int strend(char *s, char *t);
int main(void) 
{
	char s[LEN], t[LEN];
	int n;
	printf("Please enter the source string:\n");
	s_gets(s, LEN);
	printf("Please enter the target string:\n");
	s_gets(t, LEN);
	n = strend(s, t);
	if (n == 1)
		printf("%s is at the end of %s.\n", t, s);
	else
		printf("%s is not at the end of %s.\n", t, s);
	return 0;
}
char *s_gets(char *s, int n)
{
	char *ret, *find;
	ret = fgets(s, n, stdin);
	if (ret) {
		find = strchr(s, '\n');
		if (find)
			*find = '\0';
		else
			while (getchar() != '\n')
				continue;
	}
	return ret;
}
int strend(char *s , char *t)
{
	int len1 = strlen(s);
	int len2 = strlen(t);
	if (len2 > len1)
		return 0;
	int i, j;
	for (i = len1-len2, j = 0; (j < len2) && (s[i] == t[j]); i++, j++)
		;
	if (j != len2)
		return 0;
	else
		return 1;
}

5. Write versions of the library functions strncpy, strncat, and strncmp, which operate on at most the first n characters of their argument strings. For example, strncpy(s,t,n) copies at most n characters of t to s. Full descriptions are in Appendix B.

#include<stdio.h>
#include<string.h>
#include<ctype.h>
#define LEN 100
char *s_gets(char *s , int n);
int input(int min, int max);
char getlet(char *s);
void menu(void);
char *mstrncpy(char *s, char *t, int n);
char *mstrncat(char *s, char *t, int n);
int mstrncmp(char *s, char *t, int n);
int main(void)
{
	char s[LEN], t[LEN];
	int n;
	char c;
	printf("Please enter the source string:\n");
	s_gets(s, LEN);
	printf("Please enter the target string:\n");
	s_gets(t, LEN);
	printf("Please enter the number of characters to be processed: ");
	n = input(0, LEN-1);
	menu();
	c = getlet("abc");
	switch(c) {
		case 'a': mstrncpy(s, t, n); break;
		case 'b': mstrncat(s, t, n); break;
		case 'c': mstrncmp(s, t, n); break;
	}
	return 0;
}
char *s_gets(char *s, int n)
{
	char *ret, *find;
	ret = fgets(s, n, stdin);
	if (ret) {
		find = strchr(s, '\n');
		if (find)
			*find = '\0';
		else
			while (getchar() != '\n')
				continue;
	}
	return ret;
}
int input(int min, int max)
{
	int n, status;
	int ok = 1;
	while (ok) {
		status = scanf("%d", &n);
		if (status != 1 || n < min || n > max || getchar() != '\n') {
			while (getchar() != '\n')
				continue;
			printf("Enter again (%d ~ %d): ", min, max);
			continue;
		}
		return n;
	}
}
void menu(void)
{
	puts("Choose your choice:");
	puts("a) strncpy	b) strncat");
	puts("c) strncmp");
}
char getlet(char *s)
{
	char c;
	int ok = 1;
	while (ok) {
	c = getchar();
	c = tolower(c);
	if (strchr(s, c))
		return c;
	printf("Enter again: ");
	}
}
char *mstrncpy(char *s, char *t, int n)
{
	int len, i;
	len = strlen(t);
	for (i = 0; i < n && t[i] != '\0'; i++)
		s[i] = t[i];
	s[i] = '\0';
	printf("Source string: %s\n",s);
	return s;
}
char *mstrncat(char *s, char *t, int n)
{
	int len1, len2, maxlen;
	int i, j;
	len1 = strlen(s);
	len2 = strlen(t);
	n = (n > len2) ? len2 : n;
	maxlen = LEN - len1 - 1;
	if (n > maxlen) {
		printf("Beyond the range, enter the number of characters to be" 
		" catenated (0 ~ %d) again: ",maxlen);
		n = input(0, maxlen);
	} 
	for (i = len1, j = 0; j < n; s[i] = t[j], i++, j++)
		;
	s[i] = '\0';
	printf("Source string: %s\n",s);
	return s;
}
int mstrncmp(char *s, char *t, int n)
{
	int i;
	for (i = 0; i < n && s[i] == t[i]; i++)
		;
		if (i == n) {
			printf("The first %d characters of %s and %s are the" 
			" same.\n",n,s,t);
			return 0;
		}
		else if (s[i] > t[i]) 
			printf("%s precede %s alphabetically.\n",t,s);
		else
			printf("%s precede %s alphabetically.\n",s,t);
		return s[i] -t[i];
}

6. Rewrite appropriate programs from earlier chapters and exercises with pointers instead of array indexing. Good possibilities include getline (Chapters 1 and 4), atoi, itoa, and their variants (Chapters 2, 3, and 4), reverse (Chapter 3), and strindex and getop (Chapter 4).

将书中的指针数组改为二位数组,存放字符串内容而非地址。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAXLINES 5000
#define MAXLEN 100
int mgetline(char *a, int n);
int readlines(char (*line)[MAXLEN], int maxlines);
void mqsort(char (*a)[MAXLEN], int left, int right);
void writelines(char (*a)[MAXLEN], int maxlines);
int main(void)
{
	int nlines;
	char line[MAXLINES][MAXLEN];
	printf("Enter strings (empty line to stop):\n");
	if ((nlines = readlines(line, MAXLINES)) > 0) {
		mqsort(line, 0, nlines-1);
		printf("\nSorted strings:\n");
		writelines(line, nlines);
		return 0;
	}
	else {
		printf("Error: no input.\n");
		return -1;
	}
}
int mgetline(char *a, int n)
{
	int i, c;
	for (i = 0; i < n && (c = getchar()) != EOF && c != '\n'; i++)
		*(a+i) = c;
	*(a+i) = '\0';
	return i;
}
int readlines(char line[][MAXLEN], int maxlines)
{
	int nlines, len;
	char a[MAXLEN];
	for (nlines = 0; nlines < maxlines && (len = mgetline(a, MAXLEN-1)) > 0; nlines++) 
		strcpy(line[nlines], a);
	return nlines;
}
//快速排序
#define cutoff 3
void swap(char (*s1)[MAXLEN], char (*s2)[MAXLEN])
{
	char t[MAXLEN]; //这里交换字符串,是一个以为数组,不能直接赋值
	strcpy(t, *s1);
	strcpy(*s1, *s2);
	strcpy(*s2, t);
}
char *Median3(char (*a)[MAXLEN], int left, int right)
{
	int center = (left + right) / 2;
	if (strcmp(a[left], a[center]) > 0)
		swap(&a[left], &a[center]);
	if (strcmp(a[left], a[right]) > 0)
		swap(&a[left], &a[right]);
	if (strcmp(a[center], a[right]) > 0)
		swap(&a[center], &a[right]);
	swap(&a[center], &a[right-1]); //pivot == a[right-1]
	return a[right-1];
}
void InsertionSort(char (*a)[MAXLEN], int nlines)
{
	int i, p;
	char tmp[MAXLEN];
	for (p = 1; p < nlines; p++) {
		strcpy(tmp, a[p]);
		for (i = p; i > 0 && strcmp(a[i-1], tmp) > 0; i--)
			strcpy(a[i], a[i-1]);
		strcpy(a[i], tmp);
	}
}
void mqsort(char (*a)[MAXLEN], int left, int right)
{
	int i, j;
	if (left + cutoff <= right) {
	char *pivot;
	pivot = Median3(a, left, right);
	i = left;
	j = right - 1;
	for (; ; ) {
		while (strcmp(a[++i], pivot) < 0) 
			;
		while (strcmp(a[--j], pivot) > 0)
			;
		if (i < j)
			swap(&a[i], &a[j]);
		else
			break;
	}
	swap(&a[i], &a[right-1]);
	mqsort(a, left, i-1);
	mqsort(a, i+1, right);
	}
	else
		InsertionSort(a, right - left + 1);
}
//writelines
void writelines(char (*a)[MAXLEN], int maxlines)
{
	int i;
	for (i = 0; i < maxlines; i++) 
		puts(a[i]);
}

输出:

Enter strings (empty line to stop):
sja
d
ddj
ds
cj


Sorted strings:
cj
d
ddj
ds
sja

8 There is no error checking in day_of_year or month_day. Remedy this defect.

需要实现功能是:
输入年,月,日,计算该日是该年的第几天。
输入年,这一年的第几天,计算月份和该月的天数。
输入超过范围时提示错误并重新输入。

#include<stdio.h>
#include<limits.h>
#include<string.h>
char day_tab[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 input(int min, int max);
int day_of_year(int leap, int month, int m_day); 
void month_day(int leap, int day, int *pmonth, int *m_day); 
int main(void)
{
	int year, month, m_day, day;
	int leap;
	int day_max;
	printf("Please enter the year: ");
	year = input(1, INT_MAX);
	printf("Enter the month: ");
	month = input(1, 12);
	leap = year%4 == 0 && year/100 != 0 || year%400 == 0;//true for leap year
	day_max = day_tab[leap][month];
	printf("Now enter the month day (1 ~ %d): ",day_max);
	m_day = input(1, day_max);
	day = day_of_year(leap, month, m_day);
	printf("\nThe day %d-%d-%d is %d day of %d.\n", year,month,m_day,day,year);
	printf("\nEnter the year: ");
	year = input(1, INT_MAX);
	leap = year%4 == 0 && year/100 != 0 || year%400 == 0;//true for leap year
	day_max = (leap == 0) ? 365 : 366;
	printf("Enter the day of year: ");
	day = input(1, day_max);
	month_day(leap, day, &month, &m_day);
	printf("year: %d, day of year: %d, month: %d, day of month: " 
	"%d\n",year,day,month,m_day);
	return 0;
}
int input(int min, int max)
{
	int status;
	long int n;
	int ok = 1;
	while (ok) {
		status = scanf("%ld",&n);
		if (status != 1 && n < min && n > max && getchar() != '\n') {
			printf("Enter again (%d ~ %d): ",min, max);
			while (getchar() != '\n')
				continue;
			continue;
		}
		else
			return (int)n;
	}
}
int day_of_year(int leap, int month, int m_day) 
{
	int i;
	int day = m_day;
	for (i = 1; i < month; i++) 
		day += day_tab[leap][i];
	return day;
}
void month_day(int leap, int day, int *pmonth, int *pm_day) 
{
	int i, j;
	for (i = 1; (day - day_tab[leap][i]) > 0; i++) 
		day -= day_tab[leap][i];
	*pmonth = i;
	*pm_day = day;
}

9. Rewrite the routines day_of_year and month_day with pointers instead of indexing.

分析:原来用二位数组存放月份天数:

char day_tab[2][13];

两行分别表示平年和闰年的月份,使用时操作数组行列。

现在,用指针操作,只需要一个一维数组存放每月的天数,而平年和闰年通过一个一个指针修改二月份天数。因此变成:

char month_tab[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
char *day_tab = month_tab; 

判断是否闰年:

leap = year%4 == 0 && year%100 != 0 || year%400 == 0;
*(day_tab+2) = leap == 0 ? 28 : 29;
#include<stdio.h>
#include<limits.h>
char month_tab[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
char *day_tab = month_tab; 
int input(int min, int max);
int day_of_year(int month, int m_day);
void month_day(int day, int *pmonth, int *pm_day);
int main(void)
{
	int year, month, m_day, day;
	int day_max;
	int leap;
	printf("Enter the year: ");
	year = input(1, INT_MAX);
	leap = year%4 == 0 && year%100 != 0 || year%400 == 0;
	*(day_tab+2) = leap == 0 ? 28 : 29;
	printf("Enter the month: ");
	month = input(1, 12);
	printf("Enter the number of days in month %d: ",month);
	day_max = *(day_tab + month);
	m_day = input(1, day_max);
	day = day_of_year(month, m_day);
	printf("\nThe number of days of %d-%d-%d in %d is: "  
	"%d.\n",year,month,m_day,year,day);
	printf("\nEnter the year: ");
	year = input(1, INT_MAX);
	leap = year%4 == 0 && year%100 != 0 || year%400 == 0;
	*(day_tab+2) = leap == 0 ? 28 : 29;
	printf("Enter the day of year: ");
	day = input(1, leap == 0 ? 365 : 366);
	month_day(day, &month, &m_day);
	printf("%d in %d is %d-%d-%d.\n",day, year, year, month, m_day);
	return 0;
}
int input(int min, int max)
{
	int status;
	long int n;
	int ok = 1;
	while (ok) {
		status = scanf("%ld", &n);
		if (status != 1 && n < min && n > max && getchar() != '\n') {
			printf("Enter again (%d ~ %d): ",min, max);
			while (getchar() != '\n')
				continue;
			continue;
		}
		else
			return (int)n;
	}
}
int day_of_year(int month, int m_day)
{
	int i;
	int day = m_day;
	day_tab++;
	for (i = 1; i < month; i++) 
		day += *day_tab++;
	return day;
}
void month_day(int day, int *pmonth, int *pm_day)
{
	*pmonth = 1;
	day_tab = month_tab + 1;
	int m, d;
	while (day / *day_tab) {
		day -= *day_tab++;
		(*pmonth)++;
	}
	*pm_day = day;
}

结果:

Enter the year: 2019
Enter the month: 10
Enter the number of days in month 10: 27

The number of days of 2019-10-27 in 2019 is: 300.

Enter the year: 2019
Enter the day of year: 300
300 in 2019 is 2019-10-27.

10. Write the program expr, which evaluates a reverse Polish expression from the command line, where each operator or operand is a separate argument. For example,

expr 2 3 4 + *
evaluates 2 * (3+4).

#include<stdio.h>
#include<ctype.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#define NUMBER 'N'
#define FUNC 'F'
int getop(char *s);
void push(double f);
double pop(void);
void mathfunc(char *s);
int main(int argc, char *argv[])
{
	if (argc == 1) {
		printf("Usage: expression\n");
		return 0;
	}
	int i, op;
	double op2, sum;
	for (i = 1; i < argc; i++) {
		op = getop(argv[i]);
		switch (op) {
			case NUMBER:
					push(atof(argv[i]));
					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("Zero divisor\n");
					break;
			case '%':
					op2 = pop();
					if (op2 != 0.0)
						push(fmod(pop(),op2));
					else
						printf("Zero divisor\n");
					break;
			case FUNC:
					mathfunc(argv[i]);
					break;
			default:
					printf("Error: unknown command %s\n",argv[i]);
					break;
		}
	}
	sum = pop();
	printf("%.8g\n",sum);
	return 0;
}
//push() pop()
#define MAXVAL 100
int sp = 0;
double val[MAXVAL];
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;
	}
}
//math function
void mathfunc(char *s)
{
	double op2;
		if (strcmp(s, "sin") == 0) 
			push(sin(pop()));
		else if (strcmp(s, "cos") == 0) 
			push(cos(pop()));
		else if (strcmp(s, "tan") == 0)
			push(tan(pop()));
		else if (strcmp(s, "pow") == 0) {
			op2 = pop();
			push(pow(pop(), op2));
		}			
		else if (strcmp(s, "exp") == 0) 
			push(exp(pop()));
}
//getop()
int getop(char *s)
{
	int i;
	int isnum = 0;
	if (!isdigit(s[0]) && s[0] != '.' && s[0] != '-') {
		if (strcmp(s,"sin") == 0 || strcmp(s,"cos") == 0\
		|| strcmp(s,"tan") == 0 || strcmp(s,"pow") == 0 \
		|| strcmp(s, "exp") == 0)
		return FUNC;
	else if (strcmp(s, "+") == 0)
		return '+';
	else if (strcmp(s, "*") == 0)
		return '*';
	else if (strcmp(s, "/") == 0)
		return '/';
	else if (strcmp(s, "%") == 0)
		return '%';
	else
		return -1;
	}
	if (strcmp(s, "-") == 0)
		return '-';
	i = 0;
	if (s[0] == '-')
		while (isdigit(s[++i]))
			isnum = 1;
	if (isdigit(s[i])) {
		isnum = 1;
		while (isdigit(s[++i]))
			isnum = 1;
	}
	if (s[i] == '.')
		while (isdigit(s[++i]))
			isnum = 1;
	if (isnum == 0 || s[i] != '\0')
		return -1;
	else
		return NUMBER;
}

输出:

./expr 2 3 - sin 2 + 
1.158529

11. Modify the program entab and detab (written as exercises in Chapter 1) to accept a list of tab stops as arguments. Use the default tab settings if there are no arguments.

detab

对于 detab 函数,将输入中的制表符换成自定义的n个空格。
分析:

1. tab 规则:

对于 tab 的规则,本系统上是8个空格,tab 有对齐功能,如:
//tab 测试程序
#include<stdio.h>
#include<string.h>
int main(void)
{
	printf("\npress [tab]:\na	dj	djs	sdjl	jdl	djs\n");
	printf("a	jd	sjd	ksdj	kskdl	\n");
	printf("\ninput \t:\n");
	printf("a\tab\tabc\tabcd\tabcdefg\tabcdefgh\tabcd\n");
	return 0;
}

输出:

press [tab]:
a	dj	djs	sdjl	jdl	djs	
a	jd	sjd	ksdj	kskdl	

input 	:
a	ab	abc	abcd	abcdefg	abcdefgh        abcd

根据结果可知,tab 结束位置为8的倍数。

2. 实现功能:

题目需要实现的 detab 的功能是通过命令行输入tab的参数,可以输入多个,如无参数,则按照默认的是8的倍数,如输入参数8,17,则tab停止的位置分别8和17。实现功能有两种:
a. 当输入一行字符串按 [enter] 换行后,输出将 tab 替换后的字符串。
b. 输入多行字符串(以空行结束输入),完成后将全部字符串替换输出。

3. 思路:

  1. 约定: 当遇见一个制表符 \t 时,该位置为 tab start,从该位置开始填充空格,最后一个空格位置为 tab stop。

    认为第一个位置下标为1,则如果 tab 值为8,tab stop 的位置为8的倍数位置。

    即遇到一个制表符,从该位置填充空格直到下一个 tab stop的位置。如:tab stop为3,则空格填充位置为 3 ~ 8,第9个位置无空格。如果tab stop 为7,则空格填充 7 ~ 8两个空格。

  2. 如果输入一行立马反馈将该行替换后输出,可以用 getchar() 获取单个字符,用一个变量记录位置(个数),遇到制表符时,查看当前位置以及下一个 tab stop 位置填充空格。

    如果通过命令行输入 tab stop 位置,则需要一个数组存放位置状态,这样当输入第 n 个字符为制表符时,查看数组对应的第 n 个元素状态,如果为 tab stop 位(如设置1为tab stop,0为其他),则putchar输出空格,结束后继续扫描后面字符。

    需要解决是存放tab 状态的数组尺寸设置多大,可以自定义一个尺寸如 len,如果输入长度超过 len,则换行,下标重置,这样如果输入tab stop 参数大于 len 就没用到。

    另一种可以先找出输入参数最大的位置数,以该位置长度为一行最大的长度。

  3. 如果输入多行结束后一次输出,则需要将输入的字符串存起来,可以用指针数组存储,数组每个元素对应输入的每行字符串的首字符地址。

    假设输入参数最后一个 tab stop 位为25,则每行尺寸为26(25+1)(注意先检查参数是否合法,非数字错误提示并推出)

    指针数组的大小:数组元素的个数为输入的行数,自定义一个值 LINE,则输入行数达到个数或者遇到空行时结束输入。

    一个 tab 状态一维数组,尺寸为 25,设置状态位,1为stop,0为其他。

    每一行的处理:
    (1)如果设定的一行最大值为len,则如果输入的一行超过len,则分成多行处理,赋值给多个指针。
    (2)同上,如果一行超过分成多行输出,但仍然算作一行赋值给一个指针,即扫描到一个换行符才算一行,nlines加1。

    第一种好处理,扫描达到数目后,如没有换行符,添加空结束符,如有换行符,换行符替换为空结束符。但这样可能会导致多余的输入的字符未输出。
    第二种需要改 getline 函数,如果数目达到但未扫描到换行符,在末尾加换行符,然后另分配空间继续扫描,直到扫到换行符。将几个数组合并(按顺序在数组末尾添加空结束符变成字符串,再用 strcat 函数,后面的字符串会覆盖之前的空结束符)为一个字符串赋给指针,这样nlines加1。

    程序实现第一种,第二种以后补充。

4. 程序

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#define MAXLINES 100
int checkargs(int argc, char *argv[]);
int args_max(int argc, char *argv[]);
void settab(char *tab, int maxlen, int argc, char *argv[]);
int mgetline(char *s, char *tab, int maxlen);
void writelines(char *lineptr[], int nlines);
int main(int argc, char *argv[])
{
	char *lineptr[MAXLINES];
	int maxlen;//maxlen为一行字符数,不包括换行符,空结束符,实际存储字符串要加1
	//判断命令行参数
	if (argc == 1) 
		maxlen = 40;
	else {
		int ok;
		ok = checkargs(argc, argv);
		if (ok == 0) {
			fprintf(stdout, "Error: nonumeric arguments\n");
			exit(EXIT_FAILURE);
		}
		else 
			maxlen = args_max(argc, argv);
	}
	//设置 tab stop 
	char tab[maxlen];
	settab(tab, maxlen, argc, argv);
	//读字符串
	int nlines = 0;
	char line[maxlen+1];
	int len;
	printf("Input stings (maximum lines and character size : %d," 
	"%d):\n",MAXLINES, maxlen);
	while (nlines < MAXLINES && (len = mgetline(line, tab, maxlen)) >  0) {
			lineptr[nlines] = malloc((len+1) * sizeof(char));
			strcpy(lineptr[nlines++], line);
		}
	printf("Strings with detab:\n");
	writelines(lineptr, nlines);
	//释放内存
	for (int i = 0; i < nlines; i++)
		free(lineptr[i]);
	return 0;
}
int checkargs(int argc, char *argv[])
{
	int i, j;
	int ok = 1;
	for (i = 1; i < argc; i++) {
		for(j = 0; *(argv[i]+j) != '\0'; j++) 
			if (!isdigit(*(argv[i]+j)))
				return 0;
	}
	return 1;
}
int args_max(int argc, char *argv[])
{
	int i;
	int max;
	for (max = atoi(argv[1]),i = 2; i < argc; i++) 
		if (atoi(argv[i]) > max)
			max = atoi(argv[i]);
	return max;
}
void settab(char *tab, int maxlen, int argc, char *argv[])
{
	int i, pos;
	if (argc == 1) 
		for (i = 0; i < maxlen; i++)
			tab[i] = (i+1) % 8 == 0 ? 1 : 0;
	else {
		for (i = 0; i < maxlen; i++)
			tab[i] = 0;
		for (i = 1; i < argc; i++) {
			pos = atoi(argv[i]);
			tab[pos-1] = 1;
		}
	}
}
//mgetline 存储一行数据
int mgetline(char *s, char *tab, int n) 
{
	int i, c;
	for (i = 0; i < n && (c = getchar()) != EOF && c != '\n'; i++) {
		if (c == '\t') {
			while (tab[i] != 1 && i+1 < n)
				s[i++] = ' ';
			s[i] = ' ';
		}
		else 
			s[i] = c;
	}
		s[i] = '\0';
	return i;
}
//writelines
void writelines(char *lineptr[], int nlines)
{
	int i;
	for (i = 0; i < nlines; i++)
		printf("%s\n",lineptr[i]);
}

输出:

./detab.o 3 6 9 12 15 18
Input stings (maximum lines and character size : 100,18):
a	bc	def	ghi	l
ab	cde	frh	jklm	no
pqu	vwxy	z1234	56789	111
23456	32123	321234	323233  ewkl

Strings with detab:
a  bc def   ghi   
l
ab cde   frh   jkl
m  no
pqu   vwxy  z1234 
56789 111
23456 32123 321234
   323233   ewkl

entab

将输入的字符串中的空格替换成最少数目的制表符和空格,最终达到的效果一样,这样减少了字符的数目。

tab stop 的设置和 detab 相同。

这个程序和 detab 程序不同在于 mgetline 函数。当识别到空格时,继续向后扫描,如果后面空格中有 tab stop 位,则第一个空格位置用制表符替换,重复继续,直到空格中无 tab stop位,则仍用空格。

注意:如果扫到一个空格,该字符是该行容纳的最后一个字符,那么如果继续向后扫描,扫描到不能替换的空格以及非空格字符,不能再存在该行,这时应该将多余的字符返回到缓冲区中,可以用 ungetch 函数。而 getch 和 ungetch 的缓冲区大小为 maxlen ,该值要计算,因此定义为外部变量使用。

注意: 如果第一个空格处于 tab stop 位,不替换。

#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<string.h>
#define MAXLINES 100
int maxlen; //创建为外部变量
int checkargs(int argc, char *argv[]);
int max_args(int argc, char *argv[]);
void settab(int *tab, int argc, char *argv[]);
int mgetline(char *s, int *tab, int n);
void writelines(char *lineptr[], int nlines);
int main(int argc, char *argv[])
{
	//检查命令行参数
	if (argc == 1)
		maxlen = 40;
	else {
		int ok;//检查参数是否合法,非数字错误,退出
		if ((ok = checkargs(argc, argv)) == 0) {
			fprintf(stdout, "Error: nonumeric argument(s)\n");
			exit(EXIT_FAILURE);
		}
		else {
			int max = max_args(argc, argv);
			maxlen = max;
		}
	}
	//设置 tab stop
	int tab[maxlen];
	settab(tab, argc, argv);
	//读取字符串输入
	printf("Enter strings (maximum lines: %d, character size: " 
	"%d):\n",MAXLINES, maxlen);
	char *lineptr[MAXLINES];
	int nlines = 0; //实际扫描的行数
	int len; //实际每行扫描的字符数
	char line[maxlen+1]; //临时存放每行字符串
	//当扫描行数未超过且不是空行时循环扫描
	while (nlines < MAXLINES && (len = mgetline(line, tab, maxlen)) > 0) { 
		lineptr[nlines] = (char *)malloc((len + 1) * sizeof(char));
		strcpy(lineptr[nlines++], line);
	}
	printf("Strings with entab:\n");
	writelines(lineptr, nlines);
	//释放内存
	for (int i = 0; i < nlines; i++)
		free(lineptr[i]);
	return 0;
}
int checkargs(int argc, char *argv[])
{
	int i, j;
	for (i = 1; i < argc; i++) 
		for (j = 0; *(argv[i]+j) != '\0'; j++)
			if (!isdigit(*(argv[i]+j)))
				return 0;
	return 1;
}
int max_args(int argc, char *argv[])
{
	int i;
	int max = atoi(argv[1]);
	for (i = 2; i < argc; i++)
		if (atoi(argv[i]) > max)
			max = atoi(argv[i]);
	return max;
}

void settab(int *tab, int argc, char *argv[])
{
	int i;
	if (argc == 1) 
		for (i = 0; i < maxlen; i++) 
			tab[i] = (i+1) % 8 == 0 ? 1 : 0;
	
	else {
		for (i = 0; i < maxlen; i++)
			tab[i] = 0;
		for (i = 1; i < argc; i++)
			tab[atoi(argv[i]) -1] = 1;
	}
}
//getch ungetch
#define BUFSIZE 100
int bufp = 0;
char buf[BUFSIZE];
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;
}
//mgetline 获取字符串并处理
int mgetline(char *s, int *tab, int n)
{
	int i, j, k;//i 为原来下标,j为替换后下标,k临时计算空格
	int c, ct;
	int t_count = 0;
	int stop[maxlen];//存放 tab stop下标
	int x = 0; //stop 下标,值为 tab stop个数
	for (i = 0, j = 0; i < n && (c = getch()) != EOF && c != '\n'; i++) {
		if (c == ' ') {
			for (k = i+1; k < n && (c = getch()) == ' '; k++)
				if (tab[k] == 1) {
					stop[x++] = k;
					t_count++;
				}
			//注意i处如果为 tab stop 位,则当空格
			//ct 为除去制表符后空格的个数
			if (tab[i] == 1) {
				s[j++] = ' ';
				ct = t_count > 0 ? k-1-stop[x-1] : k-1-i;
			}
			else if (tab[i] == 0)
				ct = t_count > 0 ? k-1-stop[x-1] : k-i;
			for (; t_count > 0; t_count--)
				s[j++] = '\t';
			for (; ct > 0; ct--)
				s[j++] = ' ';
			if (k == n) {
				s[j] = '\0';
				ungetch(c);
				return j;
			}
			else if (k < n) {
				s[j++] = c;
				i = k;
			}
		}
		else 
			s[j++] = c;
	}
	s[j] = '\0';
	return j;
}
void writelines(char *lineptr[], int nlines)
{
	int i;
	for (i = 0; i < nlines; i++)
		puts(lineptr[i]);
}

输出:
在这里插入图片描述
注意:
程序如果通过命令行输入tab stop 的参数,要与默认的一样是8的倍数才能与输出一样,否则需要再将有制表符的字符串通过detab输出。

遇到第一个空格时,要判断该空格位置是否是 tab stop 位,如果是,先将空格赋值,如果不是,后面有制表符,该位置才是制表符。对应程序:

//ct 为除去制表符后空格的个数
			if (tab[i] == 1) {
				s[j++] = ' ';
				ct = t_count > 0 ? k-1-stop[x-1] : k-1-i;
			}
			else if (tab[i] == 0)
				ct = t_count > 0 ? k-1-stop[x-1] : k-i;

12. Extend entab and detab to accept the shorthand

entab -m +n
to mean tab stops every n columns, starting at column m. Choose convenient (for the user) default behavior.

需要实现功能,接受特定格式命令行参数,-m 指从m行开始,+n 指每增加 n 行为一个 tab stop 位。

思路:

检查命令行参数,如果没有设置参数,采用默认形式,每8列为tab stop 位,下标从 0 开始,因此,m 为7,n 为8。

如果有参数,检查是否合法,形式只能是 -m, +n,如果参数不合法, 提示非法参数且采用默认参数。

除了检查参数的函数 checkarg 和设置tab stop 的函数 settab 需要修改外,其他不变。

将detab 和 entab 放在一个程序,先输出原本字符串,再经过 entab ,最后 detab 变回原来字符串。

#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<string.h>
#define MAXLINES 100
#define MAXLEN 100
int checkargs(int argc, char *argv[]);
void settab(int *tab, int ok, int argc, char *argv[]);
int mgetline(char *s, int n);
void entab(char *s, int *tab);
void detab(char *s, char *a, int *tab);
void writelines(char *lineptr[], int nlines);
int main(int argc, char *argv[])
{
	int ok = checkargs(argc, argv);
	if (ok == 0)
		printf("No valid tab stop arguments. Default value (-7 +8) is used.\n");
	int tab[MAXLEN];
	settab(tab, ok, argc, argv);
	printf("Enter strings (maximum lines: %d, maximum characters: " 
	"%d):\n",MAXLINES, MAXLEN);
	//获取输入字符串
	int len;
	char line[MAXLEN+1];
	int nlines = 0;
	char *lineptr[MAXLINES];
	while (nlines < MAXLINES && (len = mgetline(line, MAXLEN)) > 0) {
		lineptr[nlines] = (char *)malloc((len+1) * sizeof(char));
		strcpy(lineptr[nlines++], line);
	}
	//输出原始字符串
	printf("\nOriginal strings:\n");
	writelines(lineptr, nlines);
	//用 entab 函数处理字符串空格
	printf("\nStrings processed by entab:\n");
	for (int i = 0; i < nlines; i++) {
		entab(lineptr[i], tab);
		puts(lineptr[i]);
	}
	//用detab还原字符串
	printf("\nStrings processed by detab:\n");
	char a[MAXLEN+1];
	for (int i = 0; i < nlines; i++) {
		detab(lineptr[i], a,  tab);
		puts(a);
	}
	//释放内存
	for (int i = 0; i < nlines; i++)
		free(lineptr[i]);
	return 0;
}
int checkargs(int argc, char *argv[])
{
	int i, j;
	int ok;
	if (argc == 3) { 
		if (*argv[1] == '-' && *argv[2] == '+') {
			for (i = 1; i < argc; i++)
				for (j = 1; argv[i][j] != '\0'; j++) 
					if (!isdigit(argv[i][j])) 
						return 0;
			return 1;
		}
	}
	else 
		return 0;
}
void settab(int *tab, int ok, int argc, char *argv[]) {
	int i, j;
	int m, n;
	if (ok == 0) {
		m = 7;
		n = 8;
	}
	else {
		m = atoi(++argv[1]);
		n = atoi(++argv[2]);
	}
	for (i = 0; i < MAXLEN; i++)
		tab[i] = 0;
	for (i = m; i < MAXLEN; i += n)
		tab[i] = 1;
}
int mgetline(char *s, int n)
{
	int i, c;
	for (i = 0; i < n && (c = getchar()) != EOF && c != '\n'; i++)
		s[i] = c;
	s[i] = '\0';
	return i;
}
void entab(char *s, int *tab)
{
	int i, j, k;
	int len = strlen(s);
	int stop[len];
	int x = 0;
	int t_count = 0;
	int space;
	for (i = 0, k = 0; i < len; i++) {
		if (s[i] == ' ') {
		    //注意,如果该处空格位置为tab stop位,当作空格
			if (tab[i] == 1)
				s[k++] = ' ';
			for (j = i+1; j < len && s[j] == ' '; j++) 
				if (tab[j] == 1) {
					stop[x++] = j;
					t_count++;
				}
			if (j == i+1) //第一个空格后面没有空格
				continue;
			else {
				// i+1 ~ j-1 之间都为空格
				space = (t_count > 0) ? j - 1 - stop[x-1] : j - i - 1;
				for (; t_count > 0; t_count--)
					s[k++] = '\t';
				for (; space > 0; space--)
					s[k++] = ' ';
				if (j == len) 
					break;	
				else {
					s[k++] = s[j];
					i = j;
				}
			}
		}
		else
			s[k++] = s[i];
	}
	s[k] = '\0';
}
void detab(char *s, char *a, int *tab)

{
	int i, j, k;
	int len = strlen(s);
	for (i = 0, k = 0; i < len; i++) {
		if (s[i] == '\t') {
			for (j = k; j < MAXLEN && tab[k] == 0; j++)
				a[k++] = ' ';
			if (j == MAXLEN) 
				break;	
			else 
				a[k++] = ' ';
		}
		else
			a[k++] = s[i];
	}
	a[k] = '\0';
}
void writelines(char *lineptr[], int nlines)
{
	int i;
	for (i = 0; i < nlines; i++)
		puts(lineptr[i]);
}

输出:
如果用默认参数,则entab后输出与原来间隔一致,否则,自己设置参数,entab 后间隙变化(因为与系统制表符个数不统一),但 detab 后还是会变成原样。
在这里插入图片描述

13. Write the program tail, which prints the last n lines of its input. By default, n is set to 10, let us say, but it can be changed by an optional argument so that tail -n prints the last n lines.

The program should behave rationally no matter how unreasonable the input or the value of n. Write the program so it makes the best use of available storage; lines should be stored as in the sorting program of Section 5.6, not in a two-dimensional array of fixed size.

分析:
程序需要实现功能为:打印输入的最后n行,如果没有设置参数n,默认为10。

思路:

  1. 通过命令行参数设置 n。检查参数合法性,非法采用默认值。
  2. 设定接受的最大行数,每行的最大字符数,如果输入一行超过限制字符,是将后面的去掉不要还是换成多行。
    如果多余字符不去掉,多余字符会多占用行,这样输出的行并非真是输入的行。
    如果将多余的字符去掉,也会丢失内容。
    这里用第一种,将多余字符换行。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#define MAXLINES 100
#define MAXLEN 100
int checkargs(int argc, char *argv[]);
int mgetline(char *s, int n);
int main(int argc, char *argv[])
{
	int tail;
	int ok = checkargs(argc, argv);
	if (ok == 0) {
		printf("No valid argument. Default 10 is taken.\n");
		tail = 10;
	}
	else 
		tail = atoi(++argv[1]);
	//获取输入字符串
	printf("Enter strings (maximum rows: %d, maximum columns" 
	" : %d):\n",MAXLINES, MAXLEN);
	char *lineptr[MAXLINES];
	char line[MAXLEN+1];
	int nlines = 0;
	int len;
	while (nlines < MAXLINES && (len = mgetline(line, MAXLEN)) > 0) {
		lineptr[nlines] = (char *)malloc((len+1) * sizeof(char));
		strcpy(lineptr[nlines++], line);
	}
	printf("\nThe last %d lines:\n",tail);
	int i = (nlines-1) - (tail-1) > 0 ? nlines - tail : 0;
	for (; i < nlines; i++)
		puts(lineptr[i]);
	return 0;
}
int checkargs(int argc, char *argv[])
{
	int ok;
	if (argc == 2) {
		if (*argv[1] == '-') {
			for (int i = 1; argv[1][i] != '\0'; i++)
				if (!isdigit(argv[1][i]))
					return 0;
			return 1;
		}
	}
	return 0;
}
int mgetline(char *s, int n)
{
	int i, c;
	for (i = 0; i < n && (c  = getchar()) != EOF && c != '\n'; i++) 
		s[i] = c;
	s[i] = '\0';
	return i;
}

输出:

 ./tail -5
Enter strings (maximum rows: 100, maximum columns : 100):
sdj asd
sjde
 djs
dsjde
djsj
wjdjf
dsdjjf
sdnd
djj
sjej


The last 5 lines:
wjdjf
dsdjjf
sdnd
djj
sjej

14. Modify the sort program to handle a -r flag, which indicates sorting in reverse (decreasing) order. Be sure that -r works with -n.

**约定:**命令行参数有 -r 和 -n 时,以逆序,比较数字,否则默认为正序,比较字母。如果出现非法字符,提示非法字符。

增加一个逆序参数两种方法:

  1. 仍然用之前的正序排序,如果参数有 -r,则反向输出。
  2. 在比较函数中加入是否逆序的参数,可以逆序排序,以下程序使用该方法。与之前不同之处在增加参数 reverse,该参数在 比较程序中起作用,如果为1,则之前返回正值的改为范围负值,因此排序程序不用改动。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<ctype.h>
#define MAXLINES 100
#define MAXLEN 100
int mgetline(char *s, int n);
int numcmp(char *s1, char *s2, int r);
int mstrcmp(char *s1, char *s2, int r);
void mqsort(void *v[], int left, int right, int (*comp)(void*, void*, int), int
r);
void writelines(char *v[], int nlines);
int main(int argc, char *argv[])
{
	//检查参数
	int c, numeric = 0, reverse = 0;
	while (--argc > 0 && (*++argv)[0] == '-') 
		if (strcmp(*argv, "-n") == 0) 
			numeric = 1;
		else if (strcmp(*argv, "-r") == 0)
			reverse = 1;
		else {
			printf("find: illegal option %s\n",*argv);
			argc = 0;
		}
	//输入字符串
	printf("Please enter strings (maximum rows: %d, maximum columns: " 
	"%d\n",MAXLINES, MAXLEN);
	int nlines = 0;
	char *lineptr[MAXLINES];
	char line[MAXLEN+1];
	int len;
	while (nlines < MAXLINES && (len = mgetline(line, MAXLEN)) > 0) {
		lineptr[nlines] = (char *)malloc((len+1) * sizeof(char));
		strcpy(lineptr[nlines++], line);
	}
	//输出原始字符串
	printf("\nOriginal strings:\n");
	writelines(lineptr, nlines);
	//排序
	mqsort((void **)lineptr, 0, nlines-1,(int (*)(void*, void*, int))(numeric ?
	numcmp : mstrcmp), reverse);
	printf("\nSorted strings in %s order:\n",reverse > 0 ? "decreasing" : "increasing");
	writelines(lineptr, nlines);
	//释放内存
	for (int i = 0; i < nlines; i++)
		free(lineptr[i]);
	return 0;	
}
int mgetline(char *s, int n)
{
	int i, c;
	for (i = 0; i < n && (c = getchar()) != EOF && c != '\n'; i++)
		s[i] = c;
	if (c != '\n' && c != EOF)
		while (getchar() != '\n')
			continue;
	s[i] = '\0';
	return i;
}
//快速排序
#define cutoff 3
void swap(void *v[], int s1, int s2)
{
	void *tmp;
	tmp = v[s1];
	v[s1] = v[s2];
	v[s2] = tmp;
}
void InsertionSort(void *v[], int n, int (*comp)(void *, void *, int), int r)
{
	int i, p;
	char tmp[MAXLEN+1];
	for (p = 1; p < n; p++) {
		strcpy(tmp, v[p]);
		for (i = p; i > 0 && (*comp)(v[i-1], tmp, r) > 0; i--)
			strcpy(v[i], v[i-1]);
		strcpy(v[i], tmp);
	}
}
int numcmp(char *s1, char *s2, int reverse)
{
	double v1, v2;
	v1 = atof(s1);
	v2 = atof(s2);
	if (v1 < v2)
		return reverse == 0 ? -1 : 1;
	else if (v1 > v2)
		return reverse == 0 ? 1 : -1;
	else
		return 0;
}
int mstrcmp(char *s1, char *s2, int reverse)
{
	int i;
	for (i = 0; s1[i] == s2[i]; i++)
		if (s1[i] == '\0')
			return 0;
	return reverse == 0 ? s1[i] - s2[i] : s2[i] - s1[i];
}
void *Median3(void *v[], int left, int right, int (*comp)(void*, void*, int),int r)
{
	int center = (left + right) / 2;
	if ((*comp)(v[left], v[center], r) > 0)
		swap(v, left, center);
	if ((*comp)(v[left], v[right], r) > 0)
		swap(v, left, right);
	if ((*comp)(v[center], v[right], r) > 0)
		swap(v, center, right);
	swap(v, center, right-1);
	return v[right-1];
}
void mqsort(void *v[], int left, int right, int (*comp)(void*, void*, int), int
r)
{
	int i, j;
	void *pivot;
	if (left + cutoff <= right) {
		pivot = Median3(v, left, right, comp, r);
		i = left;
		j = right-1;
		for (; ; ) {
			while ((*comp)(v[++i], pivot, r) < 0)
				;
			while ((*comp)(v[--j], pivot, r) > 0)
				;
			if (i < j)
				swap(v, i, j);
			else
				break;
		}
		swap(v, i, right-1);
		mqsort(v, left, i-1, comp, r);
		mqsort(v, i+1, right, comp, r);
	}
	else
		InsertionSort(v+left, right - left + 1, comp, r);
}
//writelines
void writelines(char *v[], int nlines)
{
	for (int i = 0; i < nlines; i++)
		puts(v[i]);
}

输出:

./qsort1.o -r -n
Please enter strings (maximum rows: 100, maximum columns: 100
2
4
0
1
3


Original strings:
2
4
0
1
3

Sorted strings in decreasing order:
4
3
2
1
0

15. Add the option -f to fold upper and lower case together, so that case distinctions are not made during sorting; for example, a and A compare equal.

分析:程序增加一个参数 -f,不区分大小写

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<ctype.h>
#define MAXLINES 100
#define MAXLEN 100
int mgetline(char *s, int n);
void writelines(char *v[], int n);
int numcmp(char *s1, char *s2, int r, int f);
int mstrcmp(char *s1, char *s2, int r, int f);
void mqsort(void *v[], int left, int right, int (*comp)(void*,void*,int,int),\
int r, int f);
int main(int argc, char *argv[])
{
	//检查参数
	int numeric = 0, reverse = 0, fold = 0;
	while (--argc > 0 && (*++argv)[0] == '-') 
		if (strcmp(*argv, "-r") == 0)
			reverse = 1;
		else if (strcmp(*argv, "-n") == 0)
			numeric = 1;
		else if (strcmp(*argv, "-f") == 0)
			fold = 1;
		else {
			printf("find : illegal option %s\n",*argv);
			argc = 0;
		}
	//获取字符串
	printf("Enter strings (maximum rows: %d, maximum columns: " 
	"%d\n",MAXLINES,MAXLEN);
	int nlines = 0;
	int len;
	char *lineptr[MAXLINES];
	char line[MAXLEN+1];
	while (nlines < MAXLINES && (len = mgetline(line, MAXLEN)) > 0) {
		lineptr[nlines] = (char *)malloc((len+1) * sizeof(char));
		strcpy(lineptr[nlines++], line);
	}
	printf("\nOriginal strings:\n");
	writelines(lineptr, nlines);
	//排序
	mqsort((void **)lineptr, 0, nlines-1, (int(*)(void*, void*, int,\
	int))(numeric ? numcmp : mstrcmp), reverse, fold);
	printf("\nSorted string in %s order and %s upper and lower case " 
	"together.\n",reverse ? "decreasing" : "increasing",fold ? "fold":"unfold");
	writelines(lineptr, nlines);
	//释放内存
	for (int i = 0; i < nlines; i++)
		free(lineptr[i]);
	return 0;
}
//mgetline
int mgetline(char *s, int n)
{
	int i, c;
	for (i = 0; i < n && (c = getchar()) != EOF && c != '\n'; i++)
		s[i] = c;
	s[i] = '\0';
	if (c != '\n' && c != EOF)
		while (getchar() != '\n')
			continue;
	return i;
}
//快速排序
#define cutoff 3
void swap(void *v[], int i, int j)
{
	void *tmp;
	tmp = v[i];
	v[i] = v[j];
	v[j] = tmp;
}
void *Median3(void *v[], int left, int right, int (*comp)(void*, void*, int,
int), int r, int f)
{
	int center = (left + right) / 2;
	if ((*comp)(v[left], v[center], r, f) > 0)
		swap(v, left, center);
	if ((*comp)(v[left], v[right], r, f) > 0)
		swap(v, left, right);
	if ((*comp)(v[center], v[right], r, f) > 0)
		swap(v, center, right);
	swap(v, center, right-1);
	return v[right-1];
}
int numcmp(char *s1, char *s2, int r, int f)
{
	double v1, v2;
	v1 = atof(s1);
	v2 = atof(s2);
	if (v1 < v2)
		return r ? 1 : -1;
	else if (v1 > v2)
		return r ? -1 : 1;
	else
		return 0;
}
int mstrcmp(char *s1, char *s2, int r, int f)
{
	int i;
	for ( i = 0; s1[i] == s2[i] || (f && tolower(s1[i]) == tolower(s2[i])); i++) 
		if (s1[i] == '\0')
			return 0;
	if (f) 
		return r ? tolower(s2[i]) - tolower(s1[i]) : tolower(s1[i]) -
		tolower(s2[i]);
	else
		return r ? s2[i]-s1[i] : s1[i]-s2[i];	
}
void InsertionSort(void *v[], int n, int (*comp)(void*,void*,int,int),int r, int
f)
{
	int i, p;
	char tmp[MAXLEN+1];
	for (p = 1; p < n; p++) {
		strcpy(tmp,v[p]);
		for (i = p; i > 0 && (*comp)(v[i-1],tmp,r,f) > 0; i--)
			strcpy(v[i], v[i-1]);
		strcpy(v[i], tmp);
	}
}
void mqsort(void *v[], int left, int right, int (*comp)(void*, void*, int,
int),int r, int f) 
{
	void *pivot;
	int i, j;
	if (left + cutoff <= right) {
		pivot = Median3(v, left, right, comp, r, f);
		i = left;
		j = right-1;
		for (; ; ) {
			while ((*comp)(v[++i],pivot,r,f) < 0)
				;
			while ((*comp)(v[--j],pivot,r,f) > 0)
				;
			if (i < j)
				swap(v,i,j);
			else
				break;
		}
		swap(v, i, right-1);
		mqsort(v, left, i-1, comp, r, f);
		mqsort(v, i+1, right, comp, r, f);
	}
	else
		InsertionSort(v+left, right-left+1, comp, r, f);
}
//writelines
void writelines(char *v[], int nlines)
{
	for (int i = 0; i < nlines; i++)
		puts(v[i]);
}

输出:

./qsort2.o -f
Enter strings (maximum rows: 100, maximum columns: 100
a
B
D
c
C


Original strings:
a
B
D
c
C

Sorted string in increasing order and fold upper and lower case together.
a
B
C
c
D

从结果可知:测试中 c 在 C前面,但输出时两者交换了,相等的地方改变了次序。

16. Add the -d (``directory order’’) option, which makes comparisons only on letters, numbers and blanks. Make sure it works in conjunction with -f.

分析:
程序在之前基础上加上参数 -d,只比较字母,数字和空格,遇到其他字符跳过不比较。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<ctype.h>
#define MAXLINES 100
#define MAXLEN 100
int mgetline(char *s, int n);
void writelines(char *v[], int n);
int numcmp(char *s1 , char *s2, int r, int f, int d);
int mstrcmp(char *s1, char *s2, int r, int f, int d);
void mqsort(void *v[], int left, int right, int (*comp)(void*, void*, int, int,
int), int r, int f, int d);
void InsertionSort(void *v[], int n, int (*comp)(void*, void*, int, int,
int),int r, int f, int d);
int main(int argc, char *argv[])
{
	//检查参数
	int numeric = 0, reverse = 0, fold = 0, directory = 0;
	while (--argc > 0 && (*++argv)[0] == '-') 
		if (strcmp(*argv, "-n") == 0)
			numeric =1;
		else if (strcmp(*argv, "-r") == 0)
			reverse = 1;
		else if (strcmp(*argv, "-f") == 0)
			fold = 1;
		else if (strcmp(*argv, "-d") == 0)
			directory = 1;
		else {
			printf("find: illeagal argument %s\n", *argv);
			argc = 0;
		}
	//输入字符串
	printf("Enter strings (maximum rows: %d, maximum columns: %d\n",MAXLINES,
	MAXLEN);
	int nlines = 0;
	int len;
	char *lineptr[MAXLINES];
	char line[MAXLEN+1];
	while (nlines < MAXLINES && (len = mgetline(line, MAXLEN)) > 0) {
		lineptr[nlines] = (char *)malloc((len+1) * sizeof(char));
		strcpy(lineptr[nlines++], line);
	}
	printf("\nOriginal strings:\n");
	writelines(lineptr, nlines);
	//排序
	mqsort((void **)lineptr, 0, nlines-1, (int (*)(void*, void*, int, int,
	int))(numeric ? numcmp : mstrcmp), reverse, fold, directory);
	printf("\nSorted strings:\n");
	writelines(lineptr, nlines);
	//释放内存
	for (int i = 0; i < nlines; i++)
		free(lineptr[i]);
	return 0;
}
int mgetline(char *s, int n)
{
	int i, c;
	for (i = 0; i < n && (c = getchar()) != EOF && c != '\n'; i++)
		s[i] = c;
	s[i] = '\0';
	if (c != '\n' && c != EOF)
		while (getchar() != '\n')
			continue;
	return i;
}
void writelines(char *v[], int n)
{
	int i;
	for (i = 0; i < n; i++)
		puts(v[i]);
}
//快速排序
#define cutoff 3
void swap(void *v[], int i, int j)
{
	void *temp;
	temp = v[i];
	v[i] = v[j];
	v[j] = temp;
}
void *Median3(void *v[], int left, int right, int (*comp)(void*, void*, int,
int,int), int r, int f, int d)
{
	int center = (left + right) / 2;
	if ((*comp)(v[left], v[center], r, f, d) > 0)
		swap(v, left, center);
	if ((*comp)(v[left], v[right], r, f, d) > 0)
		swap(v, left, right);
	if ((*comp)(v[center], v[right], r, f, d) > 0)
		swap(v, center, right);
	swap(v, center, right-1);
	return v[right-1];
}
int numcmp(char *s1, char *s2, int r, int f, int d)
{
	double v1, v2;
	v1 = atof(s1);
	v2 = atof(s2);
	if (v1 > v2)
		return r ? -1 : 1;
	else if (v1 < v2)
		return r ? 1 : -1;
	else
		return 0;
}
int mstrcmp(char *s1, char *s2, int r, int f, int d)
{
	int i, j;
	for (i = 0, j = 0; s1[i] != '\0'&& s2[j] != '\0'; ) {
		if (d) {
			if (!isalnum(s1[i]) && !isspace(s1[i])) {
				i++;
				continue;
			}
			if (!isalnum(s2[j]) && !isspace(s2[j])) {
				j++;
				continue;
			}
		}
		if (f) {
			if (tolower(s1[i]) != tolower(s2[j]))
				return r ? tolower(s2[j]) - tolower(s1[i]) : tolower(s1[i]) -
				tolower(s2[j]);
			else
				i++;
				j++;
		}
		else {
			if (s1[i] != s2[j])
				return r ? s2[j] - s1[i] : s1[i] - s2[j];
			else
				i++;
				j++;
		}
	}
	if (s1[i] == s2[j] && s1[i] == '\0')
		return 0;
	else if (s1[i] == '\0')
		return r ? 1 : -1;
	else if (s2[j] == '\0')
		return r ? -1 : 1;
}
void InsertionSort(void *v[], int n, int (*comp)(void*, void*, int, int,
int),int r, int f, int d)
{
	int i, p;
	void *tmp;
	for (p = 1; p < n; p++) {
		tmp = v[p];
		for (i = p; i > 0 && (*comp)(v[i-1], tmp, r, f, d) > 0; i--)
			v[i] = v[i-1];
		v[i] = tmp;
	}
}
void mqsort(void *v[], int left, int right, int (*comp)(void*, void *, int, int,
int), int r, int f, int d)
{
	void *pivot;
	int i, j;
	if (left + cutoff <= right) {
		pivot = Median3(v, left, right, comp, r, f, d);
		i = left; 
		j = right-1;
		for (; ;) {
			while ((*comp)(v[++i],pivot,r,f,d) < 0)
				;
			while ((*comp)(v[--j],pivot,r,f,d) > 0)
				;
			if (i < j)
				swap(v, i, j);
			else
				break;
		}
		swap(v, i, right-1);
		mqsort(v, left, i-1, comp, r ,f, d);
		mqsort(v, i+1, right, comp, r, f, d);
	}
	else 
		InsertionSort(v+left, right-left+1, comp, r, f, d);
}

本程序与前面更改地方:
之前:

void InsertionSort(void *v[], int n, int (*comp)(void *, void *, int), int r)
{
	int i, p;
	char tmp[MAXLEN+1];
	for (p = 1; p < n; p++) {
		strcpy(tmp, v[p]);
		for (i = p; i > 0 && (*comp)(v[i-1], tmp, r) > 0; i--)
			strcpy(v[i], v[i-1]);
		strcpy(v[i], tmp);
	}
}

现在:

void InsertionSort(void *v[], int n, int (*comp)(void*, void*, int, int,
int),int r, int f, int d)
{
	int i, p;
	void *tmp;
	for (p = 1; p < n; p++) {
		tmp = v[p];
		for (i = p; i > 0 && (*comp)(v[i-1], tmp, r, f, d) > 0; i--)
			v[i] = v[i-1];
		v[i] = tmp;
	}
}

除了添加参数 -d,本程序将指针直接赋值而不是之前赋值字符串。两种都能实现。但要么全部用复制字符串修改地址内容的方式,要么全部用修改地址的方式。
swap 函数也一样,两种方式都能实现。

输出测试:

./qsort3.o -r -f -d
Enter strings (maximum rows: 100, maximum columns: 100
 8as
*a1 v
a1B2
1a(sd
-1Aa


Original strings:
 8as
*a1 v
a1B2
1a(sd
-1Aa

Sorted strings:
a1B2
*a1 v
1a(sd
-1Aa
 8as

只比较字母,数字和空格,且不区分大小写,逆序排布。空格的ascall值最小,其次数字,最后字母。

17. Add a field-searching capability, so sorting may bee done on fields within lines, each field sorted according to an independent set of options. (The index for this book was sorted with -df for the index category and -n for the page numbers.)

分析:
增加一个区域处理的功能,在范围内选择一段区域选择来处理字符串。
两种实现方式:这样不用命令行参数,因为要根据输入的行数选择。通过菜单选择实现更简单。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<ctype.h>
#define MAXLINES 100
#define MAXLEN 100
int input(int min, int max);
int mgetline(char *s, int n);
void writelines(char *v[], int nlines);
char getlet(char *s);
int numcmp(char *s1, char *s2, int r, int f, int d);
int mstrcmp(char *s1, char *s2, int r, int f, int d);
void mqsort(void *v[], int, int, int (*comp)(void*, void*, int, int, int), int
r, int f, int d);
void InsertionSort(void *v[], int n, int (*comp)(void*, void*, int, int, int),
int r, int f, int d);
int main(void)
{
	//输入字符串
	printf("Enter strings (maximum rows: %d, maximum columns: %d):\n",MAXLINES,
	MAXLEN);
	int nlines = 0;
	char *lineptr[MAXLINES];
	char line[MAXLEN+1];
	int len;
	while (nlines < MAXLINES && (len = mgetline(line, MAXLEN)) > 0) {
		lineptr[nlines] = (char *)malloc((len+1) * sizeof(char));
		strcpy(lineptr[nlines++], line);
	}
	printf("\nOriginal strings:\n");
	writelines(lineptr, nlines);
	//选择参数
	int left, right;
	int ok = 1;
	char c;
	int numeric, reverse, fold, directory;
	while (ok) {
		printf("Please select a field (%d ~ %d):\n", 0, nlines-1);
		left = input(0, nlines-1);
		right = input(left, nlines-1);
		printf("\nComparing strings by numeric value (n) or lexicographically (s): ");
		c = getlet("ns");
		numeric = c == 'n' ? 1 : 0;
		printf("Sorting strings in decrasing order (d) or increasing order (i): ");
		c = getlet("di");
		reverse = c == 'd' ? 1 : 0;
		if (numeric == 0) {
			printf("Fold upper and lower case togehter ? <y/n> ");
			c = getlet("yn");
			fold = c == 'y' ? 1 : 0;
			printf("Using directory order ? <y/n> ");
			c = getlet("yn");
			directory = c == 'y' ? 1 : 0;
		}
		//排序
		mqsort((void **)lineptr, left, right, (int (*)(void*, void*, int, int,
		int))(numeric ? numcmp : mstrcmp), reverse, fold, directory);
		printf("\nSorted strings (lines between %d ~ %d sorted):\n",left, right);
		writelines(lineptr, nlines);
		printf("Select another field to sort again? <y/n> ");
		c = getlet("yn");
		if (c == 'y') 
			continue;
		else
			break;
	}
	//释放内存
	for (int i = 0; i < nlines; i++)
		free(lineptr[i]);
	return 0;
}
int input(int min, int max)
{
	int n, status;
	int ok = 1;
	while (ok) {
		status = scanf("%d", &n);
		if (status != 1 || n < min || n > max || getchar() != '\n') {
			printf("Enter again (%d ~ %d): ", min, max);
			while (getchar() != '\n')
				continue;
			continue;
		}
		else
			return n;
	}
}
int mgetline(char *s, int n)
{
	int i, c;
	for (i = 0; i < n && (c = getchar()) != EOF && c != '\n'; i++)
		s[i] = c;
	s[i] = '\0';
	if (c != EOF && c != '\n')
		while (getchar () != '\n')
			continue;
	return i;
}
void writelines(char *v[], int nlines)
{
	int i;
	for (i = 0; i < nlines; i++)
		puts(v[i]);
}
char getlet(char *s)
{
	char c;
	while (c = getchar()) {
		if (strchr(s, c) && getchar() == '\n') 
			return c;
		else {
			printf("Enter again: ");
			while (getchar() != '\n')
				continue;
		}
	}
}
//快速排序
#define cutoff 3
void swap(void *v[], int i, int j)
{
	void *tmp;
	tmp = v[i];
	v[i] = v[j];
	v[j] = tmp;
}
void *Median3(void *v[], int left, int right, int (*comp)(void*, void*, int,
int, int), int r, int f, int d)
{
	int center = (left + right) / 2;
	if ((*comp)(v[left], v[center], r, f, d) > 0)
		swap(v, left, center);
	if ((*comp)(v[left], v[right], r, f, d) > 0)
		swap(v, left, right);
	if ((*comp)(v[center], v[right], r, f, d) > 0)
		swap(v, center, right);
	swap(v, center, right-1);
	return v[right-1];
}
int numcmp(char *s1, char *s2, int r, int f, int d)
{
	double v1, v2;
	v1 = atof(s1);
	v2 = atof(s2);
	if (v1 < v2)
		return r ? 1 : -1;
	else if (v1 > v2)
		return r ? -1 : 1;
	else
		return 0;
}
int mstrcmp(char *s1, char *s2, int r, int f, int d)
{
	int i, j;
	for (i = 0, j = 0; s1[i] != '\0' && s2[j] != '\0'; ) {
		if (d) {
			if (!isalnum(s1[i]) && !isspace(s1[i])) {
				i++;
				continue;
			}
			if (!isalnum(s2[j]) && !isspace(s2[j])) {
				j++;
				continue;
			}
		}
		if (f) {
			if (tolower(s1[i]) == tolower(s2[j])) {
				i++;
				j++;
			}
			else {
				return r ? tolower(s2[j]) - tolower(s1[i]) : tolower(s1[i]) -
				tolower(s2[j]);
			}
		}
		else {
			if (s1[i] == s2[j]) {
				i++;
				j++;
			}
			else 
				return r ? s2[j] - s1[i] : s1[i] - s2[j];
		}
	}
	if (s1[i] == s2[j] && s1[i] == '\0')
		return 0;
	else if (s1[i] == '\0' &&  s2[j] != '\0')
		return r ? 1 : -1;
	else
		return r ? -1 : 1;
}
void InsertionSort(void *v[], int n, int (*comp)(void*, void*, int, int, int),
int r, int f, int d)
{
	int i, p;
	void *tmp;
	for (p = 1; p < n; p++) {
		tmp = v[p];
		for (i = p; i > 0 && (*comp)(v[i-1], tmp, r, f, p) > 0; i--)
			v[i] = v[i-1];
		v[i] = tmp;
	}
}
void mqsort(void *v[], int left, int right, int (*comp)(void*, void*, int, int,
int), int r, int f, int d)
{
	void *pivot;
	int i, j;
	if (left + cutoff <= right) {
		pivot = Median3(v, left, right, comp, r, f, d);
		i = left;
		j = right-1;
		for (; ; ) {
			while ((*comp)(v[++i], pivot, r, f, d) < 0)
				;
			while ((*comp)(v[--j], pivot, r, f, d) > 0)
				;
			if (i < j)
				swap(v, i, j);
			else
				break;
		}
		swap(v, i, right-1);
		mqsort(v, left, i-1, comp, r, f, d);
		mqsort(v, i+1, right, comp, r, f, d);
	}
	else
		InsertionSort(v+left, right-left+1, comp, r, f, d);
}

输出:

./qsort4.o
Enter strings (maximum rows: 100, maximum columns: 100):
 ajde
 8as
-1*aAb
ab-sda
AB1-s


Original strings:
 ajde
 8as
-1*aAb
ab-sda
AB1-s
Please select a field (0 ~ 4):
2
4

Comparing strings by numeric value (n) or lexicographically (s): s
Sorting strings in decrasing order (d) or increasing order (i): d
Fold upper and lower case togehter ? <y/n> y
Using directory order ? <y/n> y

Sorted strings (lines between 2 ~ 4 sorted):
 ajde
 8as
ab-sda
AB1-s
-1*aAb

?18. Make dcl recover from input errors.

    1. 和20题属于最后一节复杂的声明,这里程序用了递归下降解析算法,目前还未学习解析(parsing)技术,学习编译原理后补充。

?19. Modify undcl so that it does not add redundant parentheses to declarations.

?20. Expand dcl to handle declarations with function argument types, qualifiers like const, and so on.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值