部分答案参考了官方题解和网上的答案,仅供参考,可能也有部分bug未发现或解决。

exercise5-1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
int getint(int *pn)
{
    int c, sign;
    while (isspace(c = getch())) /* skip white space */
        ;
    if (!isdigit(c) && c != EOF && c != '+' && c != '-')
    {
        ungetch(c); /* it is not a number */
        return 0;
    }
    sign = (c == '-') ? -1 : 1;
    if (c == '+' || c == '-')
        c = getch();
    for (*pn = 0; isdigit(c); c = getch())
        *pn = 10 * *pn + (c - '0');
    *pn *= sign;
    if (c != EOF)
        ungetch(c);
    return c;
}

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <stdio.h>
#include <ctype.h>

#define SIZE 100
#define BUFSIZE 100

char buf[BUFSIZE]; /* buffer for ungetch */
int bufp = 0;      /* next free position in buf */
int notdigit = 0;  /* flag for not a digit */

/* getint: get next integer from input into *pn */
int getint(int *pn)
{
    notdigit = 0;
    int c, sign, foundSign;
    while (isspace(c = getch())) /* skip white space */
        ;
    if (!isdigit(c) && c != EOF && c != '+' && c != '-')
    {
        notdigit = 1;
        // 若不是数字,将字符放回输入流,但是下次依然会读取到这个字符,所以需要注释掉ungtch(c)
        // ungetch(c); /* it is not a number */
        return 0;
    }
    sign = (c == '-') ? -1 : 1;
    if (foundSign = (c == '+' || c == '-'))
        c = getch();
    if (c != EOF && !isdigit(c))
    {
        // ungetch(c);
        if (foundSign)
            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 main()
{
    int n, array[SIZE], getint(int *);
    for (n = 0; n < SIZE && getint(&array[n]) != EOF; n++)
    {
        if (notdigit)
        {
            printf("Not a number\n");
            continue;
        }
        printf("%d\n", array[n]);
    }
}
1
2
3
4
5
6
7
8
9
-1 2
-1
2
s2
Not a number
2
-s2
0
-2

exercise5-2

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

代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
int getfloat(float *pn)
{
    notdigit = 0;
    int c, sign, foundSign;
    float power;

    while (isspace(c = getch())) /* skip white space */
        ;
    if (!isdigit(c) && c != EOF && c != '+' && c != '-' && c != '.')
    {
        notdigit = 1;
        return 0;
    }
    sign = (c == '-') ? -1 : 1;
    if (foundSign = (c == '+' || c == '-'))
        c = getch();
    if (c != EOF && !isdigit(c) && c != '.')
    {
        if (foundSign)
            ungetch((sign == -1) ? '-' : '+');   // 将符号放回
        return 0;
    }
    for (*pn = 0; isdigit(c); c = getch())
        *pn = 10 * *pn + (c - '0');
    if (c == '.')
        c = getch();
    for (power = 1.0; isdigit(c); c = getch())
    {
        *pn = 10 * *pn + (c - '0');
        power *= 10;
    }
    *pn = sign * (*pn / power);
    if (c != EOF)
        ungetch(c);
    return c;
}

运行:

1
2
1.23
1.230000

exercise5-3

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

代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#define MAXLINE 1024

/* strcat: 将 t 指向的字符串复制到 s 指向的字符串的尾部 */
void mstrcat(char *s, const char *t) {
    // 找到 s 的末尾
    while (*s) {
        s++;
    }
    // 将 t 的内容复制到 s 的末尾
    while (*t) {
        *s = *t;
        s++;
        t++;
    }
    // 添加字符串结束符
    *s = '\0';
}

int main() {
    char s[MAXLINE];
    char t[MAXLINE];

    printf("Please input S and T:\n");
    scanf("%s", s);
    scanf("%s", t);
    mstrcat(s, t);

    printf("ans is: %s\n", s);
    
    return 0;
}

运行:

1
2
3
Please input S and T:
abc def
ans is: abcdef

exercise5-4

编写函数 strend(s, t)。如果字符串 t 出现在字符串 s 的尾部,该函数返回 1;否则返回 0。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
int strend(const char *s, const char *t) {
    int i = strlen(s) - 1;
    int j = strlen(t) - 1;

    if (j > i)
        return 0;

    while (j >= 0) 
        if (s[i--] != t[j--])
            return 0;
    
    return 1;
}

int main() {
    char s[MAXLINE];
    char t[MAXLINE];

    printf("Please input S and T:\n");
    scanf("%s", s);
    scanf("%s", t);

    if (strend(s, t)) 
        printf("T appears at the end of S.\n");
    else 
        printf("T does not appear at the end of S.\n");
}

运行:

1
2
3
4
Please input S and T:
good
o
String T does not appear at the end of string S.

exercise5-5

实现库函数 strncpy、strncat 和 strncmp,它们最多对参数字符串中的前 n 个字符进行操作。例如,函数 strncpy(s, t, n)将 t 中最多前 n 个字符复制到 s中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/* mstrncpy: 将 t 中最多前 n 个字符复制到 s 中 */
char *mstrncpy(char *s, const char *t, int n)
{
    char *ans = s;
    while (n-- && (*s++ = *t++))
        ;
    while ((n--) > 0)
        *s++ = '\0';
    return ans;
}

/* mstrncat: 将 t 中最多前 n 个字符连接到 s 的末尾 */
char *mstrncat(char *s, const char *t, int n)
{
    char *ans = s;
    s += strlen(s);
    while (n-- && (*s++ = *t++))
        ;
    *s = '\0';
    return ans;
}

/* mstrncmp: 比较 s 和 t 中最多前 n 个字符 */
int mstrncmp(const char *s, const char *t, int n)
{
    while (n-- && *s && (*s++ == *t++))
        ;
    return n == -1 ? 0 : (*s - *t) != 0;
}

int main()
{
    char s1[MAXLINE] = "gooooood";
    char s2[MAXLINE] = "idea";
    char s3[MAXLINE] = "good";

    int n1 = 4;
    int result = mstrncmp(s1, s3, n1);
    printf("mstrncmp(%s, %s, %d):\n %d\n", s1, s3, n1, result);

    int n2 = 5;
    mstrncpy(s1, s3, 5);
    printf("mstrncpy(%s, %s, %d):\n %s\n", s1, s3, n2, s1);

    int n3 = 3;
    mstrncat(s2, s3, 3);
    printf("mstrncat(%s, %s, %d):\n %s\n", s2, s3, n3, s2);

    return 0;
}

运行:

1
2
3
4
5
6
mstrncmp(gooooood, good, 4):
 1
mstrncpy(good, good, 5):
 good
mstrncat(ideagoo, good, 3):
 ideagoo

exercise5-7

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <stdio.h>
#include <string.h>
#include <malloc.h>

#define MAXLINES 5000 /* max #lines to be sorted */
#define MAXLEN 1000   /* max length of any input line */

char *lineptr[MAXLINES]; /* pointers to text lines */

int Getline(char line[])
{
    int c, i;
    for (i = 0; i < MAXLEN - 1 && (c = getchar()) != EOF && c != '\n'; i++)
        line[i] = c;
    if (c == '\n')
    {
        line[i] = c;
        i++;
    }
    line[i] = '\0';
    return i;
}

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

/* qsort: sort v[left]...v[right] into increasing order */
void qsort(char *v[], int left, int right)
{
    int i, last;
    void swap(char *v[], int i, int j);
    if (left >= right) /* do nothing if array contains */
        return;        /* fewer than two elements */
    swap(v, left, (left + right) / 2);
    last = left;
    for (i = left + 1; i <= right; i++)
        if (strcmp(v[i], v[left]) < 0)
            swap(v, ++last, i);
    swap(v, left, last);
    qsort(v, left, last - 1);
    qsort(v, last + 1, right);
}

/* readlines: read input lines */
int readlines(char *lineptr[], int maxlines)
{
    int len, nlines;
    char *p, line[MAXLEN];
    nlines = 0;
    while ((len = Getline(line)) > 0)
        if (nlines >= maxlines || (p = malloc(len)) == NULL)
            return -1;
        else
        {
            line[len - 1] = '\0'; /* delete newline */
            strcpy(p, line);
            lineptr[nlines++] = p;
        }
    return nlines;
}

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

/* sort input lines */
int main()
{
    int nlines; /* number of input lines read */
    printf("Please input lines:\n");
    if ((nlines = readlines(lineptr, MAXLINES)) >= 0)
    {
        qsort(lineptr, 0, nlines - 1);
        printf("Sorted lines(sorted by first word):\n");
        writelines(lineptr, nlines);
        return 0;
    }
    else
    {
        printf("error: input too big to sort\n");
        return 1;
    }
}

增加函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
int readlines2(char linesarray[][MAXLEN], int maxlines)
{
    int len, nlines;
    nlines = 0;
    while ((len = Getline(linesarray[nlines])) > 0)
        if (nlines >= maxlines)
            return -1;
        else
            linesarray[nlines++][len - 1] = '\0';
    return nlines;
}

int main(int argc, char *argv[])
{
    printf("Please input lines:");
    if (argc > 1 && *argv[1] == '2')
    {
        printf("(by readlines2):\n");
        int nlines = readlines2(linesarray, MAXLINES);
    }
    else
    {
        printf("(by readlines):\n");
        int nlines = readlines(lineptr, MAXLINES);
    }
    return 0;
}

exercise5-8

函数 day_of_year 和 month_day 中没有进行错误检查,请解决该问题。

对输入的月份、天数进行检查

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#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 (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 (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;
}

int main(void)
{
    int year, month, day, yearday;
    printf("Test day_of_year()\nPlease input year, month and day: ");
    scanf("%d %d %d", &year, &month, &day);
    yearday = day_of_year(year, month, day);
    if (yearday == -1)
        printf("Wrong date!\n");
    else 
        printf("%d-%d-%d: the %d day in %d\n", year, month, day, yearday, year);
    
    printf("\nTest month_day()\nPlease input year and yearday: ");
    scanf("%d %d", &year, &yearday);
    int ans = month_day(year, yearday, &month, &day);
    if (ans == -1)
        printf("Wrong date!\n");
    else 
        printf("the %d day in %d: %d-%d-%d\n", yearday, year, year, month, day);
    return 0;
}

运行:

1
2
3
4
5
6
7
Test day_of_year()
Please input year, month and day: 2002 10 14
2002-10-14: the 287 day in 2002

Test month_day()
Please input year and yearday: 2002 287
the 287 day in 2002: 2002-10-14

exercise5-9

用指针方式代替数组下标方式改写函数 day_of_year 和 month_day。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int day_of_year_ptr(int year, int month, int day)
{
    int i, leap;
    if (month < 1 || month > 12 || day < 1)
        return -1;
    leap = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
    char *p = &daytab[leap][1];
    if (day > *(p + month - 1))
        return -1;
    for (i = 1; i < month; i++)
    {
        day += *p;
        ++p;
    }
    return day;
}
/* month_day: set month, day from day of year */
int month_day_ptr(int year, int yearday, int *pmonth, int *pday)
{
    int i, leap;
    if (yearday < 1)
        return -1;
    leap = year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
    if ((leap && yearday > 366) || (!leap && yearday > 365))
        return -1;
    char *p = &daytab[leap][1];
    for (i = 1; yearday > *p; i++)
    {
        yearday -= *p;
        ++p;
    }
    *pmonth = i;
    *pday = yearday;
    return 0;
}

运行:

1
2
3
4
5
6
7
Test day_of_year_ptr()
Please input year, month and day: 2002 10 14
2002-10-14: the 287 day in 2002

Test month_day_ptr()
Please input year and yearday: 2002 287
the 287 day in 2002: 2002-10-14

exercise5-10

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#define NUMBER '0'
#define MAXVAL 100  /* maximum depth of val stack */
int sp = 0;         /* next free stack position */
double val[MAXVAL]; /* value stack */

int isnumber(char *s)
{
    if (strlen(s) == 1 && (s[0] == '/' || s[0] == '*' || s[0] == '+' || s[0] == '-'))
        return s[0];

    int i = 0;
    if (s[i] == '-' || s[i] == '+')
        i++;
    for (; isdigit(s[i]); i++)
        ;
    if (s[i] == '.')
        i++;
    for (; isdigit(s[i]); i++)
        ;
    return s[i] == '\0' ? NUMBER : -1;
}

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

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

int main(int argc, char **argv)
{
    int i;
    double value;
    for (i = 1; i < argc; ++i)
    {
        switch (isnumber(argv[i]))
        {
        case NUMBER:
            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:
            printf("wrong argument: %s\n", argv[i]);
            break;
        }
    }
    printf("%g\n", pop());
    return 0;
}

运行(命令行中,’*‘需要反斜杠来转义):

1
2
➜  ch05 git:(main) ✗ ./Exercise5-10 2 3 4 + \*
14

exercise5-13

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

#define MAXLINES 5000 /* max #lines to be sorted */
#define MAXLEN 1000   /* max length of any input line */

char *lineptr[MAXLINES];           /* pointers to text lines */
char linesarray[MAXLINES][MAXLEN]; /*array that store lines*/

int Getline(char line[])
{
    int c, i;
    for (i = 0; i < MAXLEN - 1 && (c = getchar()) != EOF && c != '\n'; i++)
        line[i] = c;
    if (c == '\n')
    {
        line[i] = c;
        i++;
    }
    line[i] = '\0';
    return i;
}

/* readlines: read input lines */
int readlines(char *lineptr[], int maxlines)
{
    int len, nlines;
    char *p, line[MAXLEN];
    nlines = 0;
    while ((len = Getline(line)) > 0)
        if (nlines >= maxlines || (p = malloc(len)) == NULL)
            return -1;
        else
        {
            line[len - 1] = '\0'; /* delete newline */
            strcpy(p, line);
            lineptr[nlines++] = p;
        }
    return nlines;
}

/* writelines: write output lines */
void writelines(char *lineptr[], int nlines, int n)
{
    int i;
    for (i = nlines - n > 0 ? nlines - n : 0; i < nlines; i++)
        printf("%s\n", lineptr[i]);
}

int main(int argc, char *argv[])
{
    printf("Please input lines:\n");
    int nlines = readlines(lineptr, MAXLINES);
    if (argc > 1 && argv[1][0] == '-')
    {
        int n = atoi(argv[1] + 1);
        printf("The last %d lines are:\n", n);
        writelines(lineptr, nlines, n);
    }
    else {
        printf("The last 10 lines are:\n");
        writelines(lineptr, nlines, 10);
    }
    
    return 0;
}

运行:

1
2
3
4
5
6
7
8
➜  ch05 git:(main) ✗ ./Exercise5-13 -2
Please input lines:
sdui
qi
a
The last 2 lines are:
qi
a

exercise5-14

修改排序程序,使它能处理-r 标记。该标记表明,以逆序(递减)方式排序。要保证-r 和-n 能够组合在一起使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
int reverse;

int mstrcmp(char *s1, char *s2)
{
    char *news1 = reverse ? s2 : s1;
    char *news2 = reverse ? s1 : s2;
    return strcmp(news1, news2);
}

/* numcmp: compare s1 and s2 numerically */
int mnumcmp(char *s1, char *s2)
{
    char *news1 = reverse ? s2 : s1;
    char *news2 = reverse ? s1 : s2;

    double v1, v2;
    v1 = atof(news1);
    v2 = atof(news2);
    if (v1 < v2)
        return -1;
    else if (v1 > v2)
        return 1;
    else
        return 0;
}

/* qsort: sort v[left]...v[right] into increasing order */
void mqsort(void *v[], int left, int right, int (*comp)(void *, void *))
{
    int i, last;
    if (left >= right) /* do nothing if array contains */
        return;        /* fewer than two elements */
    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);
    mqsort(v, left, last - 1, comp);
    mqsort(v, last + 1, right, comp);
}

