第二章 语法陷阱

  • 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.1 错例1
      • 2.2.1.2 错例2
      • 2.2.1.3 错例3
      • 2.2.1.4 错例4
      • 2.2.1.5 错例5
    • 2.2.2 运算符的结合性理解
    • 2.2.3 关系运算符的优先级
    • 2.2.4 自右向左的优先级顺序的汇总
  • 2.3 注意作为语句结束标志的分号
    • 2.3.1 加上分号
    • 2.3.2 遗漏分号
  • 2.4switch语句
  • 2.5 函数列表

2.1 理解函数声明

2.1.1 如何理解函数声明

首先来看一个语句:

(*(void(*)())0)();

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

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

  1. 简单变量声明:

    float f;
    //含义:当对f进行求值时,表达式f的类型为浮点数类型。因为声明符与表达式类似,所以也可以在声明符中任意使用括号:
    float ((f));
    //含义:((f))的类型是浮点类型,由此可以推知,f也是浮点类型。
    
  2. 函数和指针类型的声明

    float ff();
    //含义:表达式ff()的求值结果是一个浮点数,也就是说,ff是一个返回值为浮点类型的函数。
    float *pf;
    //含义:*pf是一个浮点数,也就是说,pf是一个指向浮点数的指针。
    

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

注意:下面有一个函数指针:

void (*fp)();

因为fp是一个函数指针,那么*fp就是该指针所指向的函数,所以(*fp)()就是调用该函数的方式。ANSI C标准允许程序员将上式简写为fp()。但是一定要记住这种写法只是一种简写形式。

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

2.1.2 举例理解声明

2.1.2.1 例子1

(*(void(*)())0)();
//将0进行强制类型转换为(void (*)()),即函数指针类型,然后对其进行解引用,解引用之后就找到了那个函数,然后再进行函数调用
//此处就是把0当作是一个地址,本质上就是一个函数的调用

2.1.2.2 例子2

void (*signal(int,void(*)(int)))(int);
//首先signal先和圆括号先结合,形成一个函数,这个函数有两个参数,参数1是一个整数,参数2是一个函数指针,该函数指针指向的函数的参数为int类型,返回类型为void类型,signal函数的返回类型为void(*)(int),即一个函数指针类型,该函数指针指向的函数参数类型为int,返回类型为void。

2.2 运算符的优先级

运算符优先级顺序汇总

2.2.1 常见错例

2.2.1.1 错例1

if(flags & FLAG!=0 )

我们想用表达式flags和FLAG&的结果来作为if条件语句的判断条件,但是由于优先级的问题,上面的表达式却是向下面这样进行结合的,表达的意思却与我们相悖:

if(flags & (FLAG!=0))

2.2.1.2 错例2

r = h<<4 + low;//r的值等于h左移4位后的值与变量low的和

但是实际上却是这样进行结合的:

r = hi<< (4 + low);

表达的意思与我们想要表达的意思相悖。

2.2.1.3 错例3

while(c = getc(in)!=EOF)putc(c,out);

在上面的语句中,我们是想复制一个文件到另一个文件中,但是实际上却是这样进行结合的:

while(c = (getc(in)!=EOF))

表达的意思并不能达到我们想要达到的目的。此处函数getc(in)的返回值只是一个临时变量,在与EOF比较后就被丢弃了。因此,最后得到的文件副本中只包括了一组二进制为1的字节流。

2.2.1.4 错例4

if((t=BTYPE(pt1->aty)==STRTY) || t==UNIONTY)

这行代码的本意是首先赋值给t,然后判断t是否等于STRTY或者UNIONTY。

但是实际上的结合却是这样的:

if((((t=BTYPE(pt1->aty))==STRTY) || t)==UNIONTY)

实际的结果却大相径庭:根据BTYPE(pt1->aty)的值是否等于STRTY,t的取值或者为1或者为0:如果t的取值为0,还能进一步与UNIONTY比较。

2.2.1.5 错例5

while(c == '\t' || c = ' '|| c == '\n')c = getc(f);

这个例子是非法的。因为赋值运算赋的优先级比while子句中其它运算符的优先级都要低,因此上例可以解释:

