导言:

由于一个程序错误可以从不同层面采用不同方式进行考察,而根据程序错误与考察程序的方式之间的相关性,可以将程序错误进行划分为各种陷阱与缺陷:
①.词法“陷阱”
②.语法“陷阱”
③.语义“陷阱”
④.连接问题
⑤.库函数问题
⑥.预处理器问题
⑦.可移植性缺陷

我们知道程序是由符号序列所组成的。本篇主要考察在程序被词法分析器分解成各个符号的过程中可能出现的问题,也就是词法“陷阱”。

  • 导言:
  • 1.=不同于==
    • 1.1 案例1
    • 1.2 案例2
    • 1.3 启示
    • 1.4 案例3
  • 2.&和 |不同于&&和 ||
  • 3.词法分析中的“贪心法”
    • 3.1 读取规则---“贪心法”:
    • 3.2 注意:
    • 3.3 “贪心法”读取案例1
  • 4.整形常量
  • 5.字符和字符串
    • 5.1 字符
    • 5.2 字符串
  • 6.练习题
    • 6.1 练习题①
    • 6.2 练习题②

1.=不同于==

符号 =作为赋值运算符
符号==作为比较运算符
由于在C语言中赋值操作相对于比较出现更加频繁,所以将字符较少的符号=赋予更常用的含义—赋值操作。
不过这种便利的使用可能导致一个潜在的问题:当程序员本想使用比较运算符却使用了赋值运算符。

1.1 案例1


if(x=y)
break;

该句的本意是想检查x是否等于y;但实际上却是将y的值赋给了x,然后检查表达式结果是否为0。

1.2 案例2

再比如下面这个例子:
例子中循环语句本想跳过空格符,制表符,和换行号;

   while(i=' '||i=='\t'||i=='\n')i=getchar();