int main(int argc, char *argv[])
{
    int nlines;      /* number of input lines read */
    int numeric = 0; /* 1 if numeric sort */
    reverse = 0;     /* 1 if reverse sort */
    if (argc > 1 && strcmp(argv[1], "-n") == 0)
        numeric = 1;
    if (argc > 2 && strcmp(argv[2], "-r") == 0)
        reverse = 1;
    printf("Please input data:\n");
    if ((nlines = readlines(lineptr, MAXLINES)) >= 0)
    {
        mqsort((void **)lineptr, 0, nlines - 1,
              (int (*)(void *, void *))(numeric ? mnumcmp : mstrcmp));
        printf("After sorted:\n");
        writelines(lineptr, nlines);
        return 0;
    }
    else
    {
        printf("input too big to sort\n");
        return 1;
    }
}

运行:

1
2
3
4
5
6
7
8
9
➜  ch05 git:(main) ✗ ./Exercise5-14 -n -r
Please input data:
76 
44   
98
After sorted:
98
76
44

exercise5-15

增加选项-f,使得排序过程不考虑字母大小写之间的区别。例如,比较 a 和 A 时认为它们相等。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
int mstrcmp(char *s1, char *s2)
{
    char *news1 = reverse ? s2 : s1;
    char *news2 = reverse ? s1 : s2;
    return fold == 1 ? strcasecmp(news1, news2) : strcmp(news1, news2);
}