while((c == '\t' || c) = (' '|| c == '\n'))

当然,这是非法的,因为(c == '\t' || c)是不能出现在赋值运算符的左侧的。

2.2.2 运算符的结合性理解

*p++;
//上面的代码应该像下面这样进行理解:
*(p++);//++和*都是单目运算符,具有相同的优先级,但是结合性是自右向左的

2.2.3 关系运算符的优先级

6个关系运算符的优先级并不相同,>、>=、<、<=的优先级高于==、!=两个运算符的优先级,这也是我们常常会像下面这样写的原因。

if(a < b == c < d)
//上面代码等价于
if((a < b) == (c < d))

2.2.4 自右向左的优先级顺序的汇总

单目运算符、三目运算符、赋值运算符

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

2.3.1 加上分号

注意:在if或者while字句之后不要加分号,带上上分号之后往往会造成无法估量的后果造成代码错误且常常难以发现。

例如:

if(x[i] > big);big = x[i];

上述代码实际相当于:

if(x[i] > big) {}big = x[i];

即无论条件是否成立,都将执行big = x[i]语句。

2.3.2 遗漏分号

注意:遗漏分号也常常会造成无法估量的后果。

例如:

if(n<3)return
logrec.date = x[0];

上面的代码与下面的代码是等价的:

if(n<3)return logrec.date = x[0];

此时来看当n<3条件满足的时候也许还不会发生什么太大的问题,但是如果条件n<3不满足的时候,就会跳过logrec.date = x[0]这条语句。这就会与我们想表达的意思相悖。

有下面一段代码:

struct logrec
{int date;int time;int code;
}//分号被省略
main()
{···
}

如果分号没有被省略,函数main()的返回值类型为int类型(编译器默认的),省略后返回类型为struct类型。

2.4switch语句

注意:C语言中的case语句后面如果我们不想继续执行下一个case语句,就必须在case语句的末尾加上break来结束当前case语句。

2.5 函数列表

==C语言要求:在函数调用时,即使函数不带参数,也应该包括参数列表。==因此,如果f是一个函数,那么

f();

是一个函数调用语句,而

f;

却是一个什么也不做的语句。这个语句计算函数f的地址,却并不调用该函数。

《C陷阱与缺陷》----第二章 语法陷阱相关推荐

  1. C陷阱与缺陷代码分析之第2章语法陷阱

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 陷阱1 理解函数声明 作者提出一个问题:有一个首地址为0的函数,该函数返回值类型为void,没有参数.怎样用C语言的 ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

最新文章

  1. Spring mvc HTTP协议之缓存机制
  2. 大数据WEB阶段 Js常用的页面操作
  3. PHP中__get()和__set()的用法实例详
  4. HTTPS反向代理嗅探
  5. 手机充电全部用完充好还是有空就充好?
  6. Linux nmon 命令
  7. 软件工程——软件计划
  8. Axure制作音乐App原型图
  9. Cosy V3.1.3 简洁大气WordPress博客主题自适应个人自媒体网站模板(含积木部分插件)
  10. 基于vue和nodejs毕业设计酒店预约管理系统
  11. 使用GPO(Profile-Guided Optimization)优化程序
  12. 事业单位采购计算机的申请报告,事业单位采购申请报告
  13. The malloc maleficarum之The House of Spirit漏洞
  14. vs工程中哪些文件可以删除
  15. Linux常用命令笔记
  16. C语言练习二 :找出一个二维数组的鞍点
  17. 无意间发现一个好用的视频转换gif图片的开源框架
  18. ABtest如何确定样本量?
  19. 简阅客户端应用Android源码
  20. 浅谈人工智能、大数据等技术在交通领域的应用

热门文章

  1. 世界上最大的监狱是人的大脑
  2. 某电批锁付一颗螺丝的原理分析
  3. MySQL||默认值约束(Default)
  4. 网易云信(验证码短信接口接入)
  5. Continuations
  6. 通知:后天大家一定要来哟~
  7. 【Blender2.82a】学习记录
  8. 抖音上很火的H53D立体动态相册效果
  9. 地址代码Arduino以太网插板教程
  10. 防止模型过拟合的方法汇总