《C陷阱和缺陷》读书笔记(1)- 词法”陷阱“
为了敦促自己读书,特地开了一个读书笔记系列。
自上大学学习了谭浩强版本的《C程序设计》以来,以及经过这么多年遭受的C/C++面试和工作的毒打,就有一个感觉,C/C++语言的设计处处充满了陷阱。编译器设计之复杂,条目细则之多,感觉远远超出了其他语言。掌握了越多的这些”细则“或者”陷阱“,就离大师就更近了一步。而这些大师们,也津津乐道于讲述自己所掌握的这些”陷阱”以彰显自己大师之风范,以在面试中能够用这些“陷阱”困住面试者为骄傲!难怪人们常说,“细节决定成败”!而程序员最需要掌握的更加是一些编程语言、编译器的细节。
You are the master of the C language!
–> 其实是个双关语! 哈哈 :)
幸好有了 Andrew Koenig
的 《C Traps and Pitfalls》
,带领我们一步步揭开 C 语言的面纱,避开一个个诡异的陷阱,走向 C 语言专家,成为 C语言大师!恨自己早些年没有读这本书!
目录
- 词法陷阱
- 语法陷阱
- 语义陷阱
- 连接
- 库函数
- 预处理器
- 可移植性缺陷
概述
这本书主要是按照C语言编译链接过程顺序来排版。作者是鼎鼎大名的贝尔实验室的大牛。作者和贝尔实验室同时期的大部分鼎鼎有名的人都是同事,他们也都有帮忙协助修订本书。光我熟悉的人中就有 Dennis Ritchie(C语言设计者), Bjarne Strustrup(C++语言设计者),Rob Pike(Go语言设计者) Steve Bellovin(《Accelerated C++》和《C++ Primer》作者)等等,还有十几个名字我还没来得及去查。真的是牛人身边都是牛人!
这本书出的比较早,作者写本书的时候,ANSI C标准甚至还没有定稿,作者也是ANSI C的标准委员会成员,当然也就是制定者之一了。
一、词法陷阱
废话不多说,直接开始吧!
1.1 =不等于==
如果你问一个C语言新手在编程的时候最常犯的错误是什么,那么答案极有可能就是,
我在 if 条件里面判断的时候少写了一个等号!
|
|
是的!这也是我在C/C++编程的时候常常犯的错误!而且这种错误非常致命,因为编译器不会报告任何错误,甚至都不会有警告!这种错误 $\color{red}{非常难以排查}$ !
为此,许多IT公司还设计出了一个看起来不美观但是有效的办法,将操作数判断放到等号前面作为左值,这样编译器发现少了一个等号,就会报告错误!让这种错误无处遁形。
|
|
而许多其他语言也积极吸取了该教训,有的将赋值操作换一个操作符,=
只作为相等比较。有的则在if的判断表达式里面禁用等号赋值操作,只使用一个等号的直接报错!
1.2 &
和 |
不同于 &&
和 ||
其实是 赋值符号 和 相等 符号问题的延伸。作者认为这也是很容易犯的错误,容易漏写一个。但好像我们中国人不太容易犯这个错,至少我是很少的。
不过关于 &&
和 ||
我倒有一点想说,这两个符号都是 短路计算。 这也算是表达式副作用的体现,我觉得后面可能会有专门一章讲解表达式和函数的副作用。
a && b
,如果a
为假,则b
不再进行计算(如果有函数调用,则不会进行调用)。a || b
,如果a
为真,则b
不再进行计算。
1.3 词法分析中的“贪心”算法
说道贪心算法,“我要,我还要!”闪过脑海,想必大家都非常了解了! 说的简单一点就是,当从前往后识别的时候,遇到满足条件的就能够满足的就要的更多!举个栗子!
int *p;
int b = x/*p;
你们认为这段表达的意思是什么呢?我省去了语法高亮。我再把语法高亮打开,看看
|
|
贪心算法遇到 /
的时候,如果后面遇到了 *
则贪心地将这两个符号组合在一起,构成多行注释的前半部分。而不是 x
除以 *p
,而要表达这个意思,则需要写作 int b = x / *p;
或者 int b = x/(*p);
所以,一个思考题:a---b
表达的是 a-- -b
还是 a- --b
呢?
而 a+++++b
表示的是什么呢?
(答案参见附录)
1.4 整型常量
在 C语言程序中,整数表示有二进制、八进制、十进制和十六进制等多种表示方法。
0b01010101
010
1234
0xffff
格外需要注意 010
不是我们通常意义上的10,而是八进制的,表达的数字其实就是8。 我猜测,原本应该写为 0o10
,但怎么看一个字母小写o
在数字钟都显得比较奇怪,因为跟 0
比较接近,因此直接禁掉这种写法比较干脆。 但没有了这个小欧,有些人在 代码对齐 的时候就可能忽视掉这个0开头导致数字弄错。这个需要格外注意!
1.5 字符和字符串
在C语言中,单引号引起的字符跟这个字符代表的ASCII码数字完全等价! 比如, char a = 'a';
和 char a = 97;
是完全一样的。 'a' + 'd'
就完全等于 197
。
而 双引号引起的字符串,代表的是一个 字符数组常量的 指针。其实,也就是一个地址。
而如果这两者混用,通常情况下,会报告错误,但有些情况不会报错,就可能产生难以预料的错误(通常是导致程序崩溃),例如:
|
|
和
|
|
上面这个例子我们都知道,没啥问题,打印一个换行符。而下面这个例子则就迷惑多了! 掌握了这两个的区别,其实很简单,就是把 '\n'
(换行符) 这个字符ASCII值(10)当做了一个字符串指针地址传给了printf函数,所以打印出来的东西可能很混乱,也可能是崩溃,属于非法访问。
第一章就这样完成了!撒花✿✿ヽ(°▽°)ノ✿!
附录
- 1 表示
a-- -b
- 2 表示
a++ ++ +b
,会编译报错!
更多
[[c-traps-chapter-two]]