int main(int argc, char *argv[])
{
    int nlines;      /* number of input lines read */
    int numeric = 0; /* 1 if numeric sort */
    reverse = 0;     /* 1 if reverse sort */
    for (int i = 1; i < argc; i++)
        switch (argv[i][1])
        {
        case 'n':
            numeric = 1;
            break;
        case 'r':
            reverse = 1;
            break;
        case 'f':
            fold = 1;
            break;
        }
    printf("Please input data:\n");
    if ((nlines = readlines(lineptr, MAXLINES)) >= 0)
    {
        mqsort((void **)lineptr, 0, nlines - 1,
               (int (*)(void *, void *))(numeric ? mnumcmp : mstrcmp));
        printf("After sorted:\n");
        writelines(lineptr, nlines);
        return 0;
    }
    else
    {
        printf("input too big to sort\n");
        return 1;
    }
}
1
2
3
4
5
6
7
➜  ch05 git:(main) ✗ ./Exercise5-14 -f
Please input data:
aB
Ab
After sorted:
aB
Ab

exercise5-16

增加选项-d(代表目录顺序)。该选项表明,只对字母、数字和空格进行比较。要保证该选项可以和-f 组合在一起使用。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
int charcmp(char c1, char c2)
{
    if (dir)
    {
        if (!isalnum(c1) && c1 != ' ')
            return 0;
        if (!isalnum(c2) && c2 != ' ')
            return 0;
    }
    if (fold)
    {
        c1 = tolower(c1);
        c2 = tolower(c2);
    }
    return c1 - c2;
}

