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

exercise7-1

编写一个程序,根据它自身被调用时存放在argv[0]中的名字,实现将大写字母转换为小写字母或将小写字母转换为大写字母的功能。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

int main(int argc, char **argv)
{
    int (*handler[2])(int) = {tolower, toupper};

    int type = strcmp(argv[0], "./lower") == 0 ? 0 : strcmp(argv[0], "./upper") == 0 ? 1 : -1;

    if (type == -1) {
        fprintf(stderr, "Usage: %s [lower|upper]\n", argv[0]);
    } else {
        int c;
        while ((c = getchar()) != EOF)
            putchar(handler[type](c));
    }
}

运行:

1
2
3
4
5
6
7
8
9
➜  ch07 git:(main) ✗ gcc -o lower Exercise7-1.c 
➜  ch07 git:(main) ✗ ./lower               
This
this
IYUSDbusaIs
iyusdbusais
➜  ch07 git:(main) ✗ gcc -o other Exercise7-1.c
➜  ch07 git:(main) ✗ ./other 
Usage: ./other [lower|upper]

exercise7-3

改写 minprintf 函数,使它能完成 printf 函数的更多功能。

 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
void minprintf(char *fmt, ...)
{
    va_list ap; /* points to each unnamed arg in turn */
    char *p, *sval;
    int ival;
    double dval;
    va_start(ap, fmt); /* make ap point to 1st unnamed arg */
    for (p = fmt; *p; p++)
    {
        if (*p != '%')
        {
            putchar(*p);
            continue;
        }
        switch (*++p)
        {
        case 'd':
            ival = va_arg(ap, int);
            printf("%d", ival);
            break;
        case 'f':
            dval = va_arg(ap, double);
            printf("%f", dval);
            break;
        case 's':
            for (sval = va_arg(ap, char *); *sval; sval++)
                putchar(*sval);
            break;
        case 'c':   // 字符
            ival = va_arg(ap, int);
            printf("%c", ival);
            break;
        case 'x':   // 十六进制
            ival = va_arg(ap, int);
            printf("%x", ival);
            break;
        case 'o':   // 八进制
            ival = va_arg(ap, int);
            printf("%o", ival);
            break;
        case '%':   // 百分号
            putchar('%');
            break;
        case 'g':   // 浮点数
            dval = va_arg(ap, double);
            printf("%g", dval);
            break;
        default:
            putchar(*p);
            break;
        }
    }
    va_end(ap); /* clean up when done */
}

int main()
{
    minprintf("int: %d, float: %g, string: %s\n", 10, 3.14, "hello");
    minprintf("char: %c, hex: %x, oct: %o, double: %g, percent: %%\n", 'a', 255, 255, 3.1415926);
    return 0;
}

运行:

1
2
int: 10, float: 3.14, string: hello
char: a, hex: ff, oct: 377, double: 3.14159, percent: %

exercise7-4

类似于上一节中的函数 minprintf,编写 scanf 函数的一个简化版本。

 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
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>

void minscanf(char *fmt, ...)
{
    va_list ap;
    char *p, *sval;
    int *ival;
    float *dval;
    unsigned *uval;
    int c;

    va_start(ap, fmt);
    for (p = fmt; *p; p++)
    {
        while (*p != '%')
            p++;
        switch (*++p)
        {
        case 'd':
            ival = va_arg(ap, int *);
            scanf("%d", ival);
            break;
        case 'f':
            dval = va_arg(ap, float *);
            scanf("%f", dval);
            break;
        case 's':
            sval = va_arg(ap, char *);
            scanf("%s", sval);
            break;
        case 'c':
            ival = va_arg(ap, int *);
            *ival = getchar();
            break;
        default:
            break;
        }
    }
    va_end(ap);
}

int main()
{
    int i;
    float f;
    char s[100];
    char c;

    printf("Enter an integer: ");
    minscanf("%d", &i);
    printf("i = %d\n", i);
    printf("Enter a float: ");
    minscanf("%f", &f);
    printf("f = %f\n", f);
    printf("Enter a string: ");
    minscanf("%s", s);
    printf("s = %s\n", s);
    getchar(); // clear the input buffer
    printf("Enter a character: ");
    minscanf("%c", &c);
    printf("c = %c\n", c);
}

运行:

1
2
3
4
5
6
7
8
Enter an integer: 1
i = 1
Enter a float: 3.14
f = 3.140000
Enter a string: ioio
s = ioio
Enter a character: a
c = a

exercise7-5

改写第 4 章中的后缀计算器程序,用 scanf 函数和(或)sscanf 函数实现输入以及数的转换。

其他不用改变,只需要改变主函数即可,将

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
int main()
{
    int type;
    double op2;
    char s[MAXOP];
    printf("Please input expression:\n");
    while ((type = getop(s)) != EOF)
    {
        switch (type)
        {
        case NUMBER:
            push(atof(s));
            break;
        ...
        }
    }
}

