读书笔记:C程序设计语言,第五章:指针与数组(部分课后题解)

2月27日终于下决心改变学习策略:

  • 不再总结一些基础的知识
  • 不会把书中的要点记录在博客,而是直接买书(之前都是图书馆借书),直接阅读书籍
  • 博客主要总结难度较大的问题
这是最后一篇基础性的记录博客,但是很不完整,所以只是把题贴下来了,以后完成时会单开一篇博客。

5.2 指针与函数

Exercise 5-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.
答: 暂时未找到合适的解法。
Exercise 5-2. Write getfloat, the floating-point analog of getint. What type does getfloat return as its function value?
答: 暂时未找到合适的解法。

5.5字符指针和函数

Exercise 5-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.
答: 暂时未找到合适的解法。

Exercise 5-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.
答: 暂时未找到合适的解法。

Exercise 5-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.
答: 暂时未找到合适的解法。

Exercise 5-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).
答: 暂时未找到合适的解法。

5.6 指针数组以及指向指针的指针

  1. unix有个程序sort,可以完成一个功能:根据每行字符串的第一个字符,按字典顺序排序。
  2. 本节试着写了一个例子,简化版的sort。
  3. 使用 指针数组 可以高效的完成这个功能,如下图所示:

    由指针组成的数组,每一个指针指向一行。不用移动文本行,仅仅改变指针的指向,就完成了这个功能
  4. 移动文本行将带来复杂的储存管理和巨大的开销
  5. 在比较字符的时候我们使用了strcmp函数。
  6. 程序的划分:一个问题一个函数,主函数控制其他函数的执行。
  7. 具体的函数我倒没有觉得有什么难点,不过可以来看看指针数组的初始化,实际上也没什么区别:
    char *lineptr[MAXLINES]
    

练习题

Exercise 5-7. Rewrite readlines to store lines in an array supplied by main, rather than calling alloc to maintain storage. How much faster is the program?答:code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

#define TRUE     1
#define FALSE    0

#define MAXLINES 5000       /* maximum number of lines */
#define MAXLEN   1000       /* maximum length of a line */
char *lineptr[MAXLINES];
char lines[MAXLINES][MAXLEN];//第一个相当于存入的一行的编号

/* K&R2 p29 */
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;
  }
  s[i] = '\0';
  return i;
}

/* K&R2 p109 */
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 = malloc(len)) == NULL)//Malloc 向系统申请分配指定size个字节的内存空间
      return -1;
    else {
      line[len - 1] = '\0';  /* delete the newline */
      strcpy(p, line);//将读到的行复制到新申请的内存空间中
      lineptr[nlines++] = p;
    }
  return nlines;
}

int readlines2(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'; 
  return nlines;
}

int main(int argc, char *argv[])
{
  /* read things into cache, to be fair. */
  readlines2(lines, MAXLINES);

  if (argc > 1 && *argv[1] == '2') {
    puts("readlines2()");
    readlines2(lines, MAXLINES);
  } else {
    puts("readlines()");
    readlines(lineptr, MAXLINES);
  }

  return 0;
}

5.7 多维数组

  1. 类似矩阵的多维数组,使用不如指针数组那么频繁
  2. char类型在存放较小的非字符整数也是合法的
  3. 看一下二维数组的赋值,下面的二维数组表示某个月有多少天:
    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}
    };
  4. 看作矩阵的话:
    daytab[i][j]   /* [row][col] */
    
    第一个是行,第二个是列
  5. 在二位数组作为参数传递给函数的时候,在函数的声明中必须指名数组的列数,但是多少行可以不写。
    f(int daytab[2][13]) { ... }//可以这样
    f(int daytab[][13]) { ... }//可以这样
    f(int (*daytab)[13]) { ... }//也可以这样,小心圆括号,改变优先级
    第三种写法是因为,二维数组相当于储存了一个指向很多对象的指针,每个对象都是由13个整型元素构成的一维数组。具体说一下,就是一个数组在被调用的时候传递的就是指针。
    int *daytab[13]//[]的优先级高,所以这样表示一个指向整型的数组指针
    
  6. 一般多维数组中,第一维可以不指定大小,其余各维都必须明确大小