int mstrcmp(char *s1, char *s2)
{
    char *news1 = reverse ? s2 : s1;
    char *news2 = reverse ? s1 : s2;

    while (*news1 && *news2)
    {
        int cmp = charcmp(*news1, *news2);
        if (cmp != 0)
            return cmp;
        news1++;
        news2++;
    }
    return *news1 ? 1 : (*news2 ? -1 : 0);
}

/* numcmp: compare s1 and s2 numerically */
int mnumcmp(char *s1, char *s2)
{
    char *news1 = reverse ? s2 : s1;
    char *news2 = reverse ? s1 : s2;

    double v1, v2;
    v1 = atof(news1);
    v2 = atof(news2);
    if (v1 < v2)
        return -1;
    else if (v1 > v2)
        return 1;
    else
        return 0;
}

/* qsort: sort v[left]...v[right] into increasing order */
void mqsort(void *v[], int left, int right, int (*comp)(void *, void *))
{
    int i, last;
    if (left >= right) /* do nothing if array contains */
        return;        /* fewer than two elements */
    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);
    mqsort(v, left, last - 1, comp);
    mqsort(v, last + 1, right, comp);
}

int main(int argc, char *argv[])
{
    int nlines;      /* number of input lines read */
    int numeric = 0; /* 1 if numeric sort */
    reverse = 0;     /* 1 if reverse sort */
    for (int i = 1; i < argc; i++)
        switch (argv[i][1])
        {
        case 'n':
            numeric = 1;
            break;
        case 'r':
            reverse = 1;
            break;
        case 'f':
            fold = 1;
            break;
        case 'd':
            dir = 1;
            break;
        }
    printf("Please input data:\n");
    if ((nlines = readlines(lineptr, MAXLINES)) >= 0)
    {
        mqsort((void **)lineptr, 0, nlines - 1,
               (int (*)(void *, void *))(numeric ? mnumcmp : mstrcmp));
        printf("After sorted:\n");
        writelines(lineptr, nlines);
        return 0;
    }
    else
    {
        printf("input too big to sort\n");
        return 1;
    }
}