修改为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
int main()
{
    int type;
    double op2;
    char s[MAXOP];
    printf("Please input expression:\n");
    while ((type = getop(s)) != EOF)
    {
        switch (type)
        {
        case NUMBER:
            int val;
            scanf("%f", &val);
            push(val);
            break;
        ...
        }
    }
}

exercise7-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
void filecmp(FILE *fp1, FILE *fp2)
{
    char *line1 = malloc(MAXLINE), *line2 = malloc(MAXLINE);
    int lineno = 1;
    while (fgets(line1, 100, fp1) != NULL && fgets(line2, 100, fp2) != NULL)
    {
        if (strcmp(line1, line2) != 0)
        {
            printf("difference in line %d\n", lineno);
            printf("line1: %s\n", line1);
            printf("line2: %s\n", line2);
            return;
        }
        lineno++;
    }
    if (feof(fp1) && feof(fp2))
        printf("files are identical\n");
    else if (feof(fp1))
        printf("file2 has extra lines\n");
    else
        printf("file1 has extra lines\n");
}

int main(int argc, char *argv[])
{
    FILE *fp1, *fp2;
    char *prog = argv[0];       /* program name for errors */
    if (argc == 1 || argc == 2) /* no args; copy standard input */
        fprintf(stderr, "Usage: %s file1 file2\n", prog);
    else
    {
        if ((fp1 = fopen(*++argv, "r")) == NULL)
        {
            fprintf(stderr, "%s: can't open %s\n",
                    prog, *argv);
            exit(1);
        }
        if ((fp2 = fopen(*++argv, "r")) == NULL)
        {
            fprintf(stderr, "%s: can't open %s\n",
                    prog, *argv);
            exit(1);
        }
        filecmp(fp1, fp2);
    }
    fclose(fp1);
    fclose(fp2);
    exit(0);
}
1
2
3
4
5
➜  ch07 git:(main) ✗ ./Exercise7-6 Exercise7-6.c Exercise7-5.c
difference in line 5
line1: #define MAXLINE 1000

line2: void filecopy(FILE *ifp, FILE *ofp)

exercise7-7

修改第 5 章的模式查找程序,使它从一个命名文件的集合中读取输入(有文件名参数时),如果没有文件名参数,则从标准输入中读取输入。当发现一个匹配行时,是否应该将相应的文件名打印出来?

exercise7-8

编写一个程序,以打印一个文件集合,每个文件从新的一页开始打印,并且打印每个文件相应的标题和页数。

 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
#include <stdio.h>
#include <stdlib.h>

#define MAXLINE 1024
#define LINES_PER_PAGE 3

void print_file(char *file_name)
{
    FILE *f;
    int page_number = 1;
    int line_count;
    int c;
    int new_page = 1;
    assert(file_name != NULL);
    char *line = malloc(MAXLINE);
    if ((f = fopen(file_name, "r")) != NULL)
    {
        while (fgets(line, MAXLINE, f) != NULL)
        {
            if (new_page)
            {
                printf("[%s] page %d starts\n", file_name, page_number);
                new_page = 0;
                line_count = 1;
            }
            if (!feof(f) && ++line_count > LINES_PER_PAGE)
            {
                /* print out the footer */
                printf("[%s] page %d ends\n", file_name, page_number);
                /* skip another line so we can see it on screen */
                putchar('\n');
                new_page = 1;
                page_number++;
            }
            else
            {
                fputs(line, stdout);
            }
        }
        /* skip another line so we can see it on screen */
        putchar('\n');
        fclose(f);
    }
}
int main(int argc, char *argv[])
{
    int i;
    if (argc < 2)
    {
        fputs("no files specified\n", stderr);
        return EXIT_FAILURE;
    }
    for (i = 1; i < argc; i++)
        print_file(argv[i]);
    
    return EXIT_SUCCESS;
}

运行

1
2
3
4
5
6
7
8
9
➜  ch07 git:(main) ✗ ./Exercise7-8 test.txt
[test.txt] page 1 starts
It’s quite a good summary, but it would have even better when taking into account the importance of the number of requested rows, expected by the Cassandra client.
1) “To perform the country index lookup, every node is queried, looks up the 'UK' partition and then looks up each user_accounts partition found. ”
[test.txt] page 1 ends

[test.txt] page 2 starts
2) “This leads to the conclusion that the best use case for Cassandra's secondary indexes is when p is approximately n i.e. the number of partitions is about equal to the number of nodes.”
What a narrow best use case ! Hopefully, there are other use cases where seconday index are fine (that is, for low-cardinality sets), or even finer (according to the number of resulting rows requested vs the cardinality of indexed values).

exercise7-9

类似于 isupper 这样的函数可以通过某种方式实现以达到节省空间或时间的目的。考虑节省空间或时间的实现方式。

1
2
3
4
# define isupper(c) __isctype((c), _ISupper)

# define __isctype(c, type) \
  ((*__ctype_b_loc ())[(int) (c)] & (unsigned short int) type)

使用了宏定义