《C陷阱与缺陷》----第二章 语法陷阱_if (((t = btype(pt1->aty) == strty) t == uniont

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

((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 运算符的优先级


运算符优先级顺序汇总


![image-20220302211047449](https://img-blog.csdnimg.cn/img_convert/844292aabe52ff88b3ee94cf030003ea.png)


![image-20220302211107657](https://img-blog.csdnimg.cn/img_convert/574e48064682b8ffe2f33665d5b323a9.png)


![image-20220302211134778](https://img-blog.csdnimg.cn/img_convert/db718ec3ceb3a1a815e012215146c564.png)


#### 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) || tUNIONTY)


这行代码的本意是首先赋值给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 加上分号


![img](https://img-blog.csdnimg.cn/img_convert/ecbce87ee70ddcc9ebc069547a23f77e.png)
![img](https://img-blog.csdnimg.cn/img_convert/f2efc9f34ac04ceffc668a46fc673f0a.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.youkuaiyun.com/topics/618668825)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

-1715847092873)]

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.youkuaiyun.com/topics/618668825)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值