第一章 :词法“陷阱”

术语“符号”指的是程序的一个基本组成单元(就像一个句子中的单词,无论在哪个句子中,表达意思都一样),而组成符号的字符序列就不同,同一组字符序列在某个上下文环境中属于一个符号,而在另一个上下文环境中可能属于完全不同的另一个符号。

编译器中负责将程序分解为一个一个符号的部分,一般称为“词法分析器”。

在C语言中,符号之间的空白(包括空格符、制表符和换行符)将被忽略。如下:

if(x > big) big = x;//变成下面

if

(

x

>

Big

….

1.1= 不同于 ==

建议:在写比较时,将常量放在左边,当不小心将==写成=时,编译器会报错。

1.2按位运算符&和|不同于逻辑运算符 && 和 ||

1.3词法分析中的“贪心法”

(1)单字符符号:/、*、和 =

(2)多字符符号:/*、==

(3)编译器需要作出判断是多字符符号还是单字符符号,遵循策略:贪心法、大嘴法。尽可能多的读入字符,直到不能组成一个有意义的符号。

(4)注意:除了字符串与字符常量,符号的中间不能嵌有空白(空格符、制表符和换行符)

1.4整形常量

(1)整型常量的第一个字符是数字0,那么该常量被视为八进制数。因此10和010完全不同。

(2)有时候为了上下文格式对齐的需要,可能会产生误判。

046

047

125

1.5字符与字符串

(1)单引号引起的一个字符实际上代表一个整数,整数值对应于该字符在编译器采用的字符集中的序列值。

(2)有很多编译器采用ASCII字符集。

(3)双引号引起的字符串,代表的是指向无名数组起始字符的指针。

(4)少数的编译器:整型数(一般为16位或32位)的存储空间可以容纳多个字符(一般为8位),因此有的C编译器允许在一个字符常量(以及字符串常量)中包括多个字符。(‘yes’代替“yes”不会被编译器检测到,‘yes’按照各自所代表的整数值组合得到)

1.6练习

练习1-1 某些C编译器允许嵌套注释。请写一个测试程序,要求:无论是对允许嵌套注释的编译器,还是对不允许嵌套注释的编译器,该程序都能正常通过编译(无错误消息出现),但是这两种情况下程序执行的结果却不相同。

提示:被双括号括起的字符串中,注释符/*属于字符串的--部分,而在注释中出现的双引号”"又展于注释的一部分。

/*/*/0*/**/1

这个解法主要利用了编译器作词法分析时的“大嘴法”规则。

练习1-2 如果由你来实现一个C编译器,你是否会允许嵌套注释?如果你使用的C编译器允许嵌套注释,你会用到编译器的这一特性吗?你对第二个问题的回答是否会影响到你对第一个问题的回答?

C语言定义并不允许嵌套注释。

练习1-3 为什么n-->0的含义是n-- >0,而不是n-->0?

根据“大嘴法”规则,在编译器读入>之前,就已经将--作为单个符号了。

练习1-4 a+++++b的含义是什么?

a   ++   +   ++   b

第二章 :语法“陷阱”

2.1 理解函数声明

(1)当计算机启动时,硬件将调用首地址为0位置的子例程。为了模拟开机启动时的情形,设计出一个C语句,以显式调用该子例程。(*(void(*)( ))0)( ) ;

(2)构造这类表达式只有一条简单的规则:按照使用的方式来声明。

(3)任何C变量的声明都由两部分组成:类型以及一组类似表达式的声明符(declarator)。声明符从表面上看与表达式有些类似,对它求值应该返回一个声明中给定类型的结果。

float *pf ;

这个声明的含义是*pf是一个浮点数,也就是说,pf是一个指向浮点数的指针。

float *g ( ) , (*h) ( );

表示*g()与(*h)()是浮点表达式。因为()结合优先级高于*,*g()也就是*(g());g是一个函数,该函数的返回值类型为指向浮点数的指针。同理,可以得出h是一个函数指针,h所指向函数的返回值为浮点类型。

(4)类型转换符:只需要把声明中的变量名和声明末尾的分号去掉,再将剩余的部分用一个括号整个“封装”起来即可。

例如:

​​​​​​​

(5)假定变量fp是一个函数指针,那么如何调用fp所指向的函数呢?

(*fp)( ); 等于 fp( );

因为fp是一个函数指针,那么*fp就是该指针所指向的函数,所以(*fp)( )就是调用该函数的方式ANSI C标准允许程序员将上式简写为fp()

在表达式(*fp)( )中,*fp两侧的括号非常重要,因为函数运算符()的优先级高于单目运算符*。如果*fp两侧没有括号,那么*fp( )实际上与*(fp( ))的含义完全一致,ANSI C把它作为*((*fp)( ))的简写形式。

(6)对一个常数进行类型转换,将其转型为该变量的类型:只需要在变量声明中将变量名去掉即可。(比如将一份空内存,将数据强制转化为自定义类型的格式,我们也是将其变量声明时,把变量名去掉。)

​​​​​​​

(void (* )() ) 0 将0强制转换为(void (* )() )类型的变量(常量)。

(*  ( void ( * )() ) 0 )  () ; 传入合适的函数变量(常量)。

(7)使用typedef 能够使表述更加清晰:

typedef void  (*funcptr)();

( *( funcptr)0 ) () ;

(8)C 库函数 void (*signal(int sig, void (*func)(int)))(int) 设置一个函数来处理信号,即带有 sig 参数的信号处理程序。

typedef void (*HANDLER) (int ) ;

HANDLER signal (int,HANDLER);

2.2 运算符的优先级问题

(1)优先级最高者其实并不是真正意义上的运算符,包括:数组下标、函数调用操作符各结构成员选择操作符。它们都是自左于右结合,因此 a.b.c 的含义是(a.b).c,而不是a.(b.c)。

(2)单目运算符的优先级仅次于前述运算符。单目运算符是自右至左结合,因此*p++会被编译器解释成*(p++),即取指针p所指向的对象,然后将p递增1:而不是(*p)++,即取指针p所指向的对象,然后将该对象递增1。

(3)接下来就是双目运算符。在双目运算符中,算术运算符的优先级最高,移位运算符次之,关系运算符再次之,接着是逻辑运算符赋值运算符,最后是三目运算符

(4)两个重点

a.任何一个逻辑运算符的优先级低于任何一个关系运算符。

b.移位运算符的优先级比算术运算符要低,但是比关系运算符要高。

(5)任何两个逻辑运算符都具有不同的优先级。所有的按位运算符优先级要比顺序运算符的优先级高每个“与”运算符要比相应的“或”运算符优先级高,而按位异或运算符(^运算符)的优先级介于按位与运算符和按位或运算符之间

(6)三目条件运算符优先级最低。

这就允许我们在三目条件运算符的条件表达式中包括关系运算符的逻辑组合,例如:

tax_rate = income>40000 && residency<5 ? 3.5:2.0;

(7)上例其实还揭示了:赋值运算符的优先级低于条件运算符的优先级是有意义。此外,所有的赋值运算符的优先级是一样的,结合方式是从右到左

(8)在所有的运算符中,逗号运算符的优先级最低。

2.3 注意作为语句结束标志的分号

(1)多写了一个分号可能不会造成什么不良后果;

(2)也有可能造成的错误可会是一个潜伏很深、极难发现的程序Bug。

2.4 switch语句

(1)程序员很容易就会遗漏各个case部分的break 语句。

2.5 函数调用

(1)C语言要求:在函数调用时即使函数不带参数,也应该包括参数列表。

2.6 “悬挂”else引发的问题

(1)原因在于C语言中有这样的规则,else始终与同一对括号内最近的未匹配的 if结合。

2.7 练习

练习2-1   C语言允许初始化列表中出现多余的逗号,例如:int days [ ] = {31,28,31,30,31,30,31,31,30,31,30,31,} ;  为什么这种特性是有用的?

我们可以把上例的缩排格式稍作改动如下:

int days [] = {

31,28,31,30,31,30,

31,31,30,31,30,31,

} ;

现在我们可以很容易看出,初始化列表的每一行都是以逗号结尾的。正因为每一行在语法上的这种相似性,自动化的程序设计工具(例如,代码编辑器等)才能够更方便地处理很大的初始化列表。

练习2-2       本章的第3节指出了在C语言中以分号作为语句结束的标志而带来的一些问题。一个代码行的含义要受到其后续代码行的影响,这一点多少显得有些“怪异”。因此,某些程序语言改为在第n行代码中使用某种指示标志,以表示第n+1行代码应该被当作同一个语句的一部分。例如,Unix系统的Shell (如bash、ksh、csh等)在代码行的结尾使用字符\来作为指示标志,表示下一个代码行是同一个语句的一部分。C语言在预处理器中以及字符串内部,沿用了Unix系统中的这一惯例。

C陷阱与缺陷(一)词法“陷阱”、语法“陷阱”相关推荐

  1. 《C陷阱与缺陷》----第二章 语法陷阱

    第二章 语法陷阱 2.1 理解函数声明 2.1.1 如何理解函数声明 2.1.2 举例理解声明 2.1.2.1 例子1 2.1.2.2 例子2 2.2 运算符的优先级 2.2.1 常见错例 2.2.1 ...

  2. C陷阱与缺陷 第2章 语法“陷阱” 2.6 “悬挂”else引发的问题

    "悬挂"else引发的问题     if (x == 0)      if (y == 0) error();     else {         z = x + y;     ...

  3. 《C陷阱与缺陷》----词法“陷阱”

    导言: 由于一个程序错误可以从不同层面采用不同方式进行考察,而根据程序错误与考察程序的方式之间的相关性,可以将程序错误进行划分为各种陷阱与缺陷: ①.词法"陷阱" ②.语法&quo ...

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

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

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

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

  6. C陷阱与缺陷 第3章 语义“陷阱” 3.4 避免“举偶法”

    避免"举偶法"     "举偶法"(synecdoche)是一种文学修辞上的手段,有点类似于以微笑表示喜悦.赞许之情,或以隐喻表示指代物与被指代物的相互关系.在 ...

  7. C陷阱与缺陷--读书笔记3 语义“陷阱”

    第三章 一.知识点 1.C语言中的数组值得注意的地方有以下两点:(P41) (1).C语言中只有一维数组,而且数组的大小必须在编译期就作为一个常数确定下来.然而,C语言中数组的元素可以是任何类型的对象 ...

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

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

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

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

最新文章

  1. Android图片轮播
  2. MySql error 1010 无法删除数据库
  3. python最佳实践笔记
  4. 如果__name__ =='__main__':在Python中怎么办?
  5. freecodecamp_freeCodeCamp的服务器到底发生了什么?
  6. 【联盛德W806上手笔记】七、I2C
  7. 窗口闪退_今天只讲一件事,精雕软件的BUG(进浮雕闪退,笔刷变圈圈等)
  8. 天津消协警示“58同城” 请珍视消费者的信任和选择
  9. AcWing 839. 模拟堆
  10. 初始MySQL数据库
  11. 《动手学深度学习》(PyTorch版)代码注释 - 3 【Softmaxs_regression_with_zero】
  12. monitorServer IBM Tivoli Enterprise Monitor Server
  13. 同一局域网下,一台电脑连接另一台电脑的虚拟机(从属机(window)连接主机(window)虚拟机(Linux)的连接流程)
  14. JS调用拨打电话功能
  15. 开源免费录屏和直播软件OBS Studio教程(01)
  16. 【Python】多图形混合排版,如何在Matplotlib/Seaborn中实现?
  17. 【雕爷学编程】Arduino动手做(67)---BMP180气压传感器
  18. 甲骨文收购mysql,甲骨文提出十大保证 承诺收购Sun后会善待MySQL
  19. 如何在阿里云(centos7)上面搭建fastdfs服务器(搭建篇)--保姆级超级详细
  20. AutoAugment 学习

热门文章

  1. java计算机毕业设计旅游服务平台源代码+数据库+系统+lw文档
  2. 使用redis做消息队列mq的总结
  3. Python3《零基础小白从入门到实战》之“pytest测试框架之测试夹具(Fixture) ”
  4. 不用甘特图,你做什么项目管理
  5. 研发出了生产事故,到底要罚钱不?
  6. AutoJs学习-天猫养猫活动
  7. 汉源高科千兆20光8电工业以太网交换机20光8电千兆工业级环网交换机​28口千兆网管型二层机架式工业交换机
  8. apache common JCS的使用
  9. php教室预约系统,小教室预约系统
  10. php 汉王云名片_超弦云名片,让你彻底告别纸质名片!