练习题

Exercise 5-8. There is no error checking in day_of_year or month_day. Remedy this defect.(进行错误检查。
答:code:
/*
 * A solution to exercise 5-8 in K&R2, page 112:
 *
 *	There is no error checking in day_of_year or month_day. Remedy
 *	this defect.
 *
 * The error to check for is invalid argument values. That is simple, what's
 * hard is deciding what to do in case of error. In the real world, I would
 * use the assert macro from assert.h, but in this solution I take the
 * approach of returning -1 instead. This is more work for the caller, of
 * course.
 *
 * I have selected the year 1752 as the lowest allowed year, because that
 * is when Great Britain switched to the Gregorian calendar, and the leap
 * year validation is valid only for the Gregorian calendar.
 *
 * Lars Wirzenius <liw@iki.fi>
 */

#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},
};

/* day_of_year: set day of year from month & day */
int day_of_year(int year, int month, int day)
{
	int i, leap;

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

	leap = (year%4 == 0 && year%100 != 0) || year%400 == 0;
	if (day > daytab[leap][month])
		return -1;

	for (i = 1; i < month; i++)
		day += daytab[leap][i];
	return day;
}

/* month_day: set month, day from day of year */
int month_day(int year, int yearday, int *pmonth, int *pday)
{
	int i, leap;

	if (year < 1752 || yearday < 1)
		return -1;

	leap = (year%4 == 0 && year%100 != 0) || year%400 == 0;
	if ((leap && yearday > 366) || (!leap && yearday > 365))
		return -1;

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

	return 0;
}


/* main: test day_of_year and month_day */
int main(void)
{
	int year, month, day, yearday;

	for (year = 1970; year <= 2000; ++year) {
		for (yearday = 1; yearday < 366; ++yearday) {
			if (month_day(year, yearday, &month, &day) == -1) {
				printf("month_day failed: %d %d\n",
					year, yearday);
			} else if (day_of_year(year, month, day) != yearday) {
				printf("bad result: %d %d\n", year, yearday);
				printf("month = %d, day = %d\n", month, day);
			}
		}
	}

	return 0;
}

5.8 指针数组的初始化

  1. 看下面的函数就知道怎么样对指针数组初始化:
    /* month_name: return name of n-th month */
    char *month_name(int n)
    {
    	static char *name[] = {
    	"Illegal month",
    	"January", "February", "March",
    	"April", "May", "June",
    	"July", "August", "September",
    	"October", "November", "December"
    	};
    
    	return (n < 1 || n > 12) ? name[0] : name[n];
    }
    

5.9 指针与多维数组

  1. 看例子来分析,指针数组和多维数组的区别:
    int a[10][20];
    int *b[10];
    
    a是真正的二维数组,分配了200个的储存空间。b只是分配了10个指针,10个指针可以指向不同的长度数组。
  2. 下图进一步说明了上面的说法:
    char *name[] = { "Illegal month", "Jan", "Feb", "Mar" };
    

    char aname[][15] = { "Illegal month", "Jan", "Feb", "Mar" };
    

练习题

Exercise 5-9. Rewrite the routines day_of_year and month_day with pointers instead of indexing.
答:code:
/*
 * A solution to exercise 5-9 in K&R2, page 114:
 *
 *	Rewrite the routines day_of_year and month_day with pointers
 *	instead of indexing.
 *
 * Lars Wirzenius <liw@iki.fi>
 */

#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},
};

/* original versions, for comparison purposes */

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

	leap = (year%4 == 0 && year%100 != 0) || year%400 == 0;
	for (i = 1; i < month; i++)
		day += daytab[leap][i];
	return day;
}

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

	leap = (year%4 == 0 && year%100 != 0) || year%400 == 0;
	for (i = 1; yearday > daytab[leap][i]; i++)
		yearday -= daytab[leap][i];
	*pmonth = i;
	*pday = yearday;
}


/* pointer versions */

