本节书摘来自异步社区《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 =不同于==相关推荐

  1. 《C陷阱与缺陷》----第三章 语义陷阱

    第三章. 语义陷阱 3.1 指针与数组 3.2 非数组的指针 3.3 作为参数的数组声明 3.4 空指针并非空字符串 3.5 边界计算与不对称边界 3.6 求值顺序 3.9 整数溢出 3.10 为函数 ...

  2. 《C陷阱与缺陷》第三章

    文章目录 前言: 语义"陷阱" 指针与数组 操作符:sizeof() 指针 非数组的指针 作为参数的数组声明 避免"举隅法" 空指针并非空字符串 边界计算与不对 ...

  3. 《C陷阱与缺陷》第三章阅读笔记

    语义"陷阱" 3.1 指针与数组 C语言中数组值得注意的地方有以下两点: 1.C语言中只有一维数组,而且数组的大小必须在编译期就作为一个常数确定下来.然而,C语言中数组的元素可以是 ...

  4. 《C陷阱与缺陷》——第三章(语义陷阱)

    文章目录 一.指针与数组 二.非数组指针 三.作为参数的数组声明 四.避免"举隅法" 五.空指针并非空字符串 六.边界计算与不对称边界 七.求值顺序 八.运算符&& ...

  5. 《C陷阱与缺陷》一导读

    前 言 C陷阱与缺陷 对于经验丰富的行家而言,得心应手的工具在初学时的困难程度往往要超过那些容易上手的工具.刚刚接触飞机驾驶的学员,初航时总是谨小慎微,只敢沿着海岸线来回飞行,等他们稍有经验就会明白这 ...

  6. 阅读《C陷阱与缺陷》的知识增量

    看完<C陷阱与缺陷>,忍不住要重新翻一下,记录一下与自己的惯性思维不符合的地方.记录的是知识的增量,是这几天的流量,而不是存量. 这本书是在ASCI C/C89订制之前写的,有些地方有疏漏 ...

  7. 《C陷阱与缺陷》学习笔记

    第一章 词法陷阱 笔记本:<C陷阱与缺陷> 创建时间:2018/4/23 22:06:21                                                  ...

  8. 【2018深信服 醒狮计划】《C陷阱与缺陷》学习笔记

    2018深信服"醒狮计划"笔记 先自我介绍一下,湖大研一计算机的菜鸡,本科网络工程的,大学里不务正业一直在做应用,大一自学过一段时间的MFC,Windows网络编程,感觉比控制台好 ...

  9. C陷阱与缺陷之词法陷阱

    该文章及后续文章均为阅读<C陷阱和缺陷>后的读数笔记,方便以后回顾 C陷阱和缺陷电子版图书下载地址:点击打开链接 第一章词法陷阱 1.1 = 不同于 == 在C语言中,符号=作为赋值运算符 ...

最新文章

  1. 新冠图像数据分析论文集合(附链接)
  2. 算法基础课-动态规划-区间dp-AcWing 282. 石子合并:区间dp
  3. 干货 | Elasticsearch7.X Scripting脚本使用详解
  4. linux 中将文件设置密码,linux – 如何使用公钥在openssl中加密大文件
  5. 作者:王瑞(1987-),女,中国科学院软件研究所工程师
  6. [转帖]onInterceptTouchEvent和onTouchEvent调用时序
  7. spring源码解析百度网盘下载
  8. cad快速选择命令快捷键_CAD人必知的6大CAD操作命令及快捷键
  9. 星光不问赶路人,时光不负奇舞团
  10. 图灵机和通用计算机,数学的不完美之美——阿兰?图灵与图灵机
  11. 通信信道带宽为1Gbit/s,端到端时延为10ms。TCP的发送窗口为65535字节。试问: 可能达到的最大吞吐量是多少?信道的利用率是多少?
  12. Spring之IOC自动装配解析
  13. Java开发中图片压缩工具Thumbnailator
  14. linux 虚拟光驱软件,在Linux操作系统下使用虚拟光驱的方法
  15. Apple FairPlay DRM及其工作原理
  16. ig信息增益 java_【Python 编程】实现文本分类中的信息增益算法
  17. 入职避坑指南(杭州)
  18. vue中的方法 methods 定义时不要使用箭头函数
  19. 常用RGB颜色值对照表
  20. branch什么意思中文翻译_这么污的鸡尾酒名字,到底是什么鬼

热门文章

  1. Python3bytes转16进制字符(例如:b111111转\x8c\x8c\x8c\x8c\x8c\x8c)
  2. 相机标定中标定棋盘的角点是哪个?
  3. JDBC初学者的basedao工具类
  4. install maven3 on ubuntu
  5. C/C++开发工具大比拼【转】
  6. 戳破“砖家”假面:唯快不破的时代,为什么这件事一定要慢慢做?
  7. python多线程框架_Python爬虫第七天:多线程爬虫|Scrapy框架
  8. 这个没去大厂的程序猿,用 4 年时间证明自己做对了!
  9. 一个IT时代的终结:109岁的IBM将分拆为两家公司
  10. 领域驱动设计(DDD):领域和子域