目 录
译者序
序
第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 输入与输出:
B.2 字符类测试:
B.3 字符串函数:
B.4 数学函数:
B.5 实用函数:
B.6 诊断:
B.7 变量变元表:
B.8 非局部跳转:
B.9 信号处理:
B.10 日期与时间函数:
B.11 由实现定义的限制:
附录C 变更小结
1.5.1 文件复制
Verify that the expression getchar()
!=
EOF
is 0 or 1.
Write a program to print the value of EOF
.
编写一个用于统计空格、制表符与换行符个数的程序
Write a program to count blanks, tabs, and newlines
编写一个程序,把它的输入复制到输出,并在此过程中将相连的多个空格用一个
空格代替。
Write a program to copy its input to its output, replacing each string of one or more blanks by a single blan
编写一个程序,把它的输入复制到输出,并在此过程中把制表符换成/ 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.
1.5.4 单词计数
你准备怎样测试单词计数程序?如果程序中出现任何错误,那么什么样的输入最
有利于发现这些错误?
How would you test the word count program? What kinds of input are most likely to uncover bugs if there are any?
编写一个程序,以每行一个单词的形式打印输入。
Write a program that prints its input one word per line.
1.6 数组
练习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.
练习1-14 编写一个程序,打印其输入的文件中各个字符出现频率的直方图。
Write a program to print a histogram of the frequencies of different characters in its input.
1.7 函数
练习1-15 重写1 . 2节的温度转换程序,使用函数来实现温度转换。
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
- #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
- #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
- #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
- #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
- 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.
练习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
1.10 外部变量与作用域
练习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?
练习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?
练习1-22 编写一个程序,用于把较长的输入行“折”成短一些的两行或多行,折行的位
置在输入的第n列之前的最后一个非空白字符之后。要保证程序具备一定的智能,能应付输入行
很长以及在指定的列前没有空白符或制表符时的情况。
Write a program to "fold" long input lines into two or more shorter lines after the last non-blank character that occurs before the n -th column of input. Make sure your program does something intelligent with very long lines, and if there are no blanks or tabs before the specified column.
练习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.
练习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.)