运行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
➜  ch05 git:(main) ✗ ./Exercise5-16 -d -f
Please input data:
A+b
a-B
After sorted:
A+b
a-B
Please input data:
a-B
A+b
After sorted:
a-B
A+b

exercise5-18

修改 dcl 程序,使它能够处理输入中的错误。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#define MAXTOKEN 100
#define BUFSIZE 100

enum
{
    NAME,
    PARENS,
    BRACKETS
};
int tokentype;           /* type of last token */
char token[MAXTOKEN];    /* last token string */
char name[MAXTOKEN];     /* identifier name */
char datatype[MAXTOKEN]; /* data type = char, int, etc. */
char out[1000];

void dcl(void);
void dirdcl(void);

/* clear remaining characters in input line to recover from errors */
void clear_input()
{
    int c;
    while ((c = getch()) != '\n' && c != EOF)
        ;
}

/* handle and print error messages */
void errmsg(const char *msg)
{
    printf("Error: %s\n", msg);
    clear_input(); // Skip to the end of the current line after an error
}

/* gettoken: return next token */
int gettoken(void)
{
    int c;
    char *p = token;

    while ((c = getch()) == ' ' || c == '\t') // Skip whitespace
        ;

    if (c == '(')
    {
        if ((c = getch()) == ')')
        {
            strcpy(token, "()");
            return tokentype = PARENS;
        }
        else
        {
            ungetch(c);
            return tokentype = '(';
        }
    }
    else if (c == '[')
    {
        for (*p++ = c; (*p++ = getch()) != ']';)
        {
            if (p - token >= MAXTOKEN - 1) // Prevent buffer overflow
            {
                errmsg("brackets too long or unclosed");
                return tokentype = -1;
            }
        }
        *p = '\0';
        return tokentype = BRACKETS;
    }
    else if (isalpha(c))
    {
        for (*p++ = c; isalnum(c = getch());)
        {
            if (p - token >= MAXTOKEN - 1) // Prevent buffer overflow
            {
                errmsg("name too long");
                return tokentype = -1;
            }
            *p++ = c;
        }
        *p = '\0';
        ungetch(c);
        return tokentype = NAME;
    }
    else
    {
        return tokentype = c;
    }
}

