《C陷阱与缺陷》读书笔记

书名:C陷阱与缺陷
豆瓣评分:8.9分(584人评价)
博主评价:

未评价很差较差还行推荐力荐

来自豆瓣读书资源
前阵子到上海图书馆借了《C陷阱与缺陷》这本书,也算是C语言中的一本经典书籍了,现在看完了,简单写一些笔记吧。
词法陷阱
1. C语言词法分析中的“贪心法”:从左到右,每一个符号应该包含尽可能多的字符。
2. 除了字符和字符常量,符号的中间不能嵌有空白。 C语言中,符号之间的空白(包括空格符,制表符,换行符)将被忽略。
3. =与==, |与||,&与&&区别
4. 0开头表示八进制,0x开头 十六进制。
5. 用单引号引起的一个字符实际上代表一个整数,整数值对应于该字符在编译器采用的字符集中的序列值。 用双引号引起的字符串,代表的却是一个指向无名数组起始字符的指针,该数组被双引号之间的字符以及一个额外的二进制值为零的字符'\0'初始化。

语法陷阱
1. 任何一个逻辑运算符的优先级低于任何一个关系运算符;移位运算符的优先级比算术运算符要低,但是比关系运算符要高。
2. 分号作为语句结束标志
3. C语言,在预处理器和字符串内部,字符'\'做标志,表示下一个代码行是同一语句的一部分。
4. C语言中,else始终与同一对括号内最的未匹配的if结合。

语义陷阱
1. C语言中只有一维数组,数组的大小必须在编译期就作为一个常数确定下来。多维数组是利用了C语言中数组元素的类型可以是任意的,当然也可以是一个数组。所以可以“仿真”出多维数组。对于一个数组,我们只能做两件事:确定该数组的大小,以及获得指向该数组的下标为0的元素的指针。数组的下标操作其实就是对指针的操作。
2. 数组名a,除了作为运算符sizeof的参数之外,其他情况下,数组名a都都是代表指针。
3. 字符串常量代表了一块包括字符串中所有字符以及一个空字符('\0')的内存区域的地址。
4. C语言中,会自动地将作为参数的数组声明转化为相应的指针声明。
5. 复制指针并不同时复制指针所指向的数据。
6. 对于C编译器,将常数0转换而来的指针不等于任何有效的指针,用NULL代替(#define NULL 0)。 当常数0被转换为指针使用时,这个指针绝对不能被解除引用(dereference),也就是说,绝对不能企图使用该指针所指向的内存中存储的内容。
7. n个元素的数组,它的元素下表范围是从0到n-1。
8. C语言中,只有四个运算符存在求值顺序:&&,  ||, ?:与,       前面三个根据需要进行运算,有可能不全部计算表达式的值;逗号表达式计算一个丢弃一个,只保留最后一个数。
9. 为main函数提供返回值。main函数与其他函数一样,如果并未显式声明返回值类型,那么函数返回值就默认是整型。
10. 对于数组结尾后的下一个元素,取它的地址是合法的;实际读取这个元素的值,其结果是未定义的,而且C编译器常常不能检查出这个错误。

连接
1. 典型的连接器把由编译器或者汇编器生成的若干个目标模块,整合成一个被称为载入模块或可执行文件的实体,该实体能够被操作系统直接执行。
2. 每个外部变量只能定义一次。
3. 为了避免出现的命名冲突,如果一个函数仅仅被同一个源文件中的其他函数调用,我们应该声明该函数为static.
4. 函数应该在调用前定义或声明,否则,返回值默认为整型。
5. 保证一个特定名称的所有外部定义在每个目标模块中都有相同的类型。
6. 我们可以遵循一个简单的规则:每个外部对象只在一个地方声明,一般是在头文件中。

库函数
1. getchar函数
2. 输入fread和输出fwrite同时操作,需在其中插入fseek函数的调用。
3. 调用库函数时,应该先检查作为错误指示的返回值,确定程序执行失败后,再检查errno,来搞清楚出错原因。
4. 信号非常复杂,而且有一些不可移植的特性。让signal处理函数尽可能简单。
5. getchar在stdio.h中被实现为一个宏,如果没有include这个头文件,getchar可能被作为一个函数,所以导致getchar比较慢。

预处理器
1. 预处理:常量替换;宏替换(避免函数调用的开销)
2. 宏定义中要注意空格
3. 宏定义中,最好:把每个参数都用括号括起来;整个结果表达式也应括起来。
4. 递增(++)在宏中的副作用
5. 宏嵌套使用可能会产生非常长的表达式。
6. assert宏: #define assert(e) ((void)((e)||_assert_error(__FILE__,__LINE__)))
7. 宏与类型定义的区别

可移植性缺陷

1           应对C语言标准变更:没看明白,估计是已经过时了,他说编译过不了的程序在我看来是如此的正常

2           标识符名称的限制:这个应该也是过时

3           整数的大小

3.1          定义一个可以存放千万数量级的数值

typedef long tenmil;

3.2          如果定义的整数跟位数有关,如需要一个无符号32位整数,则

3.2.1     这是因为int的位数与机器有关,这样可以增强可移植性,把类型定义放在头文件中

typedef uint32 int;

typedef uint32_p *uint32;

4           字符是有符号整数还是无符号整数

4.1          在字符向整数扩展时,如果字符的最高位为1,则有符号扩展,否则进行无符号扩展

5           位移运算符

5.1          右移时,有符号数补符号位,无符号数补0,默认是有符号数

5.2          移位计数允许的取值范围

5.2.1     右移0~31是可以的,假设int32

5.3          有符号数和无符号数运算,有符号数转换为无符号数

6           内存位置0:感觉不太重要,基本上应该避免读取内存位置0

7           除法运算时发生截断

7.1          这个自己在写程序的时候也会遇到,主要是一些范围方面的麻烦

7.2          书上没有提出什么有效的解决方法

8           随机数大小

8.1          有个宏定义了RAND_MAX,决定随机数的大小

9           大小写转换

9.1          主要是说一些C语言的实现使用宏来实现,一些使用函数来实现

10       首先释放,然后重新分配

10.1       讲的是一些挺怪异的指针使用

10.2       建议指针释放后,就不要再尝试对其进行解释使用

11       可移植性问题的一个例子

11.1       例子觉得不太好,不过有个挺有意思的表达式

11.2       “0123456789”[n%10],这个表达式的含义是n的个位数代表的字符

Big Endian: 最低地址存放高位字节,也叫高位优先。 PowerPC和SPARC处理器一般是高位优先。
Little Endian: 最低位地址存放低位字节,也叫低位优先。  Intel x86/x86_64都采用低位优先。

附带的作者访谈中对C++程序员的重要建议:
1. 避免使用指针
2. 提倡使用程序库
3. 使用类来表示概念

其他人关于本书的笔记:
http://blog.csdn.net/yeshuxiong/article/details/6204725
http://finalhome.org/2011/03/11/c%E9%99%B7%E9%98%B1%E4%B8%8E%E7%BC%BA%E9%99%B7%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/
http://tech.e800.com.cn/articles/2009/715/1247627078725_1.html

master

Stay hungry, stay foolish.

发表评论

邮箱地址不会被公开。 必填项已用*标注

*