《C陷阱与缺陷》一第1章 词法“陷阱”1.1 =不同于==
本节书摘来自异步社区《C陷阱与缺陷》一书中的第1章,第1.1节,作者 【美】Andrew Koenig,更多章节内容可以访问云栖社区“异步社区”公众号查看
第1章 词法“陷阱”
C陷阱与缺陷
当我们阅读一个句子时,我们并不去考虑组成这个句子的单词中单个字母的含义,而是把单词作为一个整体来理解。确实,字母本身并没有什么意义,我们总是将字母组成单词,然后给单词赋予一定的意义。
对于用C语言或其他语言编写的程序,道理也是一样的。程序中的单个字符孤立来看并没有什么意义,只有结合上下文才有意义。因此,在p->s = "->";这个语句中,两处出现的'-'字符的意义大相径庭。更精确地说,上式中出现的两个'-'字符分别是不同符号的组成部分:第一个'-'字符是符号->的组成部分,而第二个'-'字符是一个字符串的组成部分。此外,符号->的含义与组成该符号的字符'-'或字符'>'的含义也完全不同。
术语“符号”(token)指的是程序的一个基本组成单元,其作用相当于一个句子中的单词。从某种意义上说,一个单词无论出现在哪个句子,它代表的意思都是一样的,是一个表义的基本单元。与此类似,符号就是程序中的一个基本信息单元。而组成符号的字符序列就不同,同一组字符序列在某个上下文环境中属于一个符号,而在另一个上下文环境中可能属于完全不同的另一个符号。
译注:
如上面的字符'-'和字符'>'组成的字符序列->,在不同的上下文环境中,一个代表->运算符,一个代表字符串"->"。
编译器中负责将程序分解为一个一个符号的部分,一般称为“词法分析器”。
再看下面一个例子,语句:
if (x > big) big = x;
这个语句的第一个符号是C语言的关键字if,紧接着下一个符号是左括号,再下一个符号是标识符x,再下一个是大于号,再下一个是标识符big,依次类推。在C语言中,符号之间的空白(包括空格符、制表符或换行符)将被忽略,因此上面的语句还可以写成:
if
(
x
>
big
)
big
=
x
;
本章将探讨符号和组成符号的字符间的关系,以及有关符号含义的一些常见误解。
1.1 =不同于==
由Algol派生而来的大多数程序设计语言,例如Pascal和Ada,使用符号:=作为赋值运算符,符号=作为比较运算符。而C语言使用的是另一种表示法,符号=作为赋值运算,符号= =作为比较。一般而言,赋值运算相对于比较运算出现得更频繁,因此字符数较少的符号=就被赋予了更常用的含义——赋值操作。此外,在C语言中赋值符号被作为一种操作符对待,因而重复进行赋值操作(如a=b=c)可以很容易地书写,并且赋值操作还可以被嵌入到更大的表达式中。
这种使用上的便利性可能导致一个潜在的问题:当程序员本意是作比较运算时,却可能无意中误写成了赋值运算。比如下例,该语句本意似乎是要检查x是否等于y:
if (x = y) break;
而实际上是将y的值赋给了x,然后检查该值是否为零。再看下面一个例子,本例中循环语句的本意是跳过文件中的空格符、制表符和换行号:
while (c = ' ' || c == '\t' || c == '\n') c = getc (f);
由于程序员在比较字符' '和变量c时,误将比较运算符= =写成了赋值运算符=。因为赋值运算符=的优先级要低于逻辑运算符 || ,因此实际上是将以下表达式的值赋给了c:
' ' || c == '\t' || c == '\n'
因为 ' ' 不等于零(' ' 的ASCII码值为32),那么无论变量c此前为何值,上述表达式求值的结果都是1,因此循环将一直进行下去直到整个文件结束。文件结束之后循环是否还会进行下去,这取决于getc库函数的具体实现,在文件指针到达文件结尾之后是否还允许继续读取字符。如果允许继续读取字符,那么循环将一直进行,从而成为一个死循环。
某些C编译器在发现形如e1 = e2的表达式出现在循环语句的条件判断部分时,会给出警告消息以提醒程序员。当确实需要对变量进行赋值并检查该变量的新值是否为0时,为了避免来自该类编译器的警告,我们不应该简单关闭警告选项,而应该显式地进行比较。也就是说,下例
if (x = y) foo();
应该写作:if ((x = y) != 0) foo();
这种写法也使得代码的意图一目了然。至于为什么要用括号把x = y括起来,本书的2.2节将讨论这个问题。
前面一直谈的是把比较运算误写成赋值运算的情形,另一方面,如果把赋值运算误写成比较运算,同样会造成混淆:
if ((filedesc == open(argv[i], 0)) < 0)error();
在本例中,如果函数open执行成功,将返回0或者正数;而如果函数open执行失败,将返回-1。上面这段代码的本意是将函数open的返回值存储在变量filedesc之中,然后通过比较变量filedesc是否小于0来检查函数open是否执行成功。但是,此处的= =本应是=。而按照上面代码中的写法,实际进行的操作是比较函数open的返回值与变量filedesc,然后检查比较的结果是否小于0。因为比较运算符= =的结果只可能是0或1,永远不可能小于0,所以函数error()将没有机会被调用。如果代码被执行,似乎一切正常,除了变量filedesc的值不再是函数open的返回值(事实上,甚至完全与函数open无关)。某些编译器在遇到这种情况时,会警告与0比较无效。但是,作为程序员不能指望靠编译器来提醒,毕竟警告消息可以被忽略,而且并不是所有编译器都具备这样的功能。
《C陷阱与缺陷》一第1章 词法“陷阱”1.1 =不同于==相关推荐
- 《C陷阱与缺陷》----第三章 语义陷阱
第三章. 语义陷阱 3.1 指针与数组 3.2 非数组的指针 3.3 作为参数的数组声明 3.4 空指针并非空字符串 3.5 边界计算与不对称边界 3.6 求值顺序 3.9 整数溢出 3.10 为函数 ...
- 《C陷阱与缺陷》第三章
文章目录 前言: 语义"陷阱" 指针与数组 操作符:sizeof() 指针 非数组的指针 作为参数的数组声明 避免"举隅法" 空指针并非空字符串 边界计算与不对 ...
- 《C陷阱与缺陷》第三章阅读笔记
语义"陷阱" 3.1 指针与数组 C语言中数组值得注意的地方有以下两点: 1.C语言中只有一维数组,而且数组的大小必须在编译期就作为一个常数确定下来.然而,C语言中数组的元素可以是 ...
- 《C陷阱与缺陷》——第三章(语义陷阱)
文章目录 一.指针与数组 二.非数组指针 三.作为参数的数组声明 四.避免"举隅法" 五.空指针并非空字符串 六.边界计算与不对称边界 七.求值顺序 八.运算符&& ...
- 《C陷阱与缺陷》一导读
前 言 C陷阱与缺陷 对于经验丰富的行家而言,得心应手的工具在初学时的困难程度往往要超过那些容易上手的工具.刚刚接触飞机驾驶的学员,初航时总是谨小慎微,只敢沿着海岸线来回飞行,等他们稍有经验就会明白这 ...
- 阅读《C陷阱与缺陷》的知识增量
看完<C陷阱与缺陷>,忍不住要重新翻一下,记录一下与自己的惯性思维不符合的地方.记录的是知识的增量,是这几天的流量,而不是存量. 这本书是在ASCI C/C89订制之前写的,有些地方有疏漏 ...
- 《C陷阱与缺陷》学习笔记
第一章 词法陷阱 笔记本:<C陷阱与缺陷> 创建时间:2018/4/23 22:06:21 ...
- 【2018深信服 醒狮计划】《C陷阱与缺陷》学习笔记
2018深信服"醒狮计划"笔记 先自我介绍一下,湖大研一计算机的菜鸡,本科网络工程的,大学里不务正业一直在做应用,大一自学过一段时间的MFC,Windows网络编程,感觉比控制台好 ...
- C陷阱与缺陷之词法陷阱
该文章及后续文章均为阅读<C陷阱和缺陷>后的读数笔记,方便以后回顾 C陷阱和缺陷电子版图书下载地址:点击打开链接 第一章词法陷阱 1.1 = 不同于 == 在C语言中,符号=作为赋值运算符 ...
最新文章
- [十九]JavaIO之PipedReader 和 PipedWriter
- 经验共享:由备份和负载均衡
- python inspect模块
- 我司那产品经理丨第三期
- Golang——数据类型使用细节详解
- 如何在VS2013中进行Boost单元测试
- 2 万字长文盘点五种负载均衡策略
- 201521123079《java程序设计》第8周学习总结
- mysql索引红黑联盟_MySQL的索引
- 想要与北上争雄,深圳还有哪些课要补?| DT城数
- Flutter系列之改变CupertinoSwitch的大小
- WebService CXF-RS技术之@Pathparam与@Queryparam注解区别
- 红帽子linux编译命令,Linux REDHAT下安装内核源代码详细步骤教程
- 最新在线客服系统php代码微信软件公众号小程序app二维码聊天网站源码
- Java_socket通信基础补充
- MATLAB中的bsxfun函数
- matlab 显示高光谱,高光谱图像显示问题
- 公开招标,邀请招标,竞争性谈判,询价采购之间的区别
- 《HyVulDect: A hybrid semantic vulnerability mining system based ongraph neural network》阅读笔记
- 怎样阅读论文(台湾彭明辉)