/* dcl: parse a declarator */
void dcl(void)
{
    int ns;
    for (ns = 0; gettoken() == '*';) /* count *'s */
        ns++;
    dirdcl();
    while (ns-- > 0)
        strcat(out, " pointer to");
}

/* dirdcl: parse a direct declarator */
void dirdcl(void)
{
    int type;

    if (tokentype == '(')
    {
        dcl();
        if (tokentype != ')')
        {
            errmsg("missing )");
            return;
        }
    }
    else if (tokentype == NAME)
    {
        strcpy(name, token);
    }
    else
    {
        errmsg("expected name or (dcl)");
        return;
    }

    while ((type = gettoken()) == PARENS || type == BRACKETS)
    {
        if (type == PARENS)
        {
            strcat(out, " function returning");
        }
        else
        {
            strcat(out, " array");
            strcat(out, token);
            strcat(out, " of");
        }
    }
}

int main() /* convert declaration to words */
{
    while (gettoken() != EOF)
    {                            /* 1st token on line */
        strcpy(datatype, token); /* is the datatype */
        out[0] = '\0';
        dcl(); /* parse rest of line */
        if (tokentype != '\n')
            printf("syntax error\n");
        printf("%s: %s %s\n", name, out, datatype);
    }
}

运行:

1
2
3
4
char *func()
func:  function returning pointer to char
char (*func())()
func:  function returning pointer to function returning char

exercise5-19

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/* undcl: convert word description to declaration */
int undcl(void)
{
    int type;
    char temp[MAXTOKEN];
    int parentheses_needed = 0; // track when parentheses are necessary

    while (gettoken() != EOF)
    {
        strcpy(out, token); // Initialize out with the first token
        while ((type = gettoken()) != '\n')
        {
            if (type == PARENS || type == BRACKETS)
            {
                strcat(out, token); // Append function or array notation
            }
            else if (type == '*')
            {
                // Only add parentheses if needed for precedence (i.e., if out is a function or array)
                if (parentheses_needed)
                {
                    sprintf(temp, "(*%s)", out);
                    parentheses_needed = 0; // reset parentheses flag
                }
                else
                {
                    sprintf(temp, "*%s", out); // regular pointer, no parentheses needed
                }
                strcpy(out, temp);
            }
            else if (type == NAME)
            {
                sprintf(temp, "%s %s", token, out); // Add name to out
                strcpy(out, temp);
            }
            else
            {
                printf("invalid input at %s\n", token);
            }
        }
        printf("%s\n", out);
    }
    return 0;
}

运行:

1
2
x () * [] * () char
char (*(*x())[])()

exercise5-20

扩展 dcl 程序的功能,使它能够处理包含其它成分的声明,例如带有函数参数类型的声明、带有类似于 const 限定符的声明等。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#define MAXTOKEN 100
#define BUFSIZE 100

enum
{
    NAME,
    PARENS,
    BRACKETS,
    QUALIFIER,
    TYPE
};