int day_of_year_pointer(int year, int month, int day)
{
	int i, leap;
	char *p;

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

	/* Set `p' to point at first month in the correct row. */
	p = &daytab[leap][1];//指向第一个月

	/* Move `p' along the row, to each successive month. */
	for (i = 1; i < month; i++) {
		day += *p;
		++p;
	}
	return day;
}

void month_day_pointer(int year, int yearday, int *pmonth, int *pday)
{
	int i, leap;
	char *p;

	leap = (year%4 == 0 && year%100 != 0) || year%400 == 0;
	p = &daytab[leap][1];//指向第一个月
	for (i = 1; yearday > *p; i++) {
		yearday -= *p;
		++p;
	}
	*pmonth = i;
	*pday = yearday;
}


int main(void)
{
	int year, month, day, yearday;

	year = 2000;
	month = 3;
	day = 1;
	printf("The date is: %d-%02d-%02d\n", year, month, day);
	printf("day_of_year: %d\n", day_of_year(year, month, day));
	printf("day_of_year_pointer: %d\n",
		day_of_year_pointer(year, month, day));


	yearday = 61;	/* 2000-03-01 */
	month_day(year, yearday, &month, &day);
	printf("Yearday is %d\n", yearday);
	printf("month_day: %d %d\n", month, day);
	month_day_pointer(year, yearday, &month, &day);
	printf("month_day_pointer: %d %d\n", month, day);

	return 0;
}

5.10 命令行参数

  1. 也就是跟着main函数的两个参数
  2. 第一个是argc,用于参数计数
  3. 第二个是argv,是参数向量,是一个字符串数组的指针,每一个字符串对应一个参数,通常用多级指针处理这些参数
  4. c语言规定:argv[0],是启动该程序的程序名,故argc至少为1
  5. unix下面的回显程序:
    echo hello, world
    argc 为 3,  argv[0], argv[1], argv[2] 分别为: "echo", "hello,", and "world"
  6. 我们在unix经常使用:
    find -nx pattern 
    
    find -x -n pattern
    
    上述这样的语句,含义就是-x和-n能够对这个程序做出一些更具体的要求,这里:-n:打印行号-x:打印所有与模式不匹配的文本行pattern:所谓模式,就是查找某行里有没有某一串字符串,这个某一行字符串就是模式。下面这段代码就是讲述了如何实现这样的效果:
    #include <stdio.h>
    #include <string.h>
    #define MAXLINE 1000
    int getline(char *line, int max);
    /* find: print lines that match pattern from 1st arg */
    main(int argc, char *argv[])
    {
    	char line[MAXLINE];
    	long lineno = 0;
    	int c, except = 0, number = 0, found = 0;
    	while (--argc > 0 && (*++argv)[0] == '-')//先找到‘-’。另外没有{}的原因是后面直接根了一个while的程序块
    		while (c = *++argv[0])				//为什么要加[0]这是因为*argv代表字符串
    			switch (c) {					//*argv[0]代表字符串的第一个字符
    				case 'x':					//*argv[0]和**argv是等效的
    					except = 1;				//稍稍分析就可以看出这段代码既可以应对:-nx 或者 -n -x两种形式
    					break;
    				case 'n':
    					number = 1;
    					break;
    				default:
    					printf("find: illegal option %c\n", c);
    					argc = 0;
    					found = -1;
    					break;
    		}
    	if (argc != 1)
    		printf("Usage: find -x -n pattern\n");
    	else
    		while (getline(line, MAXLINE) > 0) {
    			lineno++;
    			if ((strstr(line, *argv) != NULL) != except) {//如果含有该字会返回1
    				if (number)								//如果except等于1的话 1!=1 会返回0
    					printf("%ld:", lineno);				//所以就不打印了
    					printf("%s", line);					//如果不含有这个字段的分析方式类似
    					found++;
    			}
    		}
    	return found;
    }
    
    
    
    
    

练习题

Exercise 5-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).答:code:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>

#define STACK_SIZE 1024

double stack[STACK_SIZE];
int stack_height = 0;

void panic(const char *msg) {
	fprintf(stderr, "%s\n", msg);
	exit(EXIT_FAILURE);
}

void push(double value) {
	if (stack_height == STACK_SIZE)
		panic("stack is too high!");
	stack[stack_height] = value;
	++stack_height;
}

