《C陷阱和缺陷》读书总结随笔
简介
进行对一些比较经典的书籍进行阅读,练习自己的内功,因为博主比较菜,所以近期会对一些书籍进行全面的总结,欢迎大家来指出问题。
使用一句书中的原话
那些自认为已经“学完”C 语言的人,请你们仔细读阅读这篇文章吧。路还长,很多东西要学。我也是……
第一章 词法缺陷
1.=不是==
=是赋值运算符,==是比较运算符
如:
if(x=y)
foo();
仔细看,便会发现if中的判断为=,而非==,这段的意思是,讲x设置为y的值并检查是否非零。
有些编译器会给出警告,所以要写出上面代码的原意
如下:
if((x=y)!=0)
foo();
2.&和|不是&&和||
假设变量 A 的值为 1,变量 B 的值为 0
位运算符作用于位,并逐位执行操作。&、 | 和 ^ 的真值表如下所示:
3.贪心法
n-- >0的含义是n-- >0而不是n- ->0,根据贪心法编译器读入>之前n-- 已经是一个整体了
a+++++b的含义是((a++)++)+b,但语法是错误的,a++的结果不能作为左值再进行运算,因此编译器不会接受a++作为后面的++运算符的操作数。
第二章 句法缺陷
1.运算符并不总是具有你所想象的优先级
if(flags & FLAG != 0)
这一段代码被编译器解释为
if(flags & (FLAG != 0))
因为!=比&绑定得更紧密
需要记住的两个重要的东西是:
- 所有的逻辑运算符具有比所有关系运算符都低的优先级。
- 一位运算符比关系运算符绑定得更紧密,但又不如数学运算符。
2.switch语句
switch语句中case后面的break根据需要可以进行省却
case SUBTRACT:
opnd2 = -opnd2;
/* no break; */
case ADD:
可以这样进行代码编写
哈哈哈,看到这样写我笑出声,这样写却很合理
3.函数调用
和其他程序设计语言不同,C 要求一个函数调用必须有一个参数列表,但可以没有参数。因此,如果
f 是一个函数,
f();
就是对该函数进行调用的语句,而
f;
什么也不做。它会作为函数地址被求值,但不会调用它。
第三章 链接
如果两个文件,一个包含如下声明
int n;
另一个包含如下声明
long n;
这不是一个有效的C程序
第四章 语义缺陷
1.表达式求值顺序
a < b && c < d
&&有短路原则,即a<b为真的话,c<d无需计算
&&和||最先对左边的操作数进行求值,而右
边的操作数只有在需要的时候才进行求值
2.C 并不总是转换实参
C 中有两个简单的规则控制着函数参数的转换:
(1)比 int 短的整型被转换为 int;
(2)比 double短的浮点类型被转换为 double。
待续
第五章 库函数
1.返回整数的getchar函数
#include<stdio.h>
main()
{
char c;
while((c = getchar()) != EOF)
putchar(c);
return 0;
}
这段程序看起来好像要讲标准输入复制到标准输出。实际上,它并不完全会做这些。
原因是 c 被声明为字符而不是整数。这意味着它将不能接收可能出现的所有字符包括 EOF。
因此这里有三种可能性。
- 某些合法的输入字符在被“截断后”使得c的取值与EOF相同
- c根本不可能取到EOF这个值,将会陷入死循环
- 还可能运行正常,但是完全是巧合。编译器在比较表达式中并不是比较c和EOF,而是比较getchar函数的返回值与EOF
第六章 预处理器
1.宏不是函数
- 不能忽视宏定义中的空格
- 宏并不是函数,最好将宏定义中的每个参数都用括号括起来
- 宏并不是语句,宏定义后不能加分号
- 宏并不是类型定义