int tokentype;           /* type of last token */
char token[MAXTOKEN];    /* last token string */
char name[MAXTOKEN];     /* identifier name */
char datatype[MAXTOKEN]; /* data type = char, int, etc. */
char out[1000];
char buf[BUFSIZE]; /* buffer for ungetch */
int bufp = 0;      /* next free position in buf */

void dcl(void);
void dirdcl(void);

/* get a (possibly pushed-back) character */
int getch(void)
{
    return (bufp > 0) ? buf[--bufp] : getchar();
}

/* push character back on input */
void ungetch(int c)
{
    if (bufp >= BUFSIZE)
        printf("ungetch: too many characters\n");
    else
        buf[bufp++] = c;
}

/* clear remaining characters in input line to recover from errors */
void clear_input()
{
    int c;
    while ((c = getch()) != '\n' && c != EOF)
        ;
}

/* handle and print error messages */
void errmsg(const char *msg)
{
    printf("Error: %s\n", msg);
    clear_input(); // Skip to the end of the current line after an error
}

/* gettoken: return next token */
int gettoken(void)
{
    int c;
    char *p = token;

    while ((c = getch()) == ' ' || c == '\t') // Skip whitespace
        ;

    if (c == '(')
    {
        if ((c = getch()) == ')')
        {
            strcpy(token, "()");
            return tokentype = PARENS;
        }
        else
        {
            ungetch(c);
            return tokentype = '(';
        }
    }
    else if (c == '[')
    {
        for (*p++ = c; (*p++ = getch()) != ']';)
        {
            if (p - token >= MAXTOKEN - 1) // Prevent buffer overflow
            {
                errmsg("brackets too long or unclosed");
                return tokentype = -1;
            }
        }
        *p = '\0';
        return tokentype = BRACKETS;
    }
    else if (isalpha(c))
    {
        for (*p++ = c; isalnum(c = getch());)
        {
            if (p - token >= MAXTOKEN - 1) // Prevent buffer overflow
            {
                errmsg("name too long");
                return tokentype = -1;
            }
            *p++ = c;
        }
        *p = '\0';
        ungetch(c);

        // Handle qualifiers and types like const, volatile
        if (strcmp(token, "const") == 0 || strcmp(token, "volatile") == 0)
        {
            return tokentype = QUALIFIER;
        }

        return tokentype = NAME;
    }
    else
    {
        return tokentype = c;
    }
}

/* dcl: parse a declarator */
void dcl(void)
{
    int ns;
    for (ns = 0; gettoken() == '*';) /* count *'s */
        ns++;
    dirdcl();
    while (ns-- > 0)
        strcat(out, " pointer to");
}

/* dirdcl: parse a direct declarator */
void dirdcl(void)
{
    int type;

    if (tokentype == '(')
    {
        dcl();
        if (tokentype != ')')
        {
            errmsg("missing )");
            return;
        }
    }
    else if (tokentype == NAME)
    {
        strcpy(name, token);
    }
    else
    {
        errmsg("expected name or (dcl)");
        return;
    }

    while ((type = gettoken()) == PARENS || type == BRACKETS || type == QUALIFIER)
    {
        if (type == PARENS)
        {
            strcat(out, " function returning");
        }
        else if (type == BRACKETS)
        {
            strcat(out, " array");
            strcat(out, token);
            strcat(out, " of");
        }
        else if (type == QUALIFIER)
        {
            strcat(out, " ");
            strcat(out, token);
        }
    }
}

/* parse a function parameter list */
void parse_param_list(void)
{
    while (gettoken() != ')')
    {
        if (tokentype == NAME)
        {
            strcat(out, " parameter ");
            strcat(out, token);
        }
        else if (tokentype == ',')
        {
            strcat(out, ", ");
        }
        else if (tokentype == QUALIFIER || tokentype == TYPE)
        {
            strcat(out, " ");
            strcat(out, token);
        }
        else if (tokentype == '(')
        {
            parse_param_list(); // Recursively handle nested parentheses
        }
        else
        {
            errmsg("unexpected token in parameter list");
            break;
        }
    }
}

int main() /* convert declaration to words */
{
    while (gettoken() != EOF)
    {                            /* 1st token on line */
        strcpy(datatype, token); /* is the datatype */
        out[0] = '\0';
        dcl(); /* parse rest of line */
        if (tokentype != '\n')
            printf("syntax error\n");
        printf("%s: %s %s\n", name, out, datatype);
    }
}