"The C Programming Language", 2nd edition, Kernighan and Ritchie
来源:互联网 发布:java gbk中文乱码 编辑:程序博客网 时间:2024/04/28 17:45
目 录
译者序
序
第1版序
前言
第1章 基本概念
1.1 入门
1.2 变量与算术表达式
1.3 for语句
1.4 符号常量
1.5 字符输入输出
1.5.1 文件复制
1.5.2 字符计数
1.5.3 行计数
1.5.4 单词计数
1.6 数组
1.7 函数
1.8 变元—按值调用
1.9 字符数组
1.10 外部变量与作用域
第2章 类型、运算符与表达式
2.1 变量名
2.2 数据类型与大小
2.3 常量
2.4 说明
2.5 算术运算符
2.6 关系运算符与逻辑运算符
2.7 类型转换
2.8 加一与减一运算符
2.9 按位运算符
2.10 赋值运算符与赋值表达式
2.11 条件表达式
2.12 运算符优先级与表达式求值次序
第3章 控制流
3.1 语句与分程序
3.2 if-else语句
3.3 else-if语句
3.4 switch语句
3.5 while与for循环语句
3.6 do-while循环语句
3.7 break语句与continue语句
3.8 goto语句与标号
第4章 函数与程序结构
4.1 函数的基本知识
4.2 返回非整数值的函数
4.3 外部变量
4.4 作用域规则
4.5 头文件
4.6 静态变量
4.7 寄存器变量
4.8 分程序结构
4.9 初始化
4.10 递归
4.11 C预处理程序
4.11.1 文件包含
4.11.2 宏替换
4.11.3 条件包含
第5章 指针与数组
5.1 指针与地址
5.2 指针与函数变元
5.3 指针与数组
5.4 地址算术运算
5.5 字符指针与函数
5.6 指针数组与指向指针的指针
5.7 多维数组
5.8 指针数组的初始化
5.9 指针与多维数组
5.10 命令行变元
5.11 指向函数的指针
5.12 复杂说明
第6章 结构
6.1 结构的基本知识
6.2 结构与函数
6.3 结构数组
6.4 结构指针
6.5 自引用结构
6.6 查找表
6.7 类型定义
6.8 联合
6.9 位字段
第7章 输入与输出
7.1 标准输入输出
7.2 格式输出—printf函数
7.3 变长变元表
7.4 格式输入—scanf函数
7.5 文件访问
7.6 错误处理—stderr和exit函数
7.7 行输入输出
7.8 其他函数
7.8.1 字符串处理函数
7.8.2 字符类测试和转换函数
7.8.3 ungetc函数
7.8.4 命令执行函数
7.8.5 存储管理函数
7.8.6 数学函数
7.8.7 随机数发生器函数
第8章 UNIX系统界面
8.1 文件描述符
8.2 低级I/O—read和write系统调用
8.3 open、creat、close和unlink系统调用
8.4 随机访问—lseek系统调用
8.5 实例—fopen和getc函数的一种实现
方法
8.6 实例—目录显示
8.7 实例—存储分配程序
附录A 参考手册
A.1 引言
A.2 词法规则
A.3 语法符号
A.4 标识符的含义
A.5 对象和左值
A.6 转换
A.7 表达式
A.8 说明
A.9 语句
A.10 外部说明
A.11 作用域与连接
A.12 预处理
A.13 语法
附录B 标准库
B.1 输入与输出:<stdio.h>
B.2 字符类测试:<ctype.h>
B.3 字符串函数:<string.h>
B.4 数学函数:<math.h>
B.5 实用函数:<stdlib.h>
B.6 诊断:<assert.h>
B.7 变量变元表:<stdarg.h>
B.8 非局部跳转:<setjmp.h>
B.9 信号处理:<signal.h>
B.10 日期与时间函数:<time.h>
B.11 由实现定义的限制:<limits.h>和
<float.h>
附录C 变更小结
- getchar() and putchar()
- #include <stdio.h>
- /* 用于将输入复制到输出的程序;第2个版本*/
- main ( )
- {
- int c;
- while ( (c = getchar ( ) ) != EOF )//End Of File,文件结束< stdio.h>库定义的整数
- putchar ( c );
- }
- 在这一程序中,w h i l e循环语句先读一个字符并将其赋给c,然后测试该字符是否为文件结束标记。如果该字符不是文件结束标记,那么就执行w h i l e语句体,把该字符打印出来。再重复执行该w h i l e语句。当最后到达输入结束位置时, w h i l e循环语句终止执行,从而整个m a i n程序执行结束。之所以不把c说明成c h a r类型,是因为c必须大到除了能存储任何可能的字符外还要能存储文件结束符E O F。因此,把c说明成i n t类型的。
1.5.1 文件复制
Verify that the expression getchar()
!=
EOF
is 0 or 1.
- #include <stdio.h>
- int main(void)
- {
- printf("Press a key. ENTER would be nice :-)/n/n");
- printf("The expression getchar() != EOF evaluates to %d/n", getchar() != EOF);
- return 0;
- }
Write a program to print the value of EOF
.
- #include <stdio.h>
- int main(void)
- {
- printf("The value of EOF is %d/n/n", EOF);
- return 0;
- }
- #include <stdio.h>
- /* 统计输入的字符数; 第2个版本*/
- main ( )
- {
- double nc;
- for ( nc = 0; getchar ( ) != EOF; ++nc )
- ;
- printf("%.0f/n", nc);
- }
- #include <stdio.h>
- /* 统计输入的行数*/
- main ( )
- {
- long c, nl;
- nl = 0;
- while ( (c = getchar ( ) ) != EOF )
- if ( c =='/n' )
- ++nl;
- printf("%d/n", nl);
- }
编写一个用于统计空格、制表符与换行符个数的程序
Write a program to count blanks, tabs, and newlines
- #include <stdio.h>
- int main(void)
- {
- int blanks, tabs, newlines;
- int c;
- int done = 0;
- int lastchar = 0;
- blanks = 0;
- tabs = 0;
- newlines = 0;
- while(done == 0)
- {
- c = getchar();
- if(c == ' ')
- ++blanks;
- if(c == '/t')
- ++tabs;
- if(c == '/n')
- ++newlines;
- if(c == EOF)
- {
- if(lastchar != '/n')
- {
- ++newlines; /* this is a bit of a semantic stretch, but it copes
- * with implementations where a text file might not
- * end with a newline. Thanks to Jim Stad for pointing
- * this out.
- */
- }
- done = 1;
- }
- lastchar = c;
- }
- printf("Blanks: %d/nTabs: %d/nLines: %d/n", blanks, tabs, newlines);
- return 0;
- }
编写一个程序,把它的输入复制到输出,并在此过程中将相连的多个空格用一个
空格代替。
Write a program to copy its input to its output, replacing each string of one or more blanks by a single blan
- #include <stdio.h>
- int main(void)
- {
- int c;
- int inspace;
- inspace = 0;
- while((c = getchar()) != EOF)
- {
- if(c == ' ')
- {
- if(inspace == 0)
- {
- inspace = 1;
- putchar(c);
- }
- }
- /* We haven't met 'else' yet, so we have to be a little clumsy */
- if(c != ' ')
- {
- inspace = 0;
- putchar(c);
- }
- }
- return 0;
- }
- Chris Sidi writes: "instead of having an "inspace" boolean, you can keep track of the previous character and see if both the current character and previous character are spaces:"
- #include <stdio.h>
- /* count lines in input */
- int
- main()
- {
- int c, pc; /* c = character, pc = previous character */
- /* set pc to a value that wouldn't match any character, in case
- this program is ever modified to get rid of multiples of other
- characters */
- pc = EOF;
- while ((c = getchar()) != EOF) {
- if (c == ' ')
- if (pc != ' ') /* or if (pc != c) */
- putchar(c);
- /* We haven't met 'else' yet, so we have to be a little clumsy */
- if (c != ' ')
- putchar(c);
- pc = c;
- }
- return 0;
- }
- Stig writes: "I am hiding behind the fact that break is mentioned in the introduction"!
- #include <stdio.h>
- int main(void)
- {
- int c;
- while ((c = getchar()) != EOF) {
- if (c == ' ') {
- putchar(c);
- while((c = getchar()) == ' ' && c != EOF)
- ;
- }
- if (c == EOF)
- break; /* the break keyword is mentioned
- * in the introduction...
- * */
- putchar(c);
- }
- return 0;
- }
编写一个程序,把它的输入复制到输出,并在此过程中把制表符换成/ t、把回退
符换成/ b、把反斜杠换成/ /。这样可以使得制表符与回退符能以无歧义的方式可见。
Write a program to copy its input to its output, replacing each tab by /t
, each backspace by /b
, and each backslash by //
. This makes tabs and backspaces visible in an unambiguous way.
- Category 0
- Gregory Pietsch pointed out that my solution was actually Category 1. He was quite right. Better still, he was kind enough to submit a Category 0 solution himself. Here it is:
- /* Gregory Pietsch <gkp1@flash.net> */
- /*
- * Here's my attempt at a Category 0 version of 1-10.
- *
- * Gregory Pietsch
- */
- #include <stdio.h>
- int main()
- {
- int c, d;
- while ( (c=getchar()) != EOF) {
- d = 0;
- if (c == '//') {
- putchar('//');
- putchar('//');
- d = 1;
- }
- if (c == '/t') {
- putchar('//');
- putchar('t');
- d = 1;
- }
- if (c == '/b') {
- putchar('//');
- putchar('b');
- d = 1;
- }
- if (d == 0)
- putchar(c);
- }
- return 0;
- }
- Category 1
- This solution, which I wrote myself, is the sadly discredited Cat 0 answer which has found a new lease of life in Category 1.
- #include <stdio.h>
- #define ESC_CHAR '//'
- int main(void)
- {
- int c;
- while((c = getchar()) != EOF)
- {
- switch(c)
- {
- case '/b':
- /* The OS on which I tested this (NT) intercepts /b characters. */
- putchar(ESC_CHAR);
- putchar('b');
- break;
- case '/t':
- putchar(ESC_CHAR);
- putchar('t');
- break;
- case ESC_CHAR:
- putchar(ESC_CHAR);
- putchar(ESC_CHAR);
- break;
- default:
- putchar(c);
- break;
- }
- }
- return 0;
- }
1.5.4 单词计数
- #include <stdio.h>
- #define IN 1 /* 在单词内*/
- #define OUT 0 /* 在单词外*/
- /* 统计输入的行数、单词数与字符数*/
- main ( )
- {
- int c, nl, nw, nc, state;
- state = OUT;
- nl = nw = nc = 0;
- while ( (c = getchar ( ) ) != EOF ) {
- ++nc;
- if ( c =='/n' )
- ++nl;
- if ( c == ' ' ||c == '/n' || c == '/t' )
- state = OUT;
- else if ( state == OUT ) {
- state = IN;
- ++nw;
- }
- }
- printf("%d %d %d/n", nl, nw, nc);
- }
你准备怎样测试单词计数程序?如果程序中出现任何错误,那么什么样的输入最
有利于发现这些错误?
How would you test the word count program? What kinds of input are most likely to uncover bugs if there are any?
- It sounds like they are really trying to get the programmers to learn how to do a unit test. I would submit the following:
- 0. input file contains zero words
- 1. input file contains 1 enormous word without any newlines
- 2. input file contains all white space without newlines
- 3. input file contains 66000 newlines
- 4. input file contains word/{huge sequence of whitespace of different kinds}/word
- 5. input file contains 66000 single letter words, 66 to the line
- 6. input file contains 66000 words without any newlines
- 7. input file is /usr/dict contents (or equivalent)
- 8. input file is full collection of moby words
- 9. input file is binary (e.g. its own executable)
- 10. input file is /dev/nul (or equivalent)
- 66000 is chosen to check for integral overflow on small integer machines.
- Dann suggests a followup exercise 1-11a: write a program to generate inputs (0,1,2,3,4,5,6)
- I guess it was inevitable that I'd receive a solution for this followup exercise! Here is Gregory Pietsch's program to generate Dann's suggested inputs:
- #include <assert.h>
- #include <stdio.h>
- int main(void)
- {
- FILE *f;
- unsigned long i;
- static char *ws = " /f/t/v";
- static char *al = "abcdefghijklmnopqrstuvwxyz";
- static char *i5 = "a b c d e f g h i j k l m "
- "n o p q r s t u v w x y z "
- "a b c d e f g h i j k l m "
- "n o p q r s t u v w x y z "
- "a b c d e f g h i j k l m "
- "n/n";
- /* Generate the following: */
- /* 0. input file contains zero words */
- f = fopen("test0", "w");
- assert(f != NULL);
- fclose(f);
- /* 1. input file contains 1 enormous word without any newlines */
- f = fopen("test1", "w");
- assert(f != NULL);
- for (i = 0; i < ((66000ul / 26) + 1); i++)
- fputs(al, f);
- fclose(f);
- /* 2. input file contains all white space without newlines */
- f = fopen("test2", "w");
- assert(f != NULL);
- for (i = 0; i < ((66000ul / 4) + 1); i++)
- fputs(ws, f);
- fclose(f);
- /* 3. input file contains 66000 newlines */
- f = fopen("test3", "w");
- assert(f != NULL);
- for (i = 0; i < 66000; i++)
- fputc('/n', f);
- fclose(f);
- /* 4. input file contains word/
- * {huge sequence of whitespace of different kinds}
- * /word
- */
- f = fopen("test4", "w");
- assert(f != NULL);
- fputs("word", f);
- for (i = 0; i < ((66000ul / 26) + 1); i++)
- fputs(ws, f);
- fputs("word", f);
- fclose(f);
- /* 5. input file contains 66000 single letter words,
- * 66 to the line
- */
- f = fopen("test5", "w");
- assert(f != NULL);
- for (i = 0; i < 1000; i++)
- fputs(i5, f);
- fclose(f);
- /* 6. input file contains 66000 words without any newlines */
- f = fopen("test6", "w");
- assert(f != NULL);
- for (i = 0; i < 66000; i++)
- fputs("word ", f);
- fclose(f);
- return 0;
- }
编写一个程序,以每行一个单词的形式打印输入。
Write a program that prints its input one word per line.
- #include <stdio.h>
- int main(void)
- {
- int c;
- int inspace;
- inspace = 0;
- while((c = getchar()) != EOF)
- {
- if(c == ' ' || c == '/t' || c == '/n')
- {
- if(inspace == 0)
- {
- inspace = 1;
- putchar('/n');
- }
- /* else, don't print anything */
- }
- else
- {
- inspace = 0;
- putchar(c);
- }
- }
- return 0;
- }
1.6 数组
- #include <stdio.h>
- /* 统计各个数字、空白字符及其他字符分别出现的次数*/
- main ( )
- {
- int c, i, nwhite, nother;
- int ndigit[10];
- nwhite = nother = 0;
- for ( i =0; i < 10; ++i )
- ndigit[i] = 0;
- while ( ( c = getchar() ) != EOF )
- if ( c >= '0' && c <= '9' )
- ++ndigit[c - '0'];
- else if ( c == ' ' ||c == '/n' || c == '/t' )
- ++nwhite;
- else
- ++nother;
- printf( "digits =" );
- for ( i =0; i < 10; ++i )
- printf( " %d", ndigit[i] );
- printf( ", white space = %d, other = %d", nwhite, nother);
- }
- 当把程序自身作为输入时,输出为:
digits = 9 3 0 0 0 0 0 0 0 1, white space = 123, other = 345
练习1-13 编写一个程序,打印其输入的文件中单词长度的直方图。横条的直方图比较容易
绘制,竖条直方图则要困难些。
Write a program to print a histogram of the lengths of words in its input. It is easy to draw the histogram with the bars horizontal; a vertical orientation is more challenging.
- #include <stdio.h>
- #define MAXWORDLEN 10
- int main(void)
- {
- int c;
- int inspace = 0;
- long lengtharr[MAXWORDLEN + 1];
- int wordlen = 0;
- int firstletter = 1;
- long thisval = 0;
- long maxval = 0;
- int thisidx = 0;
- int done = 0;
- for(thisidx = 0; thisidx <= MAXWORDLEN; thisidx++)
- {
- lengtharr[thisidx] = 0;
- }
- while(done == 0)
- {
- c = getchar();
- if(c == ' ' || c == '/t' || c == '/n' || c == EOF)
- {
- if(inspace == 0)
- {
- firstletter = 0;
- inspace = 1;
- if(wordlen <= MAXWORDLEN)
- {
- if(wordlen > 0)
- {
- thisval = ++lengtharr[wordlen - 1];
- if(thisval > maxval)
- {
- maxval = thisval;
- }
- }
- }
- else
- {
- thisval = ++lengtharr[MAXWORDLEN];
- if(thisval > maxval)
- {
- maxval = thisval;
- }
- }
- }
- if(c == EOF)
- {
- done = 1;
- }
- }
- else
- {
- if(inspace == 1 || firstletter == 1)
- {
- wordlen = 0;
- firstletter = 0;
- inspace = 0;
- }
- ++wordlen;
- }
- }
- for(thisval = maxval; thisval > 0; thisval--)
- {
- printf("%4d | ", thisval);
- for(thisidx = 0; thisidx <= MAXWORDLEN; thisidx++)
- {
- if(lengtharr[thisidx] >= thisval)
- {
- printf("* ");
- }
- else
- {
- printf(" ");
- }
- }
- printf("/n");
- }
- printf(" +");
- for(thisidx = 0; thisidx <= MAXWORDLEN; thisidx++)
- {
- printf("---");
- }
- printf("/n ");
- for(thisidx = 0; thisidx < MAXWORDLEN; thisidx++)
- {
- printf("%2d ", thisidx + 1);
- }
- printf(">%d/n", MAXWORDLEN);
- return 0;
- }
- Here's the output of the program when given its own source as input:
- 113 | *
- 112 | *
- 111 | *
- 110 | *
- 109 | *
- 108 | *
- 107 | *
- 106 | *
- 105 | *
- 104 | *
- 103 | *
- 102 | *
- 101 | *
- 100 | *
- 99 | *
- 98 | *
- 97 | *
- 96 | *
- 95 | *
- 94 | * *
- 93 | * *
- 92 | * *
- 91 | * *
- 90 | * *
- 89 | * *
- 88 | * *
- 87 | * *
- 86 | * *
- 85 | * *
- 84 | * *
- 83 | * *
- 82 | * *
- 81 | * *
- 80 | * *
- 79 | * *
- 78 | * *
- 77 | * *
- 76 | * *
- 75 | * *
- 74 | * *
- 73 | * *
- 72 | * *
- 71 | * *
- 70 | * *
- 69 | * *
- 68 | * *
- 67 | * *
- 66 | * *
- 65 | * *
- 64 | * *
- 63 | * * *
- 62 | * * *
- 61 | * * *
- 60 | * * *
- 59 | * * *
- 58 | * * *
- 57 | * * *
- 56 | * * *
- 55 | * * *
- 54 | * * *
- 53 | * * *
- 52 | * * * *
- 51 | * * * *
- 50 | * * * *
- 49 | * * * *
- 48 | * * * *
- 47 | * * * *
- 46 | * * * *
- 45 | * * * *
- 44 | * * * *
- 43 | * * * * *
- 42 | * * * * *
- 41 | * * * * *
- 40 | * * * * *
- 39 | * * * * *
- 38 | * * * * *
- 37 | * * * * *
- 36 | * * * * *
- 35 | * * * * *
- 34 | * * * * *
- 33 | * * * * *
- 32 | * * * * *
- 31 | * * * * *
- 30 | * * * * * *
- 29 | * * * * * *
- 28 | * * * * * * *
- 27 | * * * * * * *
- 26 | * * * * * * *
- 25 | * * * * * * * *
- 24 | * * * * * * * *
- 23 | * * * * * * * *
- 22 | * * * * * * * * *
- 21 | * * * * * * * * *
- 20 | * * * * * * * * *
- 19 | * * * * * * * * *
- 18 | * * * * * * * * *
- 17 | * * * * * * * * *
- 16 | * * * * * * * * *
- 15 | * * * * * * * * *
- 14 | * * * * * * * * * *
- 13 | * * * * * * * * * *
- 12 | * * * * * * * * * *
- 11 | * * * * * * * * * *
- 10 | * * * * * * * * * *
- 9 | * * * * * * * * * * *
- 8 | * * * * * * * * * * *
- 7 | * * * * * * * * * * *
- 6 | * * * * * * * * * * *
- 5 | * * * * * * * * * * *
- 4 | * * * * * * * * * * *
- 3 | * * * * * * * * * * *
- 2 | * * * * * * * * * * *
- 1 | * * * * * * * * * * *
- +---------------------------------
- 1 2 3 4 5 6 7 8 9 10 >10
练习1-14 编写一个程序,打印其输入的文件中各个字符出现频率的直方图。
Write a program to print a histogram of the frequencies of different characters in its input.
- #include <stdio.h>
- /* NUM_CHARS should really be CHAR_MAX but K&R haven't covered that at this stage in the book */
- #define NUM_CHARS 256
- int main(void)
- {
- int c;
- long freqarr[NUM_CHARS + 1];
- long thisval = 0;
- long maxval = 0;
- int thisidx = 0;
- for(thisidx = 0; thisidx <= NUM_CHARS; thisidx++)
- {
- freqarr[thisidx] = 0;
- }
- while((c = getchar()) != EOF)
- {
- if(c < NUM_CHARS)
- {
- thisval = ++freqarr[c];
- if(thisval > maxval)
- {
- maxval = thisval;
- }
- }
- else
- {
- thisval = ++freqarr[NUM_CHARS];
- if(thisval > maxval)
- {
- maxval = thisval;
- }
- }
- }
- for(thisval = maxval; thisval > 0; thisval--)
- {
- printf("%4d |", thisval);
- for(thisidx = 0; thisidx <= NUM_CHARS; thisidx++)
- {
- if(freqarr[thisidx] >= thisval)
- {
- printf("*");
- }
- else if(freqarr[thisidx] > 0)
- {
- printf(" ");
- }
- }
- printf("/n");
- }
- printf(" +");
- for(thisidx = 0; thisidx <= NUM_CHARS; thisidx++)
- {
- if(freqarr[thisidx] > 0)
- {
- printf("-");
- }
- }
- printf("/n ");
- for(thisidx = 0; thisidx < NUM_CHARS; thisidx++)
- {
- if(freqarr[thisidx] > 0)
- {
- printf("%d", thisidx / 100);
- }
- }
- printf("/n ");
- for(thisidx = 0; thisidx < NUM_CHARS; thisidx++)
- {
- if(freqarr[thisidx] > 0)
- {
- printf("%d", (thisidx - (100 * (thisidx / 100))) / 10 );
- }
- }
- printf("/n ");
- for(thisidx = 0; thisidx < NUM_CHARS; thisidx++)
- {
- if(freqarr[thisidx] > 0)
- {
- printf("%d", thisidx - (10 * (thisidx / 10)));
- }
- }
- if(freqarr[NUM_CHARS] > 0)
- {
- printf(">%d/n", NUM_CHARS);
- }
- printf("/n");
- return 0;
- }
- Here's the output of the program when given its own source as input:
- 474 | *
- 473 | *
- 472 | *
- 471 | *
- 470 | *
- 469 | *
- 468 | *
- 467 | *
- 466 | *
- 465 | *
- 464 | *
- 463 | *
- 462 | *
- 461 | *
- 460 | *
- 459 | *
- 458 | *
- 457 | *
- 456 | *
- 455 | *
- 454 | *
- 453 | *
- 452 | *
- 451 | *
- 450 | *
- 449 | *
- 448 | *
- 447 | *
- 446 | *
- 445 | *
- 444 | *
- 443 | *
- 442 | *
- 441 | *
- 440 | *
- 439 | *
- 438 | *
- 437 | *
- 436 | *
- 435 | *
- 434 | *
- 433 | *
- 432 | *
- 431 | *
- 430 | *
- 429 | *
- 428 | *
- 427 | *
- 426 | *
- 425 | *
- 424 | *
- 423 | *
- 422 | *
- 421 | *
- 420 | *
- 419 | *
- 418 | *
- 417 | *
- 416 | *
- 415 | *
- 414 | *
- 413 | *
- 412 | *
- 411 | *
- 410 | *
- 409 | *
- 408 | *
- 407 | *
- 406 | *
- 405 | *
- 404 | *
- 403 | *
- 402 | *
- 401 | *
- 400 | *
- 399 | *
- 398 | *
- 397 | *
- 396 | *
- 395 | *
- 394 | *
- 393 | *
- 392 | *
- 391 | *
- 390 | *
- 389 | *
- 388 | *
- 387 | *
- 386 | *
- 385 | *
- 384 | *
- 383 | *
- 382 | *
- 381 | *
- 380 | *
- 379 | *
- 378 | *
- 377 | *
- 376 | *
- 375 | *
- 374 | *
- 373 | *
- 372 | *
- 371 | *
- 370 | *
- 369 | *
- 368 | *
- 367 | *
- 366 | *
- 365 | *
- 364 | *
- 363 | *
- 362 | *
- 361 | *
- 360 | *
- 359 | *
- 358 | *
- 357 | *
- 356 | *
- 355 | *
- 354 | *
- 353 | *
- 352 | *
- 351 | *
- 350 | *
- 349 | *
- 348 | *
- 347 | *
- 346 | *
- 345 | *
- 344 | *
- 343 | *
- 342 | *
- 341 | *
- 340 | *
- 339 | *
- 338 | *
- 337 | *
- 336 | *
- 335 | *
- 334 | *
- 333 | *
- 332 | *
- 331 | *
- 330 | *
- 329 | *
- 328 | *
- 327 | *
- 326 | *
- 325 | *
- 324 | *
- 323 | *
- 322 | *
- 321 | *
- 320 | *
- 319 | *
- 318 | *
- 317 | *
- 316 | *
- 315 | *
- 314 | *
- 313 | *
- 312 | *
- 311 | *
- 310 | *
- 309 | *
- 308 | *
- 307 | *
- 306 | *
- 305 | *
- 304 | *
- 303 | *
- 302 | *
- 301 | *
- 300 | *
- 299 | *
- 298 | *
- 297 | *
- 296 | *
- 295 | *
- 294 | *
- 293 | *
- 292 | *
- 291 | *
- 290 | *
- 289 | *
- 288 | *
- 287 | *
- 286 | *
- 285 | *
- 284 | *
- 283 | *
- 282 | *
- 281 | *
- 280 | *
- 279 | *
- 278 | *
- 277 | *
- 276 | *
- 275 | *
- 274 | *
- 273 | *
- 272 | *
- 271 | *
- 270 | *
- 269 | *
- 268 | *
- 267 | *
- 266 | *
- 265 | *
- 264 | *
- 263 | *
- 262 | *
- 261 | *
- 260 | *
- 259 | *
- 258 | *
- 257 | *
- 256 | *
- 255 | *
- 254 | *
- 253 | *
- 252 | *
- 251 | *
- 250 | *
- 249 | *
- 248 | *
- 247 | *
- 246 | *
- 245 | *
- 244 | *
- 243 | *
- 242 | *
- 241 | *
- 240 | *
- 239 | *
- 238 | *
- 237 | *
- 236 | *
- 235 | *
- 234 | *
- 233 | *
- 232 | *
- 231 | *
- 230 | *
- 229 | *
- 228 | *
- 227 | *
- 226 | *
- 225 | *
- 224 | *
- 223 | *
- 222 | *
- 221 | *
- 220 | *
- 219 | *
- 218 | *
- 217 | *
- 216 | *
- 215 | *
- 214 | *
- 213 | *
- 212 | *
- 211 | *
- 210 | *
- 209 | *
- 208 | *
- 207 | *
- 206 | *
- 205 | *
- 204 | *
- 203 | *
- 202 | *
- 201 | *
- 200 | *
- 199 | *
- 198 | *
- 197 | *
- 196 | *
- 195 | *
- 194 | *
- 193 | *
- 192 | *
- 191 | *
- 190 | *
- 189 | *
- 188 | *
- 187 | *
- 186 | *
- 185 | *
- 184 | *
- 183 | *
- 182 | *
- 181 | *
- 180 | *
- 179 | *
- 178 | *
- 177 | *
- 176 | *
- 175 | *
- 174 | *
- 173 | *
- 172 | *
- 171 | *
- 170 | *
- 169 | *
- 168 | *
- 167 | *
- 166 | *
- 165 | *
- 164 | *
- 163 | *
- 162 | *
- 161 | *
- 160 | *
- 159 | *
- 158 | *
- 157 | *
- 156 | *
- 155 | *
- 154 | *
- 153 | *
- 152 | *
- 151 | *
- 150 | *
- 149 | *
- 148 | *
- 147 | *
- 146 | *
- 145 | *
- 144 | *
- 143 | *
- 142 | *
- 141 | *
- 140 | *
- 139 | *
- 138 | *
- 137 | *
- 136 | *
- 135 | *
- 134 | *
- 133 | *
- 132 | *
- 131 | *
- 130 | *
- 129 | *
- 128 | *
- 127 | *
- 126 | *
- 125 | *
- 124 | *
- 123 | *
- 122 | *
- 121 | *
- 120 | *
- 119 | *
- 118 | *
- 117 | *
- 116 | *
- 115 | *
- 114 | *
- 113 | *
- 112 | *
- 111 | *
- 110 | *
- 109 | * *
- 108 | * *
- 107 | * *
- 106 | * *
- 105 | * *
- 104 | * *
- 103 | * *
- 102 | * *
- 101 | * *
- 100 | * *
- 99 | * *
- 98 | * *
- 97 |** *
- 96 |** *
- 95 |** *
- 94 |** *
- 93 |** *
- 92 |** *
- 91 |** *
- 90 |** *
- 89 |** *
- 88 |** *
- 87 |** *
- 86 |** *
- 85 |** *
- 84 |** *
- 83 |** *
- 82 |** *
- 81 |** *
- 80 |** *
- 79 |** *
- 78 |** *
- 77 |** *
- 76 |** *
- 75 |** *
- 74 |** *
- 73 |** *
- 72 |** *
- 71 |** * *
- 70 |** * *
- 69 |** * *
- 68 |** * *
- 67 |** * *
- 66 |** * *
- 65 |** * *
- 64 |** * *
- 63 |** * *
- 62 |** * *
- 61 |** * *
- 60 |** * *
- 59 |** * * *
- 58 |** * * *
- 57 |** * * *
- 56 |** * * *
- 55 |** * * *
- 54 |** * * *
- 53 |** * * *
- 52 |** * * *
- 51 |** ** * *
- 50 |** ** * *
- 49 |** ** ***
- 48 |** ** ***
- 47 |** ** ***
- 46 |** ** ***
- 45 |** ** ***
- 44 |** ** ***
- 43 |** * ** ***
- 42 |** * * * ** ***
- 41 |** * * * ** ***
- 40 |** ** * * * ** ***
- 39 |** ** * * * * ** ***
- 38 |** ** * * * * ** ***
- 37 |** ** * * * * ** *** *
- 36 |** ** * * * * ** *** *
- 35 |** ** * * * * ** *** *
- 34 |** ** * * * * ** *** *
- 33 |** ** * * * * ** *** *
- 32 |** ** * * * * ** * *** *
- 31 |** ** * * * * ** * *** *
- 30 |** ** * * * * ** * *** *
- 29 |** ** * * * * ** * *** *
- 28 |** * ** * * *** ** * * *** *
- 27 |** * ** * * * *** ** * * *** *
- 26 |** * ** * * * *** ** * * *** *
- 25 |** * ** * * * *** ** * * *** *
- 24 |** * ** * * * *** ** * * *** *
- 23 |** * ** * * * *** ** * * *** *
- 22 |** * ** * * * *** ** * * *** *
- 21 |** * ** * * * * *** ** * * *** * *
- 20 |** * ** * * * * *** ** * * *** * * * *
- 19 |** * ** * * * * *** ** * * *** * * * *
- 18 |** * ** * * * * * *** ** * * *** * * * *
- 17 |** * ** * * * * * *** ** * * *** * * * *
- 16 |** * ** * * * * * *** ** * ** *** * * * *
- 15 |** * ** * * * * * * * *** ** * ** *** * * * *
- 14 |** * ** * * * * ** * * * ** *** ** * *** *** * * * *
- 13 |** * ** * * * * ** * ** *** ** *** ** * *** *** * * * *
- 12 |** * ** * * * **** * ** *** ** *** ** * *** *** * * * *
- 11 |** * ** * * * **** * ** *** * *** *** ** * ******* * * * *
- 10 |** * ** * * * **** * ** *** * *** *** ** * ******* * * * *
- 9 |** * ** * * * **** * ** *** * *** *** ** * ******* * * * *
- 8 |** * ** * * ****** * ** *** * *** *** ** * ******* * * * *
- 7 |** * ** * ** ****** * ** *** * *** **** ** ********* * * * *
- 6 |** * ** * *** ****** * ** *** ***** **** ** ********* * * * *
- 5 |** * * ****** *** ****** * ** *** ***** ******* ********* * * * *
- 4 |** * * ****** *** ****** * ** *** ***** ******* *********** * * *
- 3 |** * * ****** *** ****** * ** *** ************* *********** * * *
- 2 |** *** ****** *** ****** * ** *** ************* *********** * * *
- 1 |**********************************************************************
- +----------------------------------------------------------------------
- 0000000000000000000000000000000000000000000000111111111111111111111111
- 1333333344444444445555566666677777788889999999000000000111111111122222
- 0234578901234567890234901257902578923581235789012345789012345678901345
1.7 函数
- 下面给出函数power(m, n)的定义及调用它的主程序,由此可以看到整个结构。
- #include <stdio.h>
- int power(int m, int n);
- /* 测试power函数*/
- main ( )
- {
- int i;
- for ( i =0; i < 10; ++i )
- printf("%d %d %d/n", i, power(2, i), power(-3, i));
- return 0;
- }
- /* power: 求底的n次幂; n >=0 */
- int power(int base, int n)
- {
- int i, p;
- p = 1;
- for ( i = 1; i <= n; ++i )
- p = p * base;
- return p;
- }
- 函数定义的一般形式为:
- 返回值类型函数名(可能有的参数说明)
- {
- 说明序列
- 语句序列
- }
练习1-15 重写1 . 2节的温度转换程序,使用函数来实现温度转换。
- #include <stdio.h>
- float FtoC(float f)
- {
- float c;
- c = (5.0 / 9.0) * (f - 32.0);
- return c;
- }
- int main(void)
- {
- float fahr, celsius;
- int lower, upper, step;
- lower = 0;
- upper = 300;
- step = 20;
- printf("F C/n/n");
- fahr = lower;
- while(fahr <= upper)
- {
- celsius = FtoC(fahr);
- printf("%3.0f %6.1f/n", fahr, celsius);
- fahr = fahr + step;
- }
- return 0;
- }
1.8 变元—按值调用
- /* power: 求底的n次幂; n >=0;第2个版本*/
- int power(int base, int n)
- {
- int p;
- for ( n = 1; n > 0; --n )
- p = p * base;
- return p;
- }
1.9 字符数组
while (还有没有处理的行)
if (该行比已处理的最长的行还要长)
保存该行
保存该行的长度
打印最长的行
这一算法描述很清楚,很自然地把所要编写的程序分成了若干部分,分别用于读入新行、测试
读入的行、保存该行及控制这一过程。
由于分割得比较好,故可以像这样来编写程序。首先编写一个独立的函数g e t l i n e来读取输入
的下一行。我们想使这个函数在其他地方也能使用。g e t l i n e函数至少在读到文件末尾时要返回一
个信号,而更有用的设计是它能在读入文本行时返回该行的长度,而在遇到文件结束符时返回0。
由于0不是有效的行长度,因此是一个可以接受的标记文件结束的返回值。每一行至少要有一个
字符,只包含换行符的行的长度为1。
当发现某一个新读入的行比以前读入的最长的行还要长时,就要把该新行保存起来。这意
味着需要用第二个函数c o p y来把新行复制到一个安全的位置。
最后,需要用主函数m a i n来控制对g e t l i n e和c o p y这两个函数的调用。整个程序如下:
- #include <stdio.h>
- #define MAXLINE 1000 /* 最大输入行的大小*/
- int getline (char line[ ], int maxline );
- void copy ( char to[ ], char from [] );
- /* 打印最长的输入行*/
- 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 ("%s" , longest ) ;
- return 0 ;
- }
- /* getline:将一行读入s中并返回其长度*/
- 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;
- ++i;
- }
- s[i] = '/0';
- return i;
- }
- /* copy:从from拷贝到to; 假定to足够大*/
- void copy ( char to [ ], char from [ ])
- {
- int i;
- i = 0;
- while ( ( to[ i ] = from [ i ]) != '/0')
- ++i;
- }
练习1 - 1 6 对用于打印最长行的程序的主程序m a i n进行修改,使之可以打印任意长度的输入
行的长度以及文本行中尽可能多的字符
Revise the main routine of the longest-line program so it will correctly print the length of arbitrarily long input lines, and as much as possible of the text.
- #include <stdio.h>
- #define MAXLINE 1000 /* maximum input line size */
- int getline(char line[], int maxline);
- void copy(char to[], char from[]);
- /* print longest input line */
- int main(void)
- {
- int len; /* current line length */
- int max; /* maximum length seen so far */
- char line[MAXLINE]; /* current input line */
- char longest[MAXLINE]; /* longest line saved here */
- max = 0;
- while((len = getline(line, MAXLINE)) > 0)
- {
- printf("%d: %s", len, line);
- if(len > max)
- {
- max = len;
- copy(longest, line);
- }
- }
- if(max > 0)
- {
- printf("Longest is %d characters:/n%s", max, longest);
- }
- printf("/n");
- return 0;
- }
- /* getline: read a line into s, return length */
- int getline(char s[], int lim)
- {
- int c, i, j;
- for(i = 0, j = 0; (c = getchar())!=EOF && c != '/n'; ++i)
- {
- if(i < lim - 1)
- {
- s[j++] = c;
- }
- }
- if(c == '/n')
- {
- if(i <= lim - 1)
- {
- s[j++] = c;
- }
- ++i;
- }
- s[j] = '/0';
- return i;
- }
- /* copy: copy 'from' into 'to'; assume 'to' is big enough */
- void copy(char to[], char from[])
- {
- int i;
- i = 0;
- while((to[i] = from[i]) != '/0')
- {
- ++i;
- }
- }
- Chris Sidi, however, was not convinced - he thought this answer was "too easy", so he checked with bwk, who agreed. Chris writes: "Looks like Mr. Kernighan meant for "main routine" in Exercise 1-16 to refer to function main(), saying your solution of modifying getline() is "too easy." :) (Though I think your solution shouldn't be removed from the Answers web site, just complimented with another one that only modifies main())"
- Cue Mr "386sx", riding to the rescue on a white horse...
- /* Exercise 1-16 */
- #include <stdio.h>
- #define MAXLINE 20
- int getline(char s[], int lim);
- void copy(char to[], char from[]);
- int main(void)
- {
- char line[MAXLINE];
- char longest[MAXLINE];
- char temp[MAXLINE];
- int len, max, prevmax, getmore;
- max = prevmax = getmore = 0;
- while((len = getline(line, MAXLINE)) > 0)
- {
- if(line[len - 1] != '/n')
- {
- if(getmore == 0)
- copy(temp, line);
- prevmax += len;
- if(max < prevmax)
- max = prevmax;
- getmore = 1;
- }
- else
- {
- if(getmore == 1)
- {
- if(max < prevmax + len)
- {
- max = prevmax + len;
- copy(longest, temp);
- longest[MAXLINE - 2] = '/n';
- }
- getmore = 0;
- }
- else if(max < len)
- {
- max = len;
- copy(longest, line);
- }
- prevmax = 0;
- }
- }
- if(max > 0)
- {
- printf("%s", longest);
- printf("len = %d/n", max);
- }
- return 0;
- }
- 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;
- ++i;
- }
- else if(c == EOF && i > 0)
- {
- /* gotta do something about no newline preceding EOF */
- s[i] = '/n';
- ++i;
- }
- s[i] = '/0';
- return i;
- }
- void copy(char to[], char from[])
- {
- int i;
- i = 0;
- while((to[i] = from[i]) != '/0')
- ++i;
- }
练习1 - 1 7 编写一个程序,把所有长度大于8 0个字符的输入行都打印出来。
Write a program to print all input lines that are longer than 80 characters.
- #include <stdio.h>
- #define MINLENGTH 81
- int readbuff(char *buffer) {
- size_t i=0;
- int c;
- while (i < MINLENGTH) {
- c = getchar();
- if (c == EOF) return -1;
- if (c == '/n') return 0;
- buffer[i++] = c;
- }
- return 1;
- }
- int copyline(char *buffer) {
- size_t i;
- int c;
- int status = 1;
- for(i=0; i<MINLENGTH; i++)
- putchar(buffer[i]);
- while(status == 1) {
- c = getchar();
- if (c == EOF)
- status = -1;
- else if (c == '/n')
- status = 0;
- else
- putchar(c);
- }
- putchar('/n');
- return status;
- }
- int main(void) {
- char buffer[MINLENGTH];
- int status = 0;
- while (status != -1) {
- status = readbuff(buffer);
- if (status == 1)
- status = copyline(buffer);
- }
- return 0;
- }
练习1-18 编写一个程序,把每个输入行中的尾部空格及制表符都删除掉,并删除空格行。
Write a program to remove all trailing blanks and tabs from each line of input, and to delete entirely blank lines.
- #include <stdio.h>
- #include <stdlib.h>
- #define MAXQUEUE 1001
- int advance(int pointer)
- {
- if (pointer < MAXQUEUE - 1)
- return pointer + 1;
- else
- return 0;
- }
- int main(void)
- {
- char blank[MAXQUEUE];
- int head, tail;
- int nonspace;
- int retval;
- int c;
- retval = nonspace = head = tail = 0;
- while ((c = getchar()) != EOF) {
- if (c == '/n') {
- head = tail = 0;
- if (nonspace)
- putchar('/n');
- nonspace = 0;
- }
- else if (c == ' ' || c == '/t') {
- if (advance(head) == tail) {
- putchar(blank[tail]);
- tail = advance(tail);
- nonspace = 1;
- retval = EXIT_FAILURE;
- }
- blank[head] = c;
- head = advance(head);
- }
- else {
- while (head != tail) {
- putchar(blank[tail]);
- tail = advance(tail);
- }
- putchar(c);
- nonspace = 1;
- }
- }
- return retval;
- }
- #include <stdio.h>
- #include <stdlib.h>
- #define MAXQUEUE 1001
- int advance(int pointer)
- {
- if (pointer < MAXQUEUE - 1)
- return pointer + 1;
- else
- return 0;
- }
- int main(void)
- {
- char blank[MAXQUEUE];
- int head, tail;
- int nonspace;
- int retval;
- int c;
- int spaceJustPrinted; /*boolean: was the last character printed whitespace?*/
- retval = spaceJustPrinted = nonspace = head = tail = 0;
- while ((c = getchar()) != EOF) {
- if (c == '/n') {
- head = tail = 0;
- if (spaceJustPrinted == 1) /*if some trailing whitespace was printed...*/
- retval = EXIT_FAILURE;
- if (nonspace) {
- putchar('/n');
- spaceJustPrinted = 0; /* this instruction isn't really necessary since
- spaceJustPrinted is only used to determine the
- return value, but we'll keep this boolean
- truthful */
- nonspace = 0; /* moved inside conditional just to save a needless
- assignment */
- }
- }
- else if (c == ' ' || c == '/t') {
- if (advance(head) == tail) {
- putchar(blank[tail]); /* these whitespace chars being printed early
- are only a problem if they are trailing,
- which we'll check when we hit a /n or EOF */
- spaceJustPrinted = 1;
- tail = advance(tail);
- nonspace = 1;
- }
- blank[head] = c;
- head = advance(head);
- }
- else {
- while (head != tail) {
- putchar(blank[tail]);
- tail = advance(tail);
- }
- putchar(c);
- spaceJustPrinted = 0;
- nonspace = 1;
- }
- }
- /* if the last line wasn't ended with a newline before the EOF,
- we'll need to figure out if trailing space was printed here */
- if (spaceJustPrinted == 1) /*if some trailing whitespace was printed...*/
- retval = EXIT_FAILURE;
- return retval;
- }
练习1-19 编写函数reverse ( s ),把字符串s颠倒过来。用它编写一个程序,一次把一个输
入行字符串颠倒过来。
Write a function reverse(s)
that reverses the character string s
. Use it to write a program that reverses its input a line at a time
- #include <stdio.h>
- #define MAX_LINE 1024
- void discardnewline(char s[])
- {
- int i;
- for(i = 0; s[i] != '/0'; i++)
- {
- if(s[i] == '/n')
- s[i] = '/0';
- }
- }
- int reverse(char s[])
- {
- char ch;
- int i, j;
- for(j = 0; s[j] != '/0'; j++)
- {
- }
- --j;
- for(i = 0; i < j; i++)
- {
- ch = s[i];
- s[i] = s[j];
- s[j] = ch;
- --j;
- }
- return 0;
- }
- 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;
- }
- int main(void)
- {
- char line[MAX_LINE];
- while(getline(line, sizeof line) > 0)
- {
- discardnewline(line);
- reverse(line);
- printf("%s/n", line);
- }
- return 0;
- }
1.10 外部变量与作用域
- #include <stdio.h>
- #define MAXLINE 1000 /* 最大输入行的大小*/
- int max; /* 至目前为止所发现的最长行的长度*/
- char line[MAXLINE]; /* 当前输入的行*/
- char longest[MAXLINE]; /* 用于保存最长的行*/
- int getline (void );
- void copy ( void );
- /* 打印最长的输入行; 特别版本*/
- main ( )
- {
- int len;
- extern int max;
- extern char longest[ ];
- max = 0;
- while ( ( len = getline ( ) ) > 0 )
- if (len > max) {
- max = len;
- copy ( );
- }
- if (max > 0) /* 有一行*/
- printf ("%s" , longest ) ;
- return 0 ;
- }
- /* getline:特别版本*/
- int getline (void )
- {
- int c, i;
- extern char line[ ];
- for (i = 0; i < MAXLINE -1 && (c = getchar ( ) ) != EOF && c != '/n'; ++i )
- line[i] = c;
- if (c == '/n' ) {
- line[i] = c;
- ++i;
- }
- line[i] = '/0';
- return i;
- }
- /* copy:特别版本*/
- void copy ( void )
- {
- int i;
- extern char line[ ], longest[ ];
- i = 0;
- while ( ( longest[ i ] = line [ i ]) != '/0')
- ++i;
- }
练习1-20 编写程序d e t a b,将输入中的制表符替换成适当数目的空白符(使空白充满到下
一制表符停止位)。假定制表符停止位的位置是固定的,比如在每个n列的位置上。n应作成变量
或符号参数吗?
Write a program detab
that replaces tabs in the input with the proper number of blanks to space to the next tab stop. Assume a fixed set of tab stops, say every n columns. Should n be a variable or a symbolic parameter?
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #define MAX_BUFFER 1024
- #define SPACE ' '
- #define TAB '/t'
- int CalculateNumberOfSpaces(int Offset, int TabSize)
- {
- return TabSize - (Offset % TabSize);
- }
- /* K&R's getline() function from 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;
- ++i;
- }
- s[i] = '/0';
- return i;
- }
- int main(void)
- {
- char Buffer[MAX_BUFFER];
- int TabSize = 5; /* A good test value */
- int i, j, k, l;
- while(getline(Buffer, MAX_BUFFER) > 0)
- {
- for(i = 0, l = 0; Buffer[i] != '/0'; i++)
- {
- if(Buffer[i] == TAB)
- {
- j = CalculateNumberOfSpaces(l, TabSize);
- for(k = 0; k < j; k++)
- {
- putchar(SPACE);
- l++;
- }
- }
- else
- {
- putchar(Buffer[i]);
- l++;
- }
- }
- }
- return 0;
- }
- In answer to the question about whether n should be variable or symbolic, I'm tempted to offer the answer 'yes'. :-) Of course, it should be variable, to allow for modification of the value at runtime, for example via a command line argument, without requiring recompilation.
练习1-21 编写程序e n t a b,将空白符串用可达到相同空白的最小数目的制表符和空白符来
替换。使用与d e t a b程序相同的制表停止位。请问,当一个制表符与一个空白符都可以到达制表
符停止位时,选用哪一个比较好?
Write a program entab
that replaces strings of blanks with the minimum number of tabs and blanks to achieve the same spacing. Use the same stops as for detab
. When either a tab or a single blank would suffice to reach a tab stop, which should be given preference?
- #include <stdio.h>
- #define MAXLINE 1000 /* max input line size */
- #define TAB2SPACE 4 /* 4 spaces to a tab */
- char line[MAXLINE]; /*current input line*/
- int getline(void); /* taken from the KnR book. */
- int
- main()
- {
- int i,t;
- int spacecount,len;
- while (( len = getline()) > 0 )
- {
- spacecount = 0;
- for( i=0; i < len; i++)
- {
- if(line[i] == ' ')
- spacecount++; /* increment counter for each space */
- if(line[i] != ' ')
- spacecount = 0; /* reset counter */
- if(spacecount == TAB2SPACE) /* Now we have enough spaces
- ** to replace them with a tab
- */
- {
- /* Because we are removing 4 spaces and
- ** replacing them with 1 tab we move back
- ** three chars and replace the ' ' with a /t
- */
- i -= 3; /* same as "i = i - 3" */
- len -= 3;
- line[i] = '/t';
- /* Now move all the char's to the right into the
- ** places we have removed.
- */
- for(t=i+1;t<len;t++)
- line[t]=line[t+3];
- /* Now set the counter back to zero and move the
- ** end of line back 3 spaces
- */
- spacecount = 0;
- line[len] = '/0';
- }
- }
- printf("%s", line);
- }
- return 0;
- }
- /* getline: specialized version */
- int getline(void)
- {
- int c, i;
- extern char line[];
- for ( i=0;i<MAXLINE-1 && ( c=getchar()) != EOF && c != '/n'; ++i)
- line[i] = c;
- if(c == '/n')
- {
- line[i] = c;
- ++i;
- }
- line[i] = '/0';
- return i;
- }
- Stefan Farfeleder's Cat 1 solution:
- /* 1-21.c */
- #include <stdio.h>
- #define TABSTOP 4
- int main(void)
- {
- size_t spaces = 0;
- int ch;
- size_t x = 0; /* position in the line */
- size_t tabstop = TABSTOP; /* get this from the command-line
- * if you want to */
- while ((ch = getchar()) != EOF)
- {
- if (ch == ' ')
- {
- spaces++;
- }
- else if (spaces == 0) /* no space, just printing */
- {
- putchar(ch);
- x++;
- }
- else if (spaces == 1) /* just one space, never print a tab */
- {
- putchar(' ');
- putchar(ch);
- x += 2;
- spaces = 0;
- }
- else
- {
- while (x / tabstop != (x + spaces) / tabstop)
- /* are the spaces reaching behind the next tabstop ? */
- {
- putchar('/t');
- x++;
- spaces--;
- while (x % tabstop != 0)
- {
- x++;
- spaces--;
- }
- }
- while (spaces > 0) /* the remaining ones are real space */
- {
- putchar(' ');
- x++;
- spaces--;
- }
- putchar(ch); /* now print the non-space char */
- x++;
- }
- if (ch == '/n')
- {
- x = 0; /* reset line position */
- }
- }
- return 0;
- }
置在输入的第n列之前的最后一个非空白字符之后。要保证程序具备一定的智能,能应付输入行
很长以及在指定的列前没有空白符或制表符时的情况。
- #include <stdio.h>
- #define MAXLINE 1000 /* max input line size */
- char line[MAXLINE]; /*current input line*/
- int getline(void); /* taken from the KnR book. */
- int
- main()
- {
- int t,len;
- int location,spaceholder;
- const int FOLDLENGTH=70; /* The max length of a line */
- while (( len = getline()) > 0 )
- {
- if( len < FOLDLENGTH )
- {
- }
- else
- {
- /* if this is an extra long line then we
- ** loop through it replacing a space nearest
- ** to the foldarea with a newline.
- */
- t = 0;
- location = 0;
- while(t<len)
- {
- if(line[t] == ' ')
- spaceholder = t;
- if(location==FOLDLENGTH)
- {
- line[spaceholder] = '/n';
- location = 0;
- }
- location++;
- t++;
- }
- }
- printf ( "%s", line);
- }
- return 0;
- }
- /* getline: specialized version */
- int getline(void)
- {
- int c, i;
- extern char line[];
- for ( i=0;i<MAXLINE-1 && ( c=getchar()) != EOF && c != '/n'; ++i)
- line[i] = c;
- if(c == '/n')
- {
- line[i] = c;
- ++i;
- }
- line[i] = '/0';
- return i;
- }
练习1-23 编写一个用于把C程序中所有注解都删除掉的程序。不要忘记处理好带引号的字
符串与字符常量。在C程序中注解不允许嵌套。
Write a program to remove all comments from a C program. Don't forget to handle quoted strings and character constants properly. C comments do not nest.
- This was the first exercise to be posted as a fun "competition" on comp.lang.c, on 1 June 2000. As a result, there was a small flurry of submissions. Not all of them are completely working solutions. See the very end of this page for a test program which breaks most of them. :-)
- Category 0 Solutions
- From Rick Dearman
- Now handles "/* comment in string */" correctly, but does not remove the comment from
- return /* comment inside return statement */ 0;
- /******************************************************
- "Write a program to remove all comments from a C program.
- Don't forget to handle quoted strings and character
- constants properly. C comments do not nest."
- Author: Rick Dearman (rick@ricken.demon.co.uk)
- ******************************************************/
- #include <stdio.h>
- #define MAXLINE 1000 /* max input line size */
- char line[MAXLINE]; /*current input line*/
- int getline(void); /* taken from the KnR book. */
- int
- main()
- {
- int in_comment,len;
- int in_quote;
- int t;
- in_comment = in_quote = t = 0;
- while ((len = getline()) > 0 )
- {
- t=0;
- while(t < len)
- {
- if( line[t] == '"')
- in_quote = 1;
- if( ! in_quote )
- {
- if( line[t] == '/' && line[t+1] == '*')
- {
- t=t+2;
- in_comment = 1;
- }
- if( line[t] == '*' && line[t+1] == '/')
- {
- t=t+2;
- in_comment = 0;
- }
- if(in_comment == 1)
- {
- t++;
- }
- else
- {
- printf ("%c", line[t]);
- t++;
- }
- }
- else
- {
- printf ("%c", line[t]);
- t++;
- }
- }
- }
- return 0;
- }
- /* getline: specialized version */
- int getline(void)
- {
- int c, i;
- extern char line[];
- for ( i=0;i<MAXLINE-1 && ( c=getchar()) != EOF && c != '/n'; ++i)
- line[i] = c;
- if(c == '/n')
- {
- line[i] = c;
- ++i;
- }
- line[i] = '/0';
- return i;
- }
- From Ben Pfaff
- This version is a bugfix for the code var/'/2'
- /* K&R2 1-23: Write a program to remove all comments from a C program.
- Don't forget to handle quoted strings and character constants
- properly. C comments do not nest.
- This solution does not deal with other special cases, such as
- trigraphs, line continuation with /, or <> quoting on #include,
- since these aren't mentioned up 'til then in K&R2. Perhaps this is
- cheating.
- Note that this program contains both comments and quoted strings of
- text that looks like comments, so running it on itself is a
- reasonable test. It also contains examples of a comment that ends
- in a star and a comment preceded by a slash. Note that the latter
- will break C99 compilers and C89 compilers with // comment
- extensions.
- Interface: The C source file is read from stdin and the
- comment-less output is written to stdout. **/
- #include <stdio.h>
- int
- main(void)
- {
- #define PROGRAM 0
- #define SLASH 1
- #define COMMENT 2
- #define STAR 3
- #define QUOTE 4
- #define LITERAL 5
- /* State machine's current state, one of the above values. */
- int state;
- /* If state == QUOTE, then ' or ". Otherwise, undefined. */
- int quote;
- /* Input character. */
- int c;
- state = PROGRAM;
- while ((c = getchar()) != EOF) {
- /* The following cases are in guesstimated order from most common
- to least common. */
- if (state == PROGRAM || state == SLASH) {
- if (state == SLASH) {
- /* Program text following a slash. */
- if (c == '*')
- state = COMMENT;
- else {
- putchar('/');
- state = PROGRAM;
- }
- }
- if (state == PROGRAM) {
- /* Program text. */
- if (c == '/'' || c == '"') {
- quote = c;
- state = QUOTE;
- putchar(c);
- }
- else if (c == "/*"[0])
- state = SLASH;
- else
- putchar(c);
- }
- }
- else if (state == COMMENT) {
- /* Comment. */
- if (c == "/*"[1])
- state = STAR;
- }
- else if (state == QUOTE) {
- /* Within quoted string or character constant. */
- putchar(c);
- if (c == '//')
- state = LITERAL;
- else if (c == quote)
- state = PROGRAM;
- }
- else if (state == SLASH) {
- }
- else if (state == STAR) {
- /* Comment following a star. */
- if (c == '/')
- state = PROGRAM;
- else if (c != '*')
- state = COMMENT;
- }
- else /* state == LITERAL */ {
- /* Within quoted string or character constant, following /. */
- putchar(c);
- state = QUOTE;
- }
- }
- if (state == SLASH)
- putchar('/' //**/
- 1);
- return 0;
- }
- /*
- Local variables:
- compile-command: "checkergcc -W -Wall -ansi -pedantic knr123-0.c -o knr123-0"
- End:
- */
- From Lew Pitcher
- /* Lew Pitcher <lpitcher@yesic.com> */
- /*/
- ** derem - remove C comments
- **
- ** (attempt to solve K&R Exercise 1-22)
- **
- ** As I only have v1 copy of K&R, I cannot
- ** be sure what is covered in K&R ANSI chapter 1.
- ** So, I restrict myself to the components covered
- ** in K&R v1 chapter 1, but modified for requisite ANSI
- ** features (int main() and return value).
- **
- ** Components covered in v1 K&R chapter 1 include:
- ** while (), for (), if () else
- ** getchar(), putchar(), EOF
- ** character constants, character escapes
- ** strings
- ** array subscripting
- **
- ** Not directly covered are
- ** string subscripting ( "/*"[0] )
- ** initializers ( int state = PROGRAM; )
- **/
- /*/*/
- #include <stdio.h>
- #define PROGRAM 0
- #define BEGIN_COMMENT 1
- #define COMMENT 2
- #define END_COMMENT 3
- #define QUOTE 4
- int main(void)
- {
- int this_char, quote_char;
- int state;
- state = PROGRAM;
- while ((this_char = getchar()) != EOF)
- {
- if (state == PROGRAM)
- {
- if (this_char == '/')
- state = BEGIN_COMMENT;
- else if ((this_char == '"') || (this_char == '/''))
- {
- state = QUOTE;
- putchar(quote_char = this_char);
- }
- else putchar(this_char);
- }
- else if (state == BEGIN_COMMENT)
- {
- if (this_char == '*')
- state = COMMENT;
- else
- {
- putchar('/'); /* for the '/' of the comment */
- if (this_char != '/')
- {
- state = PROGRAM;
- putchar(this_char);
- }
- else state = COMMENT; /* stuttered */
- }
- }
- else if (state == QUOTE)
- {
- putchar(this_char);
- if (this_char == '//')
- putchar(getchar()); /* escaped character */
- else if (this_char == quote_char)
- state = PROGRAM;
- }
- else if (state == COMMENT)
- {
- if (this_char == '*')
- state = END_COMMENT;
- }
- else if (state == END_COMMENT)
- {
- if (this_char == '/')
- state = PROGRAM;
- else if (this_char != '*') /* stuttered */
- state = COMMENT;
- }
- }
- return 0;
- }
- From Gregory Pietsch
- /* Gregory Pietsch <gkp1@flash.net> */
- #include <stdio.h>
- char p[] =
- "0/!10/"040/'050.001/011*!21/"/41/'/51./02*!32.!23/ "
- "03*!33.!24/"004//064.045/'005//075.056.047.05";
- int main(){int c,i,d;char s,n;s='0';while((c=getchar())
- !=EOF){d=0;for(i=0;p[i]!='/0'&&d==0;i=i+4){if(p[i]==s&
- (p[i+1]==c||p[i+1]=='.')){if(p[i+2]=='0')putchar(c);else
- if(p[i+2]=='/'){putchar('/');putchar(c);}else if(p[i+2]
- ==' ')putchar(' ');n=p[i+3];d=1;}}s=n;}return 0;}
- Category 1 Solutions
- From Ben Pfaff (again)
- This version has the var/'/2' bug fix.
- /* K&R2 1-23: Write a program to remove all comments from a C program.
- Don't forget to handle quoted strings and character constants
- properly. C comments do not nest.
- This solution does not deal with other special cases, such as
- trigraphs, line continuation with /, or <> quoting on #include,
- since these aren't mentioned up 'til then in K&R2. Perhaps this is
- cheating.
- Note that this program contains both comments and quoted strings of
- text that looks like comments, so running it on itself is a
- reasonable test. It also contains examples of a comment that ends
- in a star and a comment preceded by a slash. Note that the latter
- will break C99 compilers and C89 compilers with // comment
- extensions.
- Interface: The C source file is read from stdin and the
- comment-less output is written to stdout. **/
- #include <stdio.h>
- int
- main(void)
- {
- /* State machine's current state. */
- enum {
- PROGRAM,
- SLASH,
- COMMENT,
- STAR,
- QUOTE,
- LITERAL
- } state;
- /* If state == QUOTE, then ' or ". Otherwise, undefined. */
- int quote;
- state = PROGRAM;
- for (;;) {
- int c = getchar();
- if (c == EOF) {
- if (state == SLASH)
- putchar('/' //**/
- 1 / 1 /'/1');
- break;
- }
- switch (state) {
- case SLASH:
- /* Program text following a slash. */
- if (c == "/*"[1]) {
- state = COMMENT;
- break;
- }
- putchar('/');
- state = PROGRAM;
- /* Fall through. */
- case PROGRAM:
- /* Program text. */
- if (c == '/'' || c == '"') {
- quote = c;
- state = QUOTE;
- putchar(c);
- }
- else if (c == "/*"[0])
- state = SLASH;
- else
- putchar(c);
- break;
- case COMMENT:
- /* Comment. */
- if (c == '*')
- state = STAR;
- break;
- case STAR:
- /* Comment following a star. */
- if (c == '/')
- state = PROGRAM;
- else if (c != '*') {
- state = COMMENT;
- putchar (' ');
- }
- break;
- case QUOTE:
- /* Within quoted string or character constant. */
- putchar(c);
- if (c == '//')
- state = LITERAL;
- else if (c == quote)
- state = PROGRAM;
- break;
- case LITERAL:
- /* Within quoted string or character constant, following /. */
- putchar(c);
- state = QUOTE;
- break;
- default:
- abort();
- }
- }
- return 0;
- }
- /*
- Local variables:
- compile-command: "checkergcc -W -Wall -ansi -pedantic knr123.c -o knr123"
- End:
- */
- From Chris Torek
- /* torek@elf.bsdi.com (Chris Torek) */
- /*
- "Write a program to remove all comments from a C program. Don't forget
- to handle quoted strings and character constants properly. C comments do
- not nest."
- Well, what the heck. I mailed this a day or two ago, but here is
- the posted version. I modified the problem a bit: it removes
- comments from full ANSI C89 or C99 programs, handling trigraphs
- and /-newline sequences. It attempts to preserve any trigraphs in
- the output, even while examining them in the "C code" as their
- translated characters. (I am not sure why I bothered doing all of
- them, when only ??/ matters here.) It keeps output line numbers in
- sync with input line numbers, so that if the output is compiled,
- any error messages will refer back to the proper input source line.
- Lightly tested.
- */
- #include <assert.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- /*
- * This flag controls whether we do trigraph processing.
- */
- int trigraphs = 1;
- /*
- * This flag controls whether a comment becomes "whitespace" (ANSI C)
- * or "nothing at all" (some pre-ANSI K&R C compilers).
- */
- int whitespace = 1;
- /*
- * This flag controls whether we do C89 or C99. (C99 also handles C++.)
- */
- int c99;
- /*
- * These are global so that options() can get at them, and for later
- * error messages if needed.
- */
- const char *inname, *outname;
- int options(const char *, char **);
- void usage(void);
- void process(FILE *, FILE *);
- #ifdef __GNUC__
- void panic(const char *) __attribute__((noreturn));
- #else
- void panic(const char *);
- #endif
- int main(int argc, char **argv) {
- int i;
- FILE *in, *out;
- for (i = 1; i < argc; i++) {
- if (argv[i][0] == '-')
- i += options(argv[i] + 1, argv + i + 1);
- else if (inname == NULL)
- inname = argv[i];
- else
- usage();
- }
- if (inname != NULL) {
- if ((in = fopen(inname, "r")) == NULL) {
- fprintf(stderr, "cannot open %s for reading/n", inname);
- exit(EXIT_FAILURE);
- }
- } else {
- inname = "stdin";
- in = stdin;
- }
- if (outname != NULL) {
- if ((out = fopen(outname, "w")) == NULL) {
- fprintf(stderr, "cannot open %s for writing/n",
- outname);
- exit(EXIT_FAILURE);
- }
- } else {
- outname = "stdout";
- out = stdout;
- }
- process(in, out);
- fclose(in);
- fclose(out);
- exit(EXIT_SUCCESS);
- }
- /*
- * This scans for -o type options. Options that have an argument
- * can either take it immediately or as a subsequent argument (e.g.,
- * -ofoo means the same thing as -o foo). We return 0 for "handled
- * them normally", 1 for "handled them normally but needed more
- * arguments".
- *
- * Currently this function is more powerful than really needed, but
- * if we ever decide to have more arguments...
- */
- int options(const char *afterdash, char **moreargs) {
- int nmore = 0, c;
- while ((c = *afterdash++) != '/0') {
- if (c == 'o') {
- if (*afterdash) {
- outname = afterdash;
- afterdash = "";
- } else if (moreargs[nmore] != NULL)
- outname = moreargs[nmore++];
- else
- usage();
- } else if (c == 't')
- trigraphs = 0;
- else if (c == 'w')
- whitespace = 0;
- else if (c == '9')
- c99 = 1;
- else
- usage();
- }
- return nmore;
- }
- void usage(void) {
- fprintf(stderr, "usage: uncomment [-9tw] [-o outfile] [infile]/n");
- exit(EXIT_FAILURE); /* ??? */
- }
- /*
- * States, level 0:
- * normal
- * trigraph processing: Q1 Q2 (for ??x)
- *
- * States, level 1:
- * backslash-newline processing: BACK (seen /, may consume NL)
- *
- * States, level 2:
- * normal
- * character constant: CC (seen '), CCBACK (seen / inside CC)
- * string constant: SC, SCBACK
- * comment: SLASH, COMM, COMMSTAR (for /, in-comment, & seen-star)
- * C99: SLASHSLASH
- */
- enum l0state {
- L0_NORMAL,
- L0_Q1, L0_Q2
- };
- enum l1state {
- L1_NORMAL,
- L1_BACK
- };
- enum l2state {
- L2_NORMAL,
- L2_CC, L2_CCBACK,
- L2_SC, L2_SCBACK,
- L2_SLASH, L2_COMM, L2_COMMSTAR,
- L2_SLASHSLASH
- };
- struct state {
- FILE *in;
- enum l0state l0state;
- int npushback;
- char pushback[4];
- char pushorig[4]; /* nonzero => trigraph pushback */
- int lastgetc;
- int lineno;
- };
- /*
- * Set up "initial" state.
- */
- static void state0(struct state *sp, FILE *in) {
- sp->in = in;
- sp->l0state = L0_NORMAL;
- sp->npushback = 0;
- sp->lastgetc = 0;
- sp->lineno = 1;
- }
- static void pushback(struct state *sp, int c, char origc) {
- assert(sp->npushback < sizeof sp->pushback);
- sp->pushback[sp->npushback] = c;
- sp->pushorig[sp->npushback++] = origc;
- }
- /*
- * Get a character, doing trigraph processing. Set *origc to 0 for normal
- * characters, or the actual input character pre-trigraph-mapping
- * for trigraph input.
- *
- * As a side effect, this can wind up getting up to 3 characters, maybe
- * stuffing two of them into the pushback buffer sp->buf[]. It also bumps
- * sp->lineno when a previously-read newline has been passed over.
- */
- static int getl0char(struct state *sp, char *origc) {
- int c, newc;
- enum l0state state;
- state = sp->l0state;
- *origc = 0;
- while ((c = getc(sp->in)) != EOF) {
- if (sp->lastgetc == '/n')
- sp->lineno++;
- sp->lastgetc = c;
- switch (state) {
- case L0_NORMAL:
- /* ? => get another character; otherwise we are ok */
- if (c == '?') {
- state = L0_Q1;
- continue;
- }
- assert(sp->l0state == L0_NORMAL);
- return c;
- case L0_Q1:
- /* ?? => get another character */
- if (c == '?') {
- state = L0_Q2;
- continue;
- }
- /* ?X => return ?, look at X later */
- pushback(sp, c, 0);
- sp->l0state = L0_NORMAL;
- return '?';
- case L0_Q2:
- /*
- * ??X, where X is trigraph => map
- * ??X, where X is non-trigraph => tricky
- * ??? => also tricky
- */
- switch (c) {
- case '=':
- newc = '#';
- break;
- case '(':
- newc = '[';
- break;
- case '/':
- newc = '//';
- break;
- case ')':
- newc = ']';
- break;
- case '/'':
- newc = '^';
- break;
- case '<':
- newc = '{';
- break;
- case '!':
- newc = '|';
- break;
- case '>':
- newc = '}';
- break;
- case '?':
- /*
- * This one is slightly tricky. Three '?'s
- * mean that the '?' we read two characters
- * ago gets returned, and the two remaining
- * '?'s leave us in Q2 state.
- */
- sp->l0state = L0_Q2;
- return '?';
- default:
- /*
- * This one returns the first ?, leaves
- * the second ? to be re-examined, and
- * leaves the last character to be re-examined.
- * In any case we are back in "normal" state.
- */
- pushback(sp, c, 0);
- pushback(sp, '?', 0);
- sp->l0state = L0_NORMAL;
- return '?';
- }
- /* mapped a trigraph char -- return new char */
- *origc = c;
- sp->l0state = L0_NORMAL;
- return newc;
- default:
- panic("getl0char state");
- }
- }
- sp->lastgetc = EOF;
- return EOF;
- }
- void warn(struct state *, const char *);
- void process(FILE *in, FILE *out) {
- enum l1state l1state = L1_NORMAL;
- enum l2state l2state = L2_NORMAL;
- int c, pendnls;
- char origc, backc;
- struct state state;
- state0(&state, in);
- pendnls = 0;
- backc = 0; /* defeat gcc warning */
- /*
- * Slight sort-of-bug: files ending in / cause two "final" getc()s.
- */
- do {
- if (state.npushback) {
- c = state.pushback[--state.npushback];
- origc = state.pushorig[state.npushback];
- } else if (trigraphs) {
- c = getl0char(&state, &origc);
- } else {
- c = getc(in);
- origc = 0;
- if (state.lastgetc == '/n')
- state.lineno++;
- state.lastgetc = c;
- }
- /*
- * Do backslash-newline processing.
- */
- switch (l1state) {
- case L1_NORMAL:
- if (c == '//') {
- l1state = L1_BACK;
- backc = origc;
- continue;
- }
- break;
- case L1_BACK:
- /*
- * If backc is nonzero here, the backslash that
- * got us into this state was spelled ??/ --
- * if we eat a newline (and hence the backslash),
- * we forget that the eaten newline was spelled
- * this way. This is sort of a bug, but so it goes.
- */
- l1state = L1_NORMAL;
- if (c == '/n') {
- pendnls++;
- continue;
- }
- if (c != EOF)
- pushback(&state, c, origc);
- c = '//';
- origc = backc;
- break;
- default:
- panic("bad l1state");
- }
- /*
- * Now ready to do "C proper" processing.
- */
- #define SYNCLINES() while (pendnls) putc('/n', out), pendnls--
- #define OUTPUT(ch, tri) ((tri) ? fprintf(out, "??%c", tri) : putc(ch, out))
- #define COPY() OUTPUT(c, origc)
- switch (l2state) {
- case L2_NORMAL:
- switch (c) {
- case '/'':
- l2state = L2_CC;
- break;
- case '"':
- l2state = L2_SC;
- break;
- case '/':
- l2state = L2_SLASH;
- continue;
- default:
- break;
- }
- SYNCLINES();
- if (c != EOF)
- COPY();
- break;
- case L2_CC:
- switch (c) {
- case EOF:
- warn(&state, "EOF in character constant");
- break;
- case '/n':
- warn(&state, "newline in character constant");
- break;
- case '//':
- l2state = L2_CCBACK;
- break;
- case '/'':
- l2state = L2_NORMAL;
- break;
- default:
- break;
- }
- if (c != EOF)
- COPY();
- break;
- case L2_CCBACK:
- switch (c) {
- case EOF:
- warn(&state, "EOF in character constant");
- break;
- case '/n':
- warn(&state, "newline in character constant");
- break;
- default:
- break;
- }
- l2state = L2_CC;
- if (c != EOF)
- COPY();
- break;
- case L2_SC: /* much like CC */
- switch (c) {
- case EOF:
- warn(&state, "EOF in string constant");
- break;
- case '/n':
- warn(&state, "newline in string constant");
- break;
- case '//':
- l2state = L2_SCBACK;
- break;
- case '"':
- l2state = L2_NORMAL;
- break;
- default:
- break;
- }
- if (c != EOF)
- COPY();
- break;
- case L2_SCBACK:
- switch (c) {
- case EOF:
- warn(&state, "EOF in string constant");
- break;
- case '/n':
- warn(&state, "newline in string constant");
- break;
- default:
- break;
- }
- l2state = L2_SC;
- if (c != EOF)
- COPY();
- break;
- case L2_SLASH:
- if (c == '*')
- l2state = L2_COMM;
- else if (c99 && c == '/')
- l2state = L2_SLASHSLASH;
- else {
- SYNCLINES();
- OUTPUT('/', 0);
- if (c != '/') {
- if (c != EOF)
- COPY();
- l2state = L2_NORMAL;
- }
- }
- break;
- case L2_COMM:
- switch (c) {
- case '*':
- l2state = L2_COMMSTAR;
- break;
- case '/n':
- pendnls++;
- break;
- case EOF:
- warn(&state, "EOF inside comment");
- break;
- }
- break;
- case L2_COMMSTAR:
- switch (c) {
- case '/':
- l2state = L2_NORMAL;
- /*
- * If comments become whitespace,
- * and we have no pending newlines,
- * must emit a blank here.
- *
- * The comment text is now all eaten.
- */
- if (whitespace && pendnls == 0)
- putc(' ', out);
- SYNCLINES();
- break;
- case '*':
- /* stay in L2_COMMSTAR state */
- break;
- case EOF:
- warn(&state, "EOF inside comment");
- break;
- case '/n':
- pendnls++;
- /* FALLTHROUGH */
- default:
- l2state = L2_COMM;
- }
- break;
- case L2_SLASHSLASH:
- switch (c) {
- case EOF:
- /* ??? do we really care? */
- warn(&state, "EOF inside //-comment");
- break;
- case '/n':
- l2state = L2_NORMAL;
- pendnls++; /* cheesy, but... */
- SYNCLINES();
- default:
- break;
- }
- break;
- default:
- panic("bad l2state");
- }
- } while (c != EOF);
- SYNCLINES();
- }
- void warn(struct state *sp, const char *msg) {
- fprintf(stderr, "uncomment: %s(%d): %s/n", inname, sp->lineno, msg);
- }
- void panic(const char *msg) {
- fprintf(stderr, "panic: %s/n", msg);
- abort();
- exit(EXIT_FAILURE);
- }
- From Chris Mears
- Here's Chris's updated version, without the bugs (says he). :-)
- /*
- * C comment stripper.
- *
- * Strips comments from C or C++ code.
- */
- #include <stdio.h>
- enum state_t { normal, string, character, block_comment, line_comment};
- enum token_t { none, backslash, slash, star, tri1, tri2, tri_backslash};
- static int print_mode(enum state_t s)
- {
- return (s == normal || s == string || s == character);
- }
- void cstrip(FILE *infile, FILE *outfile)
- {
- int ch;
- int comment_newline = 0;
- enum state_t state = normal;
- enum token_t token = none;
- enum token_t last_token = none;
- if (!infile || !outfile || (infile == outfile)) {
- return;
- }
- while ((ch = fgetc(infile)) != EOF) {
- switch (ch) {
- case '/':
- if (token == tri2) {
- token = tri_backslash;
- if (print_mode(state))
- fputc(ch, outfile);
- } else if (state == string || state == character) {
- fputc(ch, outfile);
- token = slash;
- } else if (state == block_comment && token == star) {
- state = normal;
- token = none;
- /* Replace block comments with whitespace. */
- if (comment_newline) {
- fputc('/n', outfile);
- } else {
- fputc(' ', outfile);
- }
- } else if (state == normal && token == slash) {
- state = line_comment;
- token = slash;
- } else {
- token = slash;
- }
- break;
- case '//':
- if (state == normal && token == slash)
- fputc('/', outfile);
- if (print_mode(state))
- fputc(ch, outfile);
- if (token == backslash || token == tri_backslash) {
- token = none;
- } else {
- last_token = token;
- token = backslash;
- }
- break;
- case '"':
- if (state == normal && token == slash)
- fputc('/', outfile);
- if (state == string && token != backslash)
- state = normal;
- else if (state == normal && token != backslash)
- state = string;
- if (print_mode(state))
- fputc(ch, outfile);
- token = none;
- break;
- case '/'':
- if (state == normal && token == slash)
- fputc('/', outfile);
- if (state == character && token != backslash)
- state = normal;
- else if (state == normal && token != backslash)
- state = character;
- if (print_mode(state))
- fputc(ch, outfile);
- token = none;
- break;
- case '/n':
- /* This test is independent of the others. */
- if (state == block_comment)
- comment_newline = 1;
- if (state == normal && token == slash)
- fputc('/', outfile);
- if (token == backslash || token == tri_backslash)
- token = last_token;
- else if (state == line_comment &
- token != backslash) {
- state = normal;
- token = none;
- } else {
- token = none;
- }
- if (print_mode(state))
- fputc(ch, outfile);
- break;
- case '*':
- if (state == normal && token == slash) {
- state = block_comment;
- token = none;
- comment_newline = 0;
- } else {
- token = star;
- }
- if (print_mode(state))
- fputc(ch, outfile);
- break;
- case '?':
- if (state == normal && token == slash)
- fputc('/', outfile);
- if (token == tri1) {
- token = tri2;
- } else if (token == tri2) {
- token = tri2; /* retain state */
- } else {
- /* We might need the last token if this
- * trigraph turns out to be a backslash.
- */
- last_token = token;
- token = tri1;
- }
- if (print_mode(state))
- fputc(ch, outfile);
- break;
- default:
- if (state == normal && token == slash)
- fputc('/', outfile);
- if (print_mode(state))
- fputc(ch, outfile);
- token = none;
- break;
- } /* switch */
- } /* while */
- return;
- }
- /* Small driver program. */
- int main(void)
- {
- cstrip(stdin, stdout);
- return 0;
- }
- Here's a critique of the above, sent in by Rick Litherland. (Please note: when Rick posted this, I hadn't yet posted Chris Mears's updated version of the code.)
- (Since I find it hard to pick the solution number out of KRX12300.C at a glance, I'll refer to the solutions as uncomment00, uncomment01, and so on.)
- [Rick - KR means K&R. X means eXercise. 1 means Chapter 1. 23 means exercise 23. The next digit is the category number - 0 == Cat 0 (ANSI C89, with code restricted to what K&R have discussed at this point in the book). The final digit is the solution number. 0 is the first I received in that category, 1 is the second, and so on. (RJH)]
- uncomment03 (Gregory Pietsch)
- ===========
- I can find only one possible flaw in this, namely that it does not allow for a slash in program text being immediately followed by a quotation mark. One could reasonably argue that this is not a flaw at all, because that would never happen in sensible code. On the other hand, it can happen in legal code, as demonstrated by the following complete (if useless) program.
- #include <stdio.h>
- int main(void)
- {
- /* print the number three */
- printf("%d/n", 6/'/2');
- /* remember to return a value from main */
- return 0;
- }
- When this is fed to uncomment03, the output is
- #include <stdio.h>
- int main(void)
- {
- printf("%d/n", 6/'/2');
- /* remember to return a value from main */
- return 0;
- }
- Clearly, uncomment03 realises that the second comment is too important to remove. Um, sorry, that was a feeble excuse for a joke. What's happening is that uncomment03 doesn't recognise the beginning of the character constant '/2', so it takes the closing quote as the start of a "character constant" that is never terminated. The peculiar idiom 6/'/2' for 3 can be replaced by the even more brain-damaged 6/"/2"[0] with the same effect. Since uncomment03 is table-driven, it's easy to make it recognise these situations by adding two new rules to the table.
- /* modified krx12303.c */
- #include <stdio.h>
- char p[] =
- "0/!10/"@40/'@50.@01/@11*!2"
- "1/"/41/'/5" /* added by RAL */
- "1./02*!32.!23/ 03*!33.!24/"@04//@64.@45/'@05//@75.@56.@47.@5";
- int main(){int c,i,d;char s,n;s='0';while((c=getchar())
- !=EOF){d=0;for(i=0;p[i]!='/0'&&d==0;i=i+4){if(p[i]==s&
- (p[i+1]==c||p[i+1]=='.')){if(p[i+2]=='@')putchar(c);else
- if(p[i+2]=='/'){putchar('/');putchar(c);}else if(p[i+2]
- ==' ')putchar(' ');n=p[i+3];d=1;}}s=n;}return 0;}
- /* end of modified krx12303.c */
- uncomment02 (Lew Pitcher)
- ===========
- uncomment11 (Chris Torek)
- ===========
- These have the same problem (or non-problem, according to your point of view) as uncomment03. If it were regarded as a problem, it could probably be fixed quite easily, though not (I think) as neatly as with uncomment03; I haven't looked at these carefully enough to be sure.
- uncomment01, uncomment10 (Ben Pfaff)
- =========== ===========
- An oversight has the effect that if a slash in program text is followed by anything other than a star or another slash, the following character is dropped. For example, with input
- int a = 4/2;
- the output is
- int a = 4/;
- The correction is the same in both cases; replace
- /* Program text following a slash. */
- if (c == '*')
- state = COMMENT;
- else {
- putchar('/');
- if (c != '/')
- state = PROGRAM;
- }
- by
- /* Program text following a slash. */
- if (c == '*')
- state = COMMENT;
- else {
- putchar('/');
- if (c != '/') {
- putchar(c);
- state = PROGRAM;
- }
- }
- After this, these programs will have the same problem (or not) as the previous three.
- uncomment12 (Chris Mears)
- ===========
- This is a completely different kettle of fish. If you run this with Ben Pfaff's solution as input, the output is quite bizarre; some comments have just their initial and final slashes removed, for instance. I've managed to find two things contributing to this. The first is illustrated by the input
- int c = '/';
- with output
- int c = '';
- This can be fixed by changing the lines
- case '/':
- if (state == string) {
- to
- case '/':
- if (state == string || state == character) {
- However, with or without this change, the input
- char *p = "//"; /* This is not a comment. */
- is left unchanged. What happens is that the closing quote of the string literal isn't recognised as such because of the preceding backlash, despite the backslash before that. The handling of backslashes is split between three cases (at least), and is complicated enough that I don't feel competent to propose a remedy.
- This program breaks most of the above submissions:
- /* krx123tp.c - a test program to serve as input to krx123*.c
- *
- * This is a shameless copy of Ben Pfaff's solution, to which I have
- * added a few extra statements to further test the candidate programs
- * for this exercise. As Ben says, this program already contains lots
- * of examples of comments and not-quite-comments. I've just made it
- * a little tougher.
- *
- */
- /* K&R2 1-23: Write a program to remove all comments from a C program.
- Don't forget to handle quoted strings and character constants
- properly. C comments do not nest.
- This solution does not deal with other special cases, such as
- trigraphs, line continuation with /, or <> quoting on #include,
- since these aren't mentioned up 'til then in K&R2. Perhaps this is
- cheating.
- Note that this program contains both comments and quoted strings of
- text that looks like comments, so running it on itself is a
- reasonable test. It also contains examples of a comment that ends
- in a star and a comment preceded by a slash. Note that the latter
- will break C99 compilers and C89 compilers with // comment
- extensions.
- Interface: The C source file is read from stdin and the
- comment-less output is written to stdout. **/
- #include <stdio.h>
- int
- main(void)
- {
- /* State machine's current state. */
- enum {
- PROGRAM,
- SLASH,
- COMMENT,
- STAR,
- QUOTE,
- LITERAL
- } state;
- /* If state == QUOTE, then ' or ". Otherwise, undefined. */
- int quote;
- state = PROGRAM;
- for (;;) {
- int c = getchar();
- if (c == EOF) {
- if (state == SLASH)
- putchar('/' //**/
- 1 / 1 /'/1');
- break;
- }
- if(0)
- printf("%d/n", 6/'/2');
- /* line of code, and comment, added by RJH 10 July 2000 */
- switch (state) {
- case SLASH:
- /* Program text following a slash. */
- if (c == "/*"[1]) {
- state = COMMENT;
- break;
- }
- putchar('/');
- state = PROGRAM;
- /* Fall through. */
- case PROGRAM:
- /* Program text. */
- if (c == '/'' || c == '"') {
- quote = c;
- state = QUOTE;
- putchar(c);
- }
- else if (c == "/*"[0])
- state = SLASH;
- else
- putchar(c);
- break;
- case COMMENT:
- /* Comment. */
- if (c == '*')
- state = STAR;
- break;
- case STAR:
- /* Comment following a star. */
- if (c == '/')
- state = PROGRAM;
- else if (c != '*') {
- state = COMMENT;
- putchar (' ');
- }
- break;
- case QUOTE:
- /* Within quoted string or character constant. */
- putchar(c);
- if (c == '//')
- state = LITERAL;
- else if (c == quote)
- state = PROGRAM;
- break;
- case LITERAL:
- /* Within quoted string or character constant, following /. */
- putchar(c);
- state = QUOTE;
- break;
- default:
- abort();
- }
- }
- return /* this comment added by RJH 10 July 2000 */ 0;
- }
- /*
- Local variables:
- compile-command: "checkergcc -W -Wall -ansi -pedantic knr123.c -o knr123"
- End:
- */
练习1-24 编写一个程序,查找C程序中的基本语法错误,如圆括号、方括号、花括号不配
对等。不要忘记引号(包括单引号和双引号)、换码序列与注解。(如果读者想把该程序编写成
完全通用性的,那么难度比较大。)
Write a program to check a C program for rudimentary syntax errors like unbalanced parentheses, brackets and braces. Don't forget about quotes, both single and double, escape sequences, and comments. (This program is hard if you do it in full generality.)
- Rick Dearman's Category 0 solution:
- /******************************************************
- KnR 1-24
- --------
- Write a program to check the syntax of a C program
- for matching {} () "" '' []
- Author: Rick Dearman
- email: rick@ricken.demon.co.uk
- ******************************************************/
- #include <stdio.h>
- #define MAXLINE 1000 /* max input line size */
- char line[MAXLINE]; /*current input line*/
- int getline(void); /* taken from the KnR book. */
- int
- main()
- {
- int len=0;
- int t=0;
- int brace=0, bracket=0, parenthesis=0;
- int s_quote=1, d_quote=1;
- while ((len = getline()) > 0 )
- {
- t=0;
- while(t < len)
- {
- if( line[t] == '[')
- {
- brace++;
- }
- if( line[t] == ']')
- {
- brace--;
- }
- if( line[t] == '(')
- {
- parenthesis++;
- }
- if( line[t] == ')')
- {
- parenthesis--;
- }
- if( line[t] == '/'')
- {
- s_quote *= -1;
- }
- if( line[t] == '"')
- {
- d_quote *= -1;
- }
- t++;
- }
- }
- if(d_quote !=1)
- printf ("Mismatching double quote mark/n");
- if(s_quote !=1)
- printf ("Mismatching single quote mark/n");
- if(parenthesis != 0)
- printf ("Mismatching parenthesis/n");
- if(brace != 0)
- printf ("Mismatching brace mark/n");
- if(bracket != 0)
- printf ("Mismatching bracket mark/n");
- if( bracket==0 && brace==0 && parenthesis==0 && s_quote == 1 && d_quote == 1)
- printf ("Syntax appears to be correct./n");
- return 0;
- }
- /* getline: specialized version */
- int getline(void)
- {
- int c, i;
- extern char line[];
- for ( i=0;i<MAXLINE-1 && ( c=getchar()) != EOF && c != '/n'; ++i)
- line[i] = c;
- if(c == '/n')
- {
- line[i] = c;
- ++i;
- }
- line[i] = '/0';
- return i;
- }
- Stefan Farfeleder's Category 1 solution:
- /* 1-24.c */
- #include <stdio.h>
- #include <stdlib.h>
- #define MAX_STACK 1024
- enum
- {
- CODE, /* nothing of the following */
- COMMENT, /* inside a comment */
- QUOTE1, /* inside '' */
- QUOTE2 /* inside "" */
- };
- int main(void)
- {
- int ch;
- int state = CODE;
- char stack[MAX_STACK];
- size_t top = 0; /* points to the top of the stack :-) */
- size_t line = 1;
- int error = 0; /* for ok-message */
- while ((ch = getchar()) != EOF)
- {
- if (ch == '/n')
- {
- line++;
- }
- switch (state)
- {
- case CODE:
- if (ch == '/'')
- {
- state = QUOTE1;
- }
- else if (ch == '"')
- {
- state = QUOTE2;
- }
- else if (ch == '/')
- {
- int second = getchar();
- if (second == '*')
- {
- state = COMMENT;
- }
- else
- {
- ungetc(second, stdin);
- }
- }
- else if (ch == '(' || ch == '[' || ch == '{')
- {
- if (top < MAX_STACK)
- {
- stack[top++] = ch;
- }
- else
- {
- printf("Stack too small!/n");
- return EXIT_FAILURE; /* exit gracefully :-) */
- }
- }
- else if (ch == ')' || ch == ']' || ch == '}')
- {
- if (top == 0) /* found closing brace but stack is empty */
- {
- printf("Line %lu: Closing '%c' found without "
- "counterpart./n", (unsigned long)line, ch);
- error = 1;
- }
- else
- {
- char open = stack[--top];
- if ((ch == ')' && open != '(') ||
- (ch == ']' && open != '[') ||
- (ch == '}' && open != '{'))
- {
- printf("Line %lu: Closing '%c' does not match "
- "opening '%c'./n", (unsigned long)line, ch, open);
- error = 1;
- }
- }
- }
- break;
- case COMMENT:
- if (ch == '*')
- {
- int second = getchar();
- if (second == '/')
- {
- state = CODE;
- }
- else
- {
- ungetc(second, stdin);
- }
- }
- break;
- case QUOTE1:
- if (ch == '//')
- {
- (void)getchar(); /* an escaped char inside '' throw it away */
- }
- else if (ch == '/'')
- {
- state = CODE;
- }
- break;
- case QUOTE2:
- if (ch == '//')
- {
- (void)getchar(); /* an escaped char inside "" throw it away */
- }
- else if (ch == '"')
- {
- state = CODE;
- }
- break;
- }
- }
- if (state == COMMENT)
- {
- printf("Code ends inside comment!/n");
- }
- else if (state == QUOTE1)
- {
- printf("Code ends inside single quotes!/n");
- }
- else if (state == QUOTE2)
- {
- printf("Code ends inside double quotes!/n");
- }
- else if (top == 0 && error == 0)
- {
- printf("Code seems to be ok./n");
- }
- if (top > 0) /* still something in the stack */
- {
- size_t i;
- for (i = 0; i < top; i++)
- {
- printf("Opening '%c' found without counterpart./n", stack[i]);
- }
- }
- return 0;
- }
- Stig Brautaset's Cat 1 solution:
- /* This is my first rudimentary C syntax checker. It checks for syntax errors,
- * like closing a set of brackets using the wrong type. It is not *very* good
- * at it, but it does not bother about comments, and it does know something
- * about escape sequences and character strings/constants.
- *
- * It uses a simple static stack to keep track of the braces, and it also uses
- * a stack to keep track of the errors on each line. Someday I might change
- * that to use a queue for the error-tracking, because as it is now, it outputs
- * the rightmost error on the line first, and then it steps leftwards (if there
- * is more than one error on each line).
- *
- * I might also implement my dynamically allocated stack and queue implementa-
- * tions, so that running out of space in the stack is not an issue. I might
- * also skip it, since it has little to do with the exercise in question.
- *
- * The program is especially bad at error-recovery. If it finds an error, (or
- * something it believes to be an error) subsequent errors reported might be a
- * bit dubious.
- */
- #include <stdio.h>
- #define MAXVAL 1000
- #define MAXLINE 1000
- typedef struct {
- int top;
- int val[MAXVAL];
- int pos[MAXVAL];
- } stackstr;
- /* very simple stack push function */
- int push(stackstr *stk, int foo, int bar)
- {
- if (stk->top == MAXVAL) {
- printf("stack overflow. NOT putting more values on the stack./n");
- return 1;
- }
- stk->val[stk->top] = foo;
- stk->pos[stk->top] = bar;
- stk->top++;
- return 0;
- }
- /* very simple function to pop values off a stack */
- int pop(stackstr *stk, int *foo, int *bar)
- {
- if (stk->top == 0) {
- return 1;
- }
- stk->top--;
- *foo = stk->val[stk->top];
- *bar = stk->pos[stk->top];
- return 0;
- }
- /* we go through the input one line at a time, and this function
- * gets the line to test
- */
- int getline(char *s, int lim)
- {
- int i, c;
- 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;
- }
- void scanline(stackstr *stk, stackstr *errstk, char *s, int len)
- {
- int i, c, d, foo;
- static int string = 0, comment = 0, isconst = 0, escape = 0;
- for (i = 0; i < len; i++) {
- c = *(s + i);
- if (!comment) {
- if (c == '//') { /* we have an escape */
- /* test for a valid escape sequence */
- if ((d = *(s + ++i)) == '//' || d == 'n' || d == '0' || d == 'r' || d == '?'
- || d == 't' || d == '/'' || d == '/"' || d == 'b' || d == 'x') {
- continue; /* ok, valid escape sequence -- don't bother about it */
- } else {
- push(errstk, 5, i); /* illigal escape sequence */
- }
- } else if (c == '/"') { /* is it a text string then? */
- if (!string)
- string = 1;
- else
- string = 0;
- } else if (c == '/'') { /* is it a constant? */
- if (!isconst)
- isconst = 1;
- else
- isconst = 0;
- }
- }
- if (!isconst && !string && !comment && c == '/') {
- if ((d = *(s + ++i)) == '*')
- comment = 1;
- } else if (comment && c == '*') {
- if ((d = *(s + ++i)) == '/') {
- comment = 0;
- continue; /* done with the comment stuff -- start over */
- }
- }
- /* only bother about ({[ ]})'s that's not in
- * a string, constant or comment
- */
- if (!isconst && !string && !comment) {
- if (c == '(' || c == '{' || c == '[') {
- push(stk, c, 0);
- } else if (c == ']' || c == '}' || c == ')') {
- if (pop(stk, &d, &foo)) {
- push(errstk, 4, i);
- }
- if (c == ')' && d != '(') {
- push(stk, d, 0);
- push(errstk, 1, i);
- } else if (c == ']' && d != '[') {
- push(stk, d, 0);
- push(errstk, 2, i);
- } else if (c == '}' && d != '{') {
- push(stk, d, 0);
- push(errstk, 3, i);
- }
- }
- }
- }
- }
- /* print errors on the line (if there were any) */
- void print_err(stackstr *errstk, int lineno)
- {
- int errno, pos;
- /* yes I know... this way the errors come "backwards" :) */
- while (!pop(errstk, &errno, &pos)) {
- printf("on line number %d: ", lineno);
- switch(errno) {
- case 1:
- printf("closing unopened parantheses, column %d/n", pos+1);
- break;
- case 2:
- printf("closing unopened square bracket, column %d/n", pos+1);
- break;
- case 3:
- printf("closing unopened curly braces, column %d/n", pos+1);
- break;
- case 4:
- printf("trying to close unopened block/control structure, column %d/n", pos+1);
- break;
- case 5:
- printf("illigal escape sequence, column %d/n", pos+1);
- break;
- default:
- printf("undeterminable error/n");
- break;
- }
- }
- }
- int main(void)
- {
- stackstr errstk = {0}, stk = {0};
- int c, linenbr = 0, errcount = 0, linelen;
- char line[MAXLINE];
- while ((linelen = getline(line, MAXLINE)) > 0) {
- linenbr++;
- scanline(&stk, &errstk, line, linelen);
- if (errstk.top) {
- print_err(&errstk, linenbr);
- errcount++;
- }
- }
- if (errcount)
- printf("%d lines contained error(s)/n", errcount);
- else
- printf("Well, *I* didn't find any syntax errors, but don't take my word for it...:)/n");
- return 0;
- }
- "The C Programming Language", 2nd edition, Kernighan and Ritchie
- The C Programming Language Brian W. Kernighan & Dennis M. Ritchie
- The C Programming Language 2nd Edition Execise4-2 Answer
- The C Programming Language 2nd Edition Execise5-4 Answer
- Re-Learn C(The C Programming Language.2Nd)
- Common Lisp the Language, 2nd Edition
- The Ruby Way, Second Edition: Solutions and Techniques in Ruby Programming (2nd Edition)
- The C programming Language Second Edition
- Unix Network Programming Volume 1 Compile issue solution for the 2nd and 3rd edition
- MySQL Administrator's Guide and Language Reference (2nd Edition)
- MySQL Administrator's Guide and Language Reference (2nd Edition)
- Unified Modeling Language User Guide, The (2nd Edition)
- The Ruby Way, Second Edition: Solutions and Techniques in Ruby, Programming (Paperback) 2nd.Edition.Oct.2006.eBook-BBL
- Objective-C Programming: The Big Nerd Ranch Guide (2nd Edition) 阅读笔记(Part I & II)
- Objective-C Programming: The Big Nerd Ranch Guide (2nd Edition) 阅读笔记(Part III)
- Objective-C Programming: The Big Nerd Ranch Guide (2nd Edition) 阅读笔记(Part IV)
- Objective-C Programming: The Big Nerd Ranch Guide (2nd Edition) 阅读笔记(Part V)
- 今天完成了《The C programming Language 2nd》前五章的阅读
- 十招能改善你的人际关系
- 经典图片
- NetAdvantage 2007vol2中WebCombo实现AutoComplete效果,并实现“大小写敏感”
- ipvsadm命令参考
- 动态调用web服务
- "The C Programming Language", 2nd edition, Kernighan and Ritchie
- 实战DeviceIoControl 之一:通过API访问设备驱动程序
- 如何手工删除多余的.dll文件
- 写给所有磨合期的情侣
- 我收藏的特殊字符过滤函数
- 实战DeviceIoControl 之二:获取软盘/硬盘/光盘的参数
- 实战DeviceIoControl 之三:制作磁盘镜像文件
- 港报:美国大学生找工作前必做的二十件事
- 查看 Oracle 表空间使用率