但是却把比较运算符误写成赋值运算符,因为赋值运算符=的优先级是低于逻辑运算符||,所以语句先将’ ‘赋给了i,然后发先i结果不为0(’ 'ASCII码值是32)根据短路求值原则,左边为真,则右边不再进行,此语句也就是真,因此循环将一值进行下去直到整个文件结束。或者变成一个死循环。

1.3 启示

某些C编译器在发现形如a=b的表达式出现在循环语句的条件判断部分时,会给出警告以提醒程序员。当确实需要对变量进行赋值并检查该变量的新值是否为0时,为了避免该类编译器的警告,我们应该进行显式地比较,比如

if(x=y)
{Function();
}

应该写成

if((x=y)!=0)
{Function();
}

这样的写法就使得代码的意图一目了然。

1.4 案例3

前面一直谈的是将比较运算符误写成赋值运算符,另一方面,如果把赋值运算符误写成比较运算符,也会造成混淆。

if ((i == fun(a, b)) < 0)error();

本语句的原本意思是如果fun函数执行成功则返回0,执行失败则返回-1或者正数,然后再将返回值赋给i,通过比较i的大小来确定是否fun函数执行成功。但实际上是fun函数的返回值与i进行比较,结果只有真与假也就是0或1,永远不可能小于0,所以函数error没有机会调用。也就不知道fun函数调用是否失败。

2.&和 |不同于&&和 ||

3.词法分析中的“贪心法”

C语言有许多符号,比如只有一个字符长的单字符符号,和多个字符长的多字字符。比如== 和 /*。
当C编译器读取符号时,是如何读取的呢?

比如读入一个字符’/‘后又跟了一个字符’ *',那编译器是将其作为两个分别的符号对待,还是合起来作为一个符号对待?

3.1 读取规则—“贪心法”:

每个符号应该包含尽可能多的字符。
也就是说,编译器将程序分解成符号的方法是:
从左到右一个字符一个字符地读入,如果该字符可能组成一个符号,那么再读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分;如果可能,继续读入下一个字符,重复上述判断,直到读入的字符组成字符串不再可能组成一个有有意义的符号。
这个处理策略就被称为“贪心法”。

3.2 注意:

需要注意的是,除了字符串与字符常量,符号的中间不能嵌有空白(空格符,制表符和换行符)。
比如:

a---b 与表达式
a-- -b的含义相同,但与
a- --b的含义完全不同。

3.3 “贪心法”读取案例1

本代码想表达的意思是,x先除以指针p指向的数,然后再将所得的商,赋给y.

y=x / *p;// *p指向的是被除数

而实际上,/*被编译器理解为一段注释的开始,编译器将不断的读入字符,直到另一个 * /出现为止。
也就是说该代码,直接将x的值赋给了y,根本不会顾及后面出现的p。

而正确的表达意思应该是这样:

y=x/(*p);//*p指的是被除数

4.整形常量

我们知道一个整形常量第一个字符是数字0,则这个常量代表的是八进制位数。
因此10与010的意思不一样

而有的C编译器会把8,9也作为八进制数字来处理,感觉很奇怪,所以ANSI C标准禁止了这种用法,以免混乱。
不过有时候,在上下文中为了格式对齐,可能会将十进制数写成八进制数。比如:

035
044
123//只是单词的使格式对齐没有多的意思,

5.字符和字符串

C语言中字符与字符串的区别在于单引号与双引号的使用,在某些情况下如果把两者弄混,编译器并不会监测报错,从而运行时产生难以预料的结果。我们先来看看字符与字符串的区别

5.1 字符

用单引号引起的一个字符实际上代表一个整数,整数对应着ASCII码值的字符序列值,比如’a‘的含义与0141(八进制)或者97(十进制)严格一致。

整数(一般为16位或32位)的存储空间可以容纳多个字符(一般位8位)因此有的C编译器允许在一个字符常量(以及字符串常量)中包含多个字符。也就是用‘yes’代替“yes”不会被该编译器监测到。“yes”代表的意思是依次包含‘y’,‘e’,‘s’以及空字符‘\0’的4个练习内存单元的首地址。而‘yes’的含义并没有准确的进行定义,但大多数编译器理解为“一个整数值”

5.2 字符串

用双引号引起的字符串,代表的是一个指向无名数组起始字符的指针,该数组被双引号之间的字符以及一个额外的二进制为0的字符’0\‘初始化。

#include <stdio.h>
int main()
{printf("xiao tao lai lo\n");char ch[100] = "xiao tao lai lo";//ch是数组名,数组名是首元素的地址,也就是首字符的地址。printf(ch);//ch里存放着首元素的地址}

这说明这两个打印效果是一样的,所以一个字符串代表着一个指向无名数组起始字符的指针。可以用一个指针来存放字符串

#include <stdio.h>
int main()
{char* p = "xiao tao lai lo";//指针p里面存放的就是字符串的首字符地址。printf(p);
}

6.练习题

6.1 练习题①


答案:
根据“贪心法”规则,在编译器读入>之前,就已经将- -作为一个符号,而后面的>不能和前面的组成一个符号。

6.2 练习题②

答案:
这个代码唯一有意义的解析是这样:

a++ + ++b

但是根据“贪心法”规则,上面代码应该被分解为

a++ ++ +b

这个式子从语法上来说是不对的,它应该写成

((a++)++)+b

但是,a++的结果不能作为左值,因此编译器不会接收a++作为后面的++运算符的操作数,该代码没有意义

……………………………………………………………………………………………………………………
………………………………………………………………………加油…………………………………………………………

《C陷阱与缺陷》----词法“陷阱”相关推荐

  1. 《C陷阱与缺陷》一第1章 词法“陷阱”1.1 =不同于==

    本节书摘来自异步社区<C陷阱与缺陷>一书中的第1章,第1.1节,作者 [美]Andrew Koenig,更多章节内容可以访问云栖社区"异步社区"公众号查看 第1章 词法 ...

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

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

  3. 《C陷阱与缺陷》第一章【词法“陷阱”】

    前言: 先在这里和关注我的小伙伴们说一声对不起,因为我已经连续三天没更新文章了.是因为学校的线上课程结束了,线下几乎每一天都是满课,写博客的时间少了很多,不过我会在五一假期期间尽量把之前的补回来. 这 ...

  4. C陷阱与缺陷(一)词法“陷阱”、语法“陷阱”

    第一章 :词法"陷阱" 术语"符号"指的是程序的一个基本组成单元(就像一个句子中的单词,无论在哪个句子中,表达意思都一样),而组成符号的字符序列就不同,同一组字 ...

  5. 《C陷阱与缺陷》词法陷阱-贪心法

    C语言的某些符号,例如/ .* .和=,只有一个字符长,称为单字符符号.而C语言中的其他符号,例如/ 和 = = ,以及标识符,包括了多个字符,称为多字符符号.当C编译器读入一个字符'/'后又跟了一个 ...

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

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

  7. c语言局限性,C语言陷阱与缺陷.pdf

    C 语言陷阱和缺陷[1] winxos 11-01-28 winxos 11-01-28 原著:Andrew Koenig - AT&T Bell Laboratories Murray Hi ...

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

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

  9. c语言 去掉双引号_技术分享|浅谈C语言陷阱和缺陷

    良好的软件架构.清晰的代码结构.掌握硬件.深入理解C语言是防错的要点,人的思维和经验积累对软件可靠性有很大影响.C语言诡异且有种种陷阱和缺陷,需要程序员多年历练才能达到较为完善的地步.软件的质量是由程 ...

最新文章

  1. execute taskaction$gradle怎么解决_Spring 源码中设计模式?怎么回答面试官才稳?
  2. adc采样时间_ADC采样原理
  3. 二十年编程语言风云,哪款是你的爱豆?
  4. ZoomBlur 聚焦模糊效果Shader(URP)
  5. python做数据可视化的代码_Python数据可视化正态分布简单分析及实现代码
  6. 记录一下(session共享的文章,wcf记录一下学习地址,Firebug)
  7. JavaScript高级之构造函数和原型
  8. linux系统编程 传智播客,传智播客王保明Linux培训系列教程全120集
  9. 桌面消息提醒_手机消息总是延迟,真的是网速不行?3招教你找出捣鬼设置
  10. script标签中defer和async的区别
  11. byte用json存 c++_.NET Core 3.1中的Json互操作最全解读收藏级
  12. Spring.Net的AOP的通知
  13. Dalvik源码阅读笔记(二)
  14. xml 标签带有号php,php操作xml入门之xml基本介绍及xml标签元素
  15. win7电脑便签怎么弄
  16. js调用数科阅读器_【JS】「直播回顾」Mars:加速数据科学的新方式
  17. VMware故障:配置文件(.vmx)损坏修复
  18. 我的世界 win10c语言版,Minecraft Win10版下载
  19. 如何用计算机将图片整成手绘画,【新手教程】如何将手绘作品转变成电子档,并让其更像“作品”?...
  20. FLutter web app 运行显示空白页

热门文章

  1. RocketMQ作业
  2. 富芮坤FR801xH使用SDK开发蓝牙GATT协议栈教程
  3. 解决win10中笔记本插入网线后无法识别的问题
  4. 全面启动“Say hi市南”中山路餐厅变身网红打卡店
  5. “杭州最惨创业者”事件初步法律分析
  6. M1/M2-MacBook 运行我的世界(MC)以及安装forge/fabric mod的简单教程
  7. 脚本语言之简单易懂的介绍
  8. 椭圆加密算法(sm2和sm4)
  9. MQTT,EMQX认识,安装,部署
  10. 购物网站的设计与开发