《C陷阱和缺陷》阅读笔记

时间:2022-12-28 11:12:49

第1章 词法“陷阱”

“符号”(token)指的是程序的一个基本组成单元。

在C语言中,符号之间的空白(包括空格符、制表符或换行符)将被忽略。

1.1 =不同于==

把比较运算误写成赋值运算。

应该显式地进行比较。

if ((x=y) != 0)
foo();

如果把复制运算误写成比较运算,同样会造成混淆。

1.2 & 和 | 不同于 && 和 ||

1.3 词法分析中的“贪心法”

准二义性问题

1.4 整型常量

如果一个整型常量的第一个字符是数字0,那么该常量将被视作八进制数。因此,10与010的含义截然不同。

1.5 字符与字符串

用单引号引起的一个字符实际上代表一个整数,整数值对应于该字符在编译器采用的字符集中的序列值。

用双引号引起的字符串,代表的确实一个指向无名数组起始字符的指针,该数组被双引号之间的字符以及一个额外的二进制值为零的字符‘\0’初始化。

第2章 语法“陷阱”

2.1 理解函数声明

2.2 运算符的优先级问题

2.3 注意作为语句结束标志的分号

一个重要的例外是在if或者while语句之后需要紧跟一条语句时,如果此时多了一个分好,那么原来紧跟在if或者while子句之后的语句就是一条单独的语句,与条件判断部分没有了任何关系。

当一个声明的结尾紧跟一个函数定义时,如果声明结尾的分号被省略,编译器可能会把申明的类型视作函数的返回值类型。

2.4 switch语句

2.5 函数调用

C语言要求:在函数调用时计时函数不带参数也应该包括参数列表。

2.6 “悬挂”else引发的问题

else始终与同一对括号内内最近的为匹配的if结合。

第3章 语义“陷阱”

3.1 指针和数组

1. C语言中只有以为数组,而且数组的大小必须在编译器就作为一个常熟确定下来。(C99标准允许变长数组(VLA),GCC编译器中实现了变长数组)

2.

*(a+i)简记为a[i]

3.2 非数组的指针

char *r, *malloc();
r
= malloc(strlen(s) + strlen(t) + 1);
if(!r) {
complain();
exit(
1);
}
strcpy(r, s);
strcat(r, t);

free(r);

3.3 作为参数的数组声明

3.4 避免“举偶法”

混淆指针与指针所指向的数据。

3.5 空指针并非空字符串

当常熟0被转换为指针使用时,这个指针绝对不能被解除引用。

3.6 边界计算与不对称边界

内容较多

3.7 求值顺序

运算符&&和运算符||首先对左侧操作数求值,只在需要时才对右侧操作数求值。

3.8 运算符&&、||和!

3.9 整数溢出

当两个操作数都是有符号整数时,“溢出”就有可能发生,而且“溢出”的结果是未定义的。当一个运算的结果发生“溢出”时,做出任何假设都是不安全的。

一种正确的方式是将两者都强制转换为无符号整数:

if ((unsigned)a + (unsigned)b > INT_MAX)
complain();

INT_MAX代表可能的最大整数值,ANSI C标准在<limits.h>中定义。

3.10 为函数main提供返回值

第4章 连接

4.1 什么是连接器

4.2 声明与定义

extern int a;

上述声明是对外部变量a的引用,而不是对a的定义。

同时程序就必须在别的某个地方包括语句

int a;

这两个语句既可以是子啊同一个源文件中,也可以位于程序的不同源文件之中。

严格的规则是,每个外部变量只能够定义一次。(如果违反该规则,产生的结果与系统有关。)大多数系统都会拒绝接收这种程序。

4.3 命名冲突与static修饰符

4.4 形参、实参与返回值

4.5 检查外部类型

4.6 头文件

第5章 库函数

第6章 预处理器

第7章 可移植性缺陷

第8章 建议与答案

附录B 访谈

Koenig:就其本省而言,C++是一种非常低级的语言。唯有利用库,才能写出高层次的程序来。