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

exercise1-1

在你自己的系统中运行“hello, world”程序。再有意去掉程序中的部分内容,看看会得到什么出错信息。

1
2
3
main() {
    printf("hello world!");
}

除去头文件

不报错且能运行,但是会有警告warning: implicit declaration of function ‘printf’ [-Wimplicit-function-declaration]

除去返回类型

不报错且能运行,但是会有警告warning: return type defaults to ‘int’ [-Wimplicit-int]

exercise1-2

做个实验,当 printf 函数的参数字符串中包含\c(其中 c 是上面的转义字符序列中未曾列出的某一个字符)时,观察一下会出现什么情况。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int main(void)
{
    printf("Experiment\f to find out what\f happens when \fprintf 's argument string contains \\c\n");
    printf("---------\n");
    printf("Experiment\v to find out what \vhappens when printf 's \vargument string contains \\c\n");
    printf("---------\n");
    printf("Experiment\r to find out what \rhappens when printf 's \rargument string contains \\c\n");
    printf("---------\n");
    return 0;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Experiment
           to find out what
                            happens when 
                                         printf 's argument string contains \c
---------
Experiment
           to find out what 
                            happens when printf 's 
                                                   argument string contains \c
---------
argument string contains \c
---------

exercise1-3

修改温度转换程序,使之能在转换表的顶部打印一个标题

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int main() {
    float celsius;
    int fahr = 0, upper = 300, step = 30;

    printf("fahr to celsius\n");
    while (fahr <= upper) {
        celsius = (5.0 / 9) * (fahr - 32);
        printf("%5d  %6.2f\n", fahr, celsius);
        fahr += step;
    }
    return 0;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
fahr to celsius
    0   -17.78
   30    -1.11
   60    15.56
   90    32.22
  120    48.89
  150    65.56
  180    82.22
  210    98.89
  240   115.56
  270   132.22
  300   148.89

exercise1-4

编写一个程序打印摄氏温度转换为相应华氏温度的转换表。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int main() {
    float fahr;
    int celsius = -200, upper = 300, step = 30;

    printf("celsius to fahr\n");
    while (celsius <= upper) {
        fahr = (celsius + 32) * (9.0 / 5);
        printf("%5d   %6.1f\n", celsius, fahr);
        celsius += step;
    }
    return 0;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
celsius to fahr
 -200   -302.4
 -170   -248.4
 -140   -194.4
 -110   -140.4
  -80    -86.4
  -50    -32.4
  -20     21.6
   10     75.6
   40    129.6
   70    183.6
  100    237.6
  130    291.6
  160    345.6
  190    399.6
  220    453.6
  250    507.6
  280    561.6

exercise1-5

修改温度转换程序,要求以逆序(即按照从 300 度到 0 度的顺序)打印温度转换表。

1
2
3
4
5
6
int main()
{
    int fahr;
    for (fahr = 300; fahr >= 0; fahr = fahr - 20)
        printf("%3d %6.1f\n", fahr, (5.0 / 9) * (fahr - 32));
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
300  148.9
280  137.8
260  126.7
240  115.6
220  104.4
200   93.3
180   82.2
160   71.1
140   60.0
120   48.9
100   37.8
 80   26.7
 60   15.6
 40    4.4
 20   -6.7
  0  -17.8

exercise1-6 & exercise1-7

编写一个打印 EOF 值的程序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int main() {
    int c;
    // ctrl + D
    for (;;) {
        c = getchar();
        if (c == EOF) {
            printf("Value of EOF is %d.\n", c);
            return 0;
        }
    }
    return 0;
}
1
Value of EOF is -1.

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

int main() {
    int c, cb = 0, ct = 0, cn = 0;
    while ((c = getchar()) != EOF) {
        switch (c)
        {
        case ' ':
            cb++;
            break;
        case '\t':
            ct++;
            break;
        case '\n':
            cn++;
            break;
        default:
            break;
        }
    }
    printf("count of blankspace is %d, count of \\t is %d, count of \\n is %d\n",
        cb, ct, cn
    );
}
1
2
3
4
yyy     yy

hh 
count of blankspace is 3, count of \t is 2, count of \n is 3

exercise1-9

编写一个将输入复制到输出的程序,并将其中连续的多个空格用一个空格代替。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int main() {
    int c, flag = 0;
    while ((c = getchar()) != EOF) {
        if (c != ' ') {
            putchar(c);
            flag = 0;
        } else if (flag == 0) {
            putchar(c);
            // 遇到连续空格中的第一个,就将 flag 置为1
            flag = 1;
        }
    }
}

exercise1-10

编写一个将输入复制到输出的程序,并将其中的制表符替换为\t,把回退符替换为\b,把反斜杠替按为\。这样可以将制表符和回退符以可见的方式显示出来。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int main() {
    int c, flag = 0;
    while ((c = getchar()) != EOF)
        if (c == '\t')
            printf("\\t");
        else if (c == '\b')
            printf("\\b");
        else if (c == '\\')
            printf("\\\\");
        else
            putchar(c);
}
1
2
        DCS\d
\tDCS\\d\t

exercise1-11

你准备如何测试单词计数程序?如果程序中存在某种错误,那么什么样的输入最可能发现这类错误呢?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#define IN 1
#define OUT 0

int main() {
    int c, flag = OUT;
    int wc = 0;
    while ((c = getchar()) != EOF) {
        if (c != ' ' && c != '\t' && c != '\n') {
            if (flag == OUT) {
                wc++;
                flag = IN;
            }
        } else {
            if (flag == IN) {
                flag = OUT;
            }
        }
    }
    printf("count of word is = %d\n", wc);
}
1
2
my name is blank!
count of word is = 4

只需要将上面exercise1-10的程序进行修改,使用INOUT来记录当前遍历的字母的状态:是处于单词内部还是单词外部。如果遇到第一个非空格、换行、制表符字符,则将状态修改为IN,表示进入到了一个单词的内部,如果遇到了第一个上述字符,则将状态修改为OUT

exercise1-12

编写一个程序,以每行一个单词的形式打印其输入。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#define IN 1
#define OUT 0

int main() {
    int c, flag = OUT;
    int wc = 0;
    while ((c = getchar()) != EOF) {
        if (c != ' ' && c != '\t' && c != '\n') {
            if (flag == OUT) {
                wc++;
                flag = IN;
            }
            putchar(c);
        } else {
            if (flag == IN) {
                flag = OUT;
                putchar('\n');
            }
        }
    }
    printf("wc = %d\n", wc);
}
1
2
3
4
5
6
my name is blank!
my
name
is
blank!
wc = 4

只需要将 1-11 的代码稍加修改。

exercise1-13

编写一个程序,打印输入中单词长度的直方图。水平方向的直方图比较容易绘制,垂直方向的直方图则要困难些。

 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
#define IN 1
#define OUT 0
#define MAXLENGTH 4096

int main() {
    int c, flag = OUT, counts[MAXLENGTH] = {0};

    int l = 0, r = 0;   // 分别代表当前单词的左边界和遍历的下标,当遍历到单词的右边界时,把两者相减,得到单词长度。

    while ((c = getchar()) != EOF) {
        if (c != ' ' && c != '\t' && c != '\n') {
            if (flag == OUT) {
                flag = IN;
                l = r;
            }
        } else {
            if (flag == IN) {
                flag = OUT;
                counts[r - l - 1]++;    // 将对应的数组元素加一
            }
        }
        r++;
    }
    printf("length\tcounts\n");
    for (int i = 0; i < MAXLENGTH; i++) {
        if (counts[i] != 0) {
            printf("%6d\t", i + 1);
            while (counts[i]--)
                putchar('#');
            putchar('\n');
        }
    }
}
1
2
3
4
5
6
7
8
This is a sad story because I don't kown why.
length  counts
     1  ##
     2  #
     3  #
     4  ###
     5  ##
     7  #

exercise1-14

编写一个程序,打印输入中各个字符出现频度的直方图。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#define MAXVALUE 256

int main() {
    int c, counts[MAXVALUE] = {0};

    while ((c = getchar()) != EOF) 
        if (c != ' ' && c != '\t' && c != '\n') 
            counts[c]++;

    printf("character\tcounts\n");
    for (int i = 0; i < MAXVALUE; i++) {
        if (counts[i] != 0) {
            printf("%9c\t", i);
            while (counts[i]--)
                putchar('#');
            putchar('\n');
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
This is a sad story because I don't kown why.
character       counts
        '       #
        .       #
        I       #
        T       #
        a       ###
        b       #
        c       #
        d       ##
        e       ##
        h       ##
        i       ##
        k       #
        n       ##
        o       ###
        r       #
        s       #####
        t       ##
        u       #
        w       ##
        y       ##

exercise1-15

重新编写 1.2 节中的温度转换程序,使用函数实现温度转换计算。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
float ctof(float celsius) {
    return (celsius + 32) * (9.0 / 5);
}

float ftoc(float fahr) {
    return (5.0 / 9) * (fahr - 32);
}

int main() {
    int fahr = 0, celsius = -200, upper = 300, step = 30;

    printf("celsius\t  fahr\n");
    while (celsius <= upper) {
        printf("%7d\t  %4.1f\n", celsius, ctof(celsius));
        celsius += step;
    }

    printf("\nfahr\tcelsius\n");
    while (fahr <= upper) {
        celsius = (5.0 / 9) * (fahr - 32);
        printf("%4d\t%7.2f\n", fahr, ftoc(fahr));
        fahr += step;
    }
}
 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
celsius   fahr
   -200   -302.4
   -170   -248.4
   -140   -194.4
   -110   -140.4
    -80   -86.4
    -50   -32.4
    -20   21.6
     10   75.6
     40   129.6
     70   183.6
    100   237.6
    130   291.6
    160   345.6
    190   399.6
    220   453.6
    250   507.6
    280   561.6

fahr    celsius
   0     -17.78
  30      -1.11
  60      15.56
  90      32.22
 120      48.89
 150      65.56
 180      82.22
 210      98.89
 240     115.56
 270     132.22
 300     148.89

exercise1-16

修改打印最长文本行的程序的主程序 main,使之可以打印任意长度的输入行的长度,并尽可能多地打印文本。

 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
#include <stdio.h>
#define MAXLINE 4 

int Getline(char line[], int maxline);
void copy(char to[], char from[]);

int main() {
    int len;               
    int max;               
    char line[MAXLINE];    
    char longest[MAXLINE]; 
    max = 0;
    while ((len = Getline(line, MAXLINE)) > 0)
        if (len > max)
        {
            max = len;
            copy(longest, line);
        }
    if (max > 0) 
        printf("Maxlength is %d\n and the content that can be printed is:  %s\n", max, longest);
    return 0;
}

int Getline(char s[], int lim)
{
    // i存放输入的长度,j存放接受的长度
    int c, i, j;
    for (i = 0, j = 0; (c = getchar()) != EOF && c != '\n'; ++i) {
        if (j < lim - 1)
            s[j++] = c;
    }
    if (c == '\n') {
        if (j < lim - 1) {
            s[j] = '\n';
            j++;
        }
        i++;
    }
    s[j] = '\0';
    return i;
}

void copy(char to[], char from[])
{
    int i;
    i = 0;
    while ((to[i] = from[i]) != '\0')
        ++i;
}
1
2
3
this is a happy game
Maxlength is 21
 and the content that can be printed is  thi

exercise1-17

编写一个程序,打印长度大于 80 个字符的所有输入行。

 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
#include <stdio.h>
#define MINLINE 80 // 符合打印要求的最小字符数(包含\n)
#define MAXLINE 1024  // 每一行的最大字符数(包含\n)

int Getline(char line[]) {
    int c, i;
    for (i = 0; i < MAXLINE - 1 && (c = getchar()) != EOF && c != '\n'; i++)
        line[i] = c;
    // 退出循环:字符限制 || 文件末尾 || 行末尾
    // 如果c=='\n',说明以上的遍历没有达到字符限制
    // 否则,则需要立刻结束遍历,将结尾置为'\0'
    if (c == '\n') {
        line[i] = c;
        i++;
    }
    line[i] = '\0';
    return i;
}

int main() {
    int len;
    char line[MAXLINE];
    while ((len = Getline(line)) != 0) 
        if (len > MINLINE)
            printf("%s", line);
}

为了便于展示,将打印长度大于 4 个字符(包含\n)的所有输入行:

1
2
3
4
5
6
7
8
this
this
is
a
goood
goood
game
game

exercise1-18

编写一个程序,删除每个输入行末尾的空格及制表符,并删除完全是空格的行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
int handler(char line[], int len) {
    int flag = line[len - 2] == '\n' ? 1 : 0;   // 用来判断本行是否含有\n
    int i = len - 2 - flag;
    for (; i >= 0 && (line[i] == '\t' || line[i] == ' '); i--);
    if (flag) {
        line[++i] = '\n';
    }
    line[++i] = '\0';
    return i;   // 返回实际长度
}

int main() {
    char line[MAXLINE], len, newlen;
    while ((len = Getline(line)) > 0) {
        newlen = handler(line, len);
        printf("len = %d, str = \"%s\", newlen = %d\n", len, line, newlen);
    }
    
}
1
2
3
4
5
6
goo     ood
len = 8, str = "goo     ood", newlen = 7

len = 2, str = "", newlen = 0
         wyu
len = 6, str = "         wyu", newlen = 5

NOTE

为了简化报告,省去了头文件,且下文所使用的所有Getline(line)函数均为

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int Getline(char line[]) {
    int c, i;
    for (i = 0; i < MAXLINE - 1 && (c = getchar()) != EOF && c != '\n'; i++) {
        line[i] = c;
    }
    if (c == '\n')
        line[i++] = c;
    line[i] = '\0';
    return i;
}

exercise1-19

编写函数 reverse(s),将字符串 s 中的字符顺序颠倒过来。使用该函数编写一个程序,每次颠倒一个输入行中的字符顺序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void reverse(char line[], int len) {
    int flag = line[len - 2] == '\n' ? 1 : 0;   // 用来判断本行是否含有\n
    int r = len - 2 - flag, l = 0;
    while (l < r) {
        char temp = line[r];
        line[r] = line[l];
        line[l] = temp;
        l++;
        r--;
    }
}

int main() {
    char line[MAXLINE], len;
    while ((len = Getline(line)) > 0) {
        reverse(line, len);
        printf("%s", line);
    }
}
1
2
hello, everyone!
!enoyreve ,olleh

exercise1-20

编写程序 detab,将输入中的制表符替换成适当数目的空格,使空格充满到下一个制表符终止位的地方。假设制表符终止位的位置是固定的,比如每隔 n 列就会出现一个制表符终止位。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
#define TABLEN 8

void detab(char dest[], char src[], int len) {
    // 分别记录在src、dest中的下标
    int i = 0, j = 0;
    while (i < len && src[i] != '\n') {
        if (src[i] != '\t') {
            dest[j] = src[i];
            j++;
        } else {
            // 计算还有多少字符到达下一个制表位
            int count = TABLEN - (j % TABLEN);
            // 替换为空格
            while (count--) 
                // 为了显示清楚,用^代替空格
                dest[j++] = '^';            
        }
        i++;
    }
    dest[j] = '\0';
}

int main() {
    int len;
    char src[MAXLINE], dest[MAXLINE];
    while ((len = Getline(src)) > 0) {
        detab(dest, src, len);
        printf("%s\n", dest);
    }
}

为了为了显示清楚,用^代替空格

1
2
hi      every   one!
hi^^^^^^every^^^one!

n 应该作为符号变量

exercise1-21

编写程序 entab,将空格串替换为最少数量的制表符和空格,但要保持单词之间的间隔不变。假设制表符终止位的位置与练习 1-20 的 detab 程序的情况相同。当使用一个制表符或者一个空格都可以到达下一个制表符终止位时,选用哪一种替换字符比较好?

 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
#define TABLEN 8

int entab(char dest[], char src[], int len) {
    int count = 0;
    for (int i = 0; i < len; i++) {
        if (src[i] == ' ')
            count++;
        else
            count = 0;
        
        if (count == TABLEN) {
            i -= (TABLEN - 1);
            len -= (TABLEN - 1);

            // 为了显示效果,用^替代\t
            src[i] = '^';

            for (int t = i + 1; t < len; t++)
                src[t] = src[t + (TABLEN - 1)];
            
            count = 0;
            src[len] = '\0';
        }
    }
    printf("%s", src);
    return 0;   
}

int main() {
    char src[MAXLINE], dest[MAXLINE];
    int len;
    while ((len = Getline(src)) > 0) {
        entab(dest, src, len);
    }
}

当使用一个制表符或者一个空格都可以到达下一个制表符终止位时,选用一个制表符比较好,能保持对齐一致性。

exercise1-22

编写一个程序,把较长的输入行“折”成短一些的两行或多行,折行的位置在输入行的第 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
#define FOLDLEN 8

int handler(char src[], int len) {
    if (len < FOLDLEN) {
    } else {
        // foldloc 用来记录折断的位置
        int foldloc = FOLDLEN - 1;
        // 循环退出条件为遍历到最后一个字符
        while (foldloc < len) {
            int i = 0;
            // 从后往前寻找第一个非空格,或者该段全为空格
            for (; i < FOLDLEN; i++) 
                if (src[foldloc - i] != ' ') 
                    break;

            if (i != FOLDLEN) 
                foldloc = foldloc - i + 1;
            
            src[foldloc] = '\n';
            foldloc += FOLDLEN;
        }
    }

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

int main() {
    char src[MAXLINE];
    int len;
    while ((len = Getline(src)) > 0)
        handler(src, len);
}
1
2
3
4
5
this is also a goooood subject.
this is
also a g
ooood su
ject.

exercise1-23

编写一个删除 C 语言程序中所有的注释语句。要正确处理带引号的字符串与字符常量。在 C 语言中,注释不允许嵌套。

 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>
#define MAXLINE 1024
#define CONTENT 0   // 内容
#define QUOTE 1 // 引号内
#define BLOCKCOMMENT 2  // 块注释
#define LINECOMMENT 3   // 行注释

int state;

/*
    能够正确运行的前提是要处理的C程序本身无语法错误
    且没有注释嵌套
    块注释的优先级大于行注释
*/
enum {
    CONTENT,    // 内容
    QUOTE,      // 引号内
    BLOCKCOMMENT,   // 块注释
    LINECOMMENT     // 行注释
};

int state;

/*
    能够正确运行的前提是要处理的C程序本身无语法错误
    且没有注释嵌套
    块注释的优先级大于行注释
*/
void handler(char src[], int len) {
    int t = 0;
    while (t < len) {
        // 进入与离开引号
        if (src[t] == '"') {
            if (state == QUOTE)
                state = CONTENT;
            else 
                state = QUOTE;
        }
        if (state != QUOTE) {
            // 进入块注释
            if (src[t] == '/' && src[t + 1] == '*') {
                t += 2;
                state = BLOCKCOMMENT;
            }
            // 离开块注释
            if (src[t] == '*' && src[t + 1] == '/') {
                t += 2;
                state = CONTENT;
            }

            // 进入行注释
            if (src[t] == '/' && src[t + 1] == '/') {
                t += 2;
                state = LINECOMMENT;
            } 
            // 到达行末尾,检测是否该行为行注释
            if (src[t] == '\n' && state == LINECOMMENT) {
                state = CONTENT;
            }

            // 如果为注释内,则跳过该字符
            if (state == BLOCKCOMMENT || state == LINECOMMENT) {
                t++;
            } else {
                printf("%c", src[t]);
                t++;
            }
        } else {
            printf("%c", src[t]);
            t++;
        }
    }
}

int main() {
    char src[MAXLINE];
    int len;
    state = CONTENT;
    while ((len = Getline(src)) > 0)
        handler(src, len);
}

设有一个测试文件test.c,内容为下:

1
2
3
4
5
6
7
8
9
#include <stdio.h>
/*
TEST 
*/

// uwey
int main() {
    printf("aosi/*123*/");  //sdui
}

执行命令:./Exercise1-23 <test.c,将test.c的文件内容作为输入,得到下面的结果:

1
2
3
4
5
6
7
#include <stdio.h>



int main() {
    printf("aosi/*123*/");  
}

exercise1-24

编写一个程序,查找 C 语言程序中的基本语法错误,如圆括号、方括号、花括号不配对等。要正确处理引号(包括单引号和双引号)、转义字符序列与注释。(如果读者想把该程序编写成完全通用的程序,难度会比较大。)

 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
typedef struct {
    char stack[MAXLINE];
    int top;
} Stack;

Stack stack;
int inSingleQuote = 0, inDoubleQuote = 0, inComment = 0;

void initStack(Stack *s) {
    s->top = -1;
}

int isFull(Stack *s) {
    return s->top == MAXLINE - 1;
}

int isEmpty(Stack *s) {
    return s->top == -1;
}

void push(Stack *s, char c) {
    if (!isFull(s)) {
        s->stack[++s->top] = c;
    }
}

char pop(Stack *s) {
    if (!isEmpty(s)) {
        return s->stack[s->top--];
    }
    return '\0';
}

int matchingBrackets(char opening, char closing) {
    return (opening == '(' && closing == ')') ||
           (opening == '[' && closing == ']') ||
           (opening == '{' && closing == '}');
}

int handler(char line[], int len) {
    int i = 0;

    while (i < len) {
        char c = line[i];

        if (inComment) {
            if (c == '*' && line[i + 1] == '/') {
                inComment = 0;
                i++;
            }
        } else if (inSingleQuote) {
            if (c == '\'' && (i == 0 || line[i - 1] != '\\')) {
                inSingleQuote = 0;
            }
        } else if (inDoubleQuote) {
            if (c == '"' && (i == 0 || line[i - 1] != '\\')) {
                inDoubleQuote = 0;
            }
        } else {
            if (c == '/' && line[i + 1] == '*') {
                inComment = 1;
                i++;
            } else if (c == '\'') {
                inSingleQuote = 1;
            } else if (c == '"') {
                inDoubleQuote = 1;
            } else if (c == '(' || c == '[' || c == '{') {
                push(&stack, c);
            } else if (c == ')' || c == ']' || c == '}') {
                if (isEmpty(&stack) || !matchingBrackets(pop(&stack), c)) {
                    printf("Syntax error: unmatched %c at position %d\n", c, i);
                    return 1;
                }
            }
        }
        i++;
    }
}

int main() {
    initStack(&stack);
    char line[MAXLINE];
    int len;
    while ((len = Getline(line)) > 0) 
        handler(line, len);
    
    if (!isEmpty(&stack)) {
        printf("Syntax error: unmatched %c\n", stack.stack[stack.top]);
        return 0;
    }

    printf("No syntax errors found.\n");
    return 0;
}

设有一个 test.c 文件,内容为

1
2
3
4
5
6
7
8
#include <stdio.h>
/*
TEST 
*/

// uwey
int main() {
    printf("aosi/*123*/"); //sdui

通过运行./Exercise1-24 <test.c,结果如下:

1
Syntax error: unmatched {

检测到了缺少花括号