double pop(void) {
	if (stack_height == 0)
		panic("stack is empty!");
	return stack[--stack_height];
}

int main(int argc, char **argv) {

	int i;
	double value;

	for (i = 1; i < argc; ++i) {
		switch (argv[i][0]) {
		case '\0':
			panic("empty command line argument");
			break;
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			push(atof(argv[i]));
			break;
		case '+':
			push(pop() + pop());
			break;
		case '-':
			value = pop();
			push(pop() - value);
			break;
		case '*':
			push(pop() * pop());
			break;
		case '/':
			value = pop();
			push(pop() / value);
			break;
		default:
			panic("unknown operator");
			break;
		}
	}

	printf("%g\n", pop());
	return 0;
}

Exercise 5-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.
答:没找到合适的解法。
Exercise 5-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.
答:没找到合适的解法。
Exercise 5-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.
答:code:
/* K&R Exercise 5-13 */
/* Steven Huang */

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

#define DEFAULT_NUM_LINES      10
#define MAX_LINE_LEN           1000

/*
   Points of interest for a novice:

   1.  atoi() has a normally annoying property of not being able to
       tell the caller conclusively whether the input was bad ("abc")
       or it was really zero ("0"), because it returns 0 for both
       cases.  Here, we exploit that property, because we only want
       to accept options in the form of "-n".

   2.  Try to understand how this program deals with input that
       doesn't even have as many lines as the line_ptrs[] array.
       That is, how does this program degenerate into just displaying
       everything it read?  (Hint:  what does it mean when line_ptrs[x]
       is NULL?)

   3.  Using modulo arithmetic on an index to a circular array is
       a common and useful technique.  Try to understand the range
       of values that current_line (and j, later) will take.  In
       particular, why shouldn't we just do this:

       for (i = 0; i < num_lines; i++)
         if (line_ptrs[i])
           printf("%s", line_ptrs[i]);

   4.  Why do we still use a "%s" to display what's inside line_ptrs,
       rather than just:

       printf(line_ptrs[i]);

   5.  There is a bug in this program, where you see:

       numlines = -numlines;

       When will this break?
*/

/* K&R2 p29 */
int getline1(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;
  s[i] = '\0';
  return i;
}

/* duplicates a string */
char *dupstr(const char *s)
{
  char *p = malloc(strlen(s) + 1);

  if (p)
    strcpy(p, s);
  return p;
}

int main(int argc, char *argv[])
{
  int num_lines = DEFAULT_NUM_LINES;
  char **line_ptrs;
  char buffer[MAX_LINE_LEN];
  int i;
  unsigned j, current_line;

  if (argc > 1) {
    /*
       We use a little trick here.  The command line parameter should be
       in the form of "-n", where n is the number of lines.  We don't
       check for the "-", but just pass it to atoi() anyway, and then
       check if atoi() returned us a negative number.
    */
    num_lines = atoi(argv[1]);
    if (num_lines >= 0) {
      fprintf(stderr, "Expected -n, where n is the number of lines\n");
      return EXIT_FAILURE;
    }
    /* Now make num_lines the positive number it's supposed to be. */
    num_lines = -num_lines;
  }

   /* First, let's get enough storage for a list of n pointers... */
  line_ptrs = malloc(sizeof *line_ptrs * num_lines);
  if (!line_ptrs) {
    fprintf(stderr, "Out of memory.  Sorry.\n");
    return EXIT_FAILURE;
  }
  /* and make them all point to NULL */
  for (i = 0; i < num_lines; i++)
    line_ptrs[i] = NULL;

  /* Now start reading */
  current_line = 0;
  do {
    getline1(buffer, sizeof buffer);
    if (!feof(stdin)) {
      if (line_ptrs[current_line]) {
        /* there's already something here */
        free(line_ptrs[current_lidne]);
      }
      line_ptrs[current_line] = dupstr(buffer);
      if (!line_ptrs[current_line]) {
        fprintf(stderr, "Out of memory.  Sorry.\n");
        return EXIT_FAILURE;
      }
      current_line = (current_line + 1) % num_lines;
    }
  } while (!feof(stdin));

  /* Finished reading the file, so we are ready to print the lines */
  for (i = 0; i < num_lines; i++) {
    j = (current_line + i) % num_lines;
    if (line_ptrs[j]) {
      printf("%s", line_ptrs[j]);
      free(line_ptrs[j]);
    }
  }

  return EXIT_SUCCESS;
}

5.11 指向函数的指针

练习题

Exercise 5-14. Modify the sort program to handle a -r flag, which indicates sorting in reverse (decreasing) order. Be sure that -r works with -n.
/* K&R Exercise 5-14 */
/* 完成者:Steven Huang */
/**
 * 添加注释者:zy
 * 这段代码调用的qsort居然是库函数stdlib.h里面的
 * 于是我顺带分析了一个qsort的几个参数
 * 分析结果如下图。
 * 此外,虽然在仅仅将函数的名字当作qsort参数传过去就可以了
 * 但是书上对这段还有着比较详细的描述,因为书上的qsort函数是自己实现的
 * 
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define TRUE 1
#define FALSE 0

#define MAXLINES 5000       /* maximum number of lines */
char *lineptr[MAXLINES];

#define MAXLEN 1000         /* maximum length of a line */

int reverse = FALSE;

/* K&R2 p29 */
int getline1(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;
  }
  s[i] = '\0';
  return i;
}

/* K&R2 p109 */
int readlines(char *lineptr[], int maxlines)
{
  int len, nlines;
  char *p, line[MAXLEN];

  nlines = 0;
  while ((len = getline1(line, MAXLEN)) > 0)
    if (nlines >= maxlines || (p = malloc(len)) == NULL)
      return -1;
    else {
      line[len - 1] = '\0';  /* delete the newline */
      strcpy(p, line);
      lineptr[nlines++] = p;
    }
  return nlines;
}

/* K&R2 p109 */
void writelines(char *lineptr[], int nlines)
{
  int i;

  for (i = 0; i < nlines; i++)
    printf("%s\n", lineptr[i]);
}

int pstrcmp(const void *p1, const void *p2)
{
  char * const *s1 = reverse ? p2 : p1;
  char * const *s2 = reverse ? p1 : p2;

  return strcmp(*s1, *s2);
}

int numcmp(const void *p1, const void *p2)
{
  char * const *s1 = reverse ? p2 : p1;
  char * const *s2 = reverse ? p1 : p2;
  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 nlines;
  int numeric = FALSE;
  int i;

  for (i = 1; i < argc; i++) {
    if (*argv[i] == '-') {
      switch (*(argv[i] + 1)) {
        case 'n':  numeric = TRUE;  break;
        case 'r':  reverse = TRUE;  break;
        default:
          fprintf(stderr, "invalid switch '%s'\n", argv[i]);
          return EXIT_FAILURE;
      }
    }
  }

  if ((nlines = readlines(lineptr, MAXLINES)) >= 0) {
    qsort(lineptr, nlines, sizeof(*lineptr), numeric ? numcmp : pstrcmp);//numcmp和pstrcmp就是函数名
    writelines(lineptr, nlines);
    return EXIT_SUCCESS;
  } else {
    fputs("input too big to sort\n", stderr);
    return EXIT_FAILURE;
  }
}

调试情况:
打印参数:
Exercise 5-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.
答:
code:
该题完成前:
zy@zy:~/Documents/eclipseWorkSpace/test/src$ ./a.out 
a
d
c
A
B


A
B
a
c
d
由于是自己花了12个小时写的,倒不是代码有多难,只是指针的使用还比较弱完成后:
/* K&R Exercise 5-15 */
/* 完成者:zy
 * 在Exercise 5-14的基础上修改完成,修改前的作者是:Steven Huang
 *
 */

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

#define TRUE 1
#define FALSE 0

#define MAXLINES 5000       /* maximum number of lines */
char *lineptr[MAXLINES];

#define MAXLEN 1000         /* maximum length of a line */

int reverse = FALSE;
int ignoreCase = FALSE;//表示是否忽略大小写

/* K&R2 p29 */
int getline1(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;
  }
  s[i] = '\0';
  return i;
}

/* K&R2 p109 */
int readlines(char *lineptr[], int maxlines)
{
  int len, nlines;
  char *p, line[MAXLEN];

  nlines = 0;
  while ((len = getline1(line, MAXLEN)) > 0)
    if (nlines >= maxlines || (p = malloc(len)) == NULL)
      return -1;
    else {
      line[len - 1] = '\0';  /* delete the newline */
      strcpy(p, line);
      lineptr[nlines++] = p;
    }
  return nlines;
}

/* K&R2 p109 */
void writelines(char *lineptr[], int nlines)
{
  int i;

  for (i = 0; i < nlines; i++)
    printf("%s\n", lineptr[i]);
}

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

/**
 * K&R2 P119
 * 不再调用库函数:stdlib.h 里面的qsort
 * 自己按着书上写一个:写的原因就是因为:当我传入 A B C a b c的时候会传出来:a a b b c c
 * 不知道为什么库函数里面的qsort会修改了原指针的值(指向)
 */
void myQsort(void *v[],int left,int right,int (*comp)(void *,void *)){
	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])<0){
			swap(v,++last,i);
		}
	swap(v,left,last);
	myQsort(v,left,last-1,comp);
	myQsort(v,left+1,right,comp);
}
/**
 * 该函数还可以进一步改进,现在只能对第一个字母实现 不区分大小写的功能
 *
 */
int pstrcmp( char *p1,  char *p2)//这里表示传过来的是一个以p1为名的字符数组,请看下图
{
	char  a1 = *p1;//p1:就是一个字符串,可以直接理解为一个字符数组,对其*p1是取其第一个字符
	char  a2 = *p2;

	if(ignoreCase){
		if(a1>64 && a1<91 ){
			a1+=32;
		}
		if(a2>64 && a2<91 ){
			a2+=32;
		}
	}
	if (a1==a2) return 0;
	else return a1-a2;
}

int numcmp( char *p1,  char *p2)
{
  double v1, v2;

  v1 = atof(p1);
  v2 = atof(p2);
  if (v1 < v2)
    return -1;
  else if (v1 > v2)
    return 1;
  else
    return 0;
}

int main(int argc, char *argv[])
{
  int nlines;
  int numeric = FALSE;
  int i;

  for (i = 1; i < argc; i++) {
    if (*argv[i] == '-') {
      switch (*(argv[i] + 1)) {
        case 'n':  numeric = TRUE;  break;
        case 'r':  reverse = TRUE;  break;
        case 'f':  ignoreCase = TRUE;  break;
        default:
          fprintf(stderr, "invalid switch '%s'\n", argv[i]);
          return EXIT_FAILURE;
      }
    }
  }

  if ((nlines = readlines(lineptr, MAXLINES)) >= 0) {
    myQsort((void **)lineptr,0, nlines-1, numeric ? numcmp : pstrcmp);//numcmp和pstrcmp就是函数名
    writelines(lineptr, nlines);
    return EXIT_SUCCESS;
  } else {
    fputs("input too big to sort\n", stderr);
    return EXIT_FAILURE;
  }
}
运行结果:
zy@zy:~/Documents/eclipseWorkSpace/test/src$ ./a.out -f
A
B
C
a
b
c


A
a
B
b
C
c
zy@zy:~/Documents/eclipseWorkSpace/test/src$ 

可以看出声明为一个char *p1就是一个数组的形式,下面直接打印p1的时候看出来,当*p1


Exercise 5-16. Add the -d (‘‘directory order’’) option, which makes comparisons only on letters, numbers and blanks. Make sure it works in conjunction with -f.
答:暂时未找到合适的解法。

5.12 复杂声明

Exercise 5-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.)
答:暂时未找到合适的解法。

Exercise 5-18. Make dcl recover from input errors.
答:
暂时未找到合适的解法。
Exercise 5-19. Modify undcl so that it does not add redundant parentheses to declarations.
答: 暂时未找到合适的解法。
Exercise 5-20. Expand dcl to handle declarations with function argument types, qualifiers like const, and so on.
答: 暂时未找到合适的解法。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值