# C和C++运算符规则记忆

[TOC]

# ✅1.C语言优先级和结合性记忆

# 1.1.前述

前述:以前以为,记忆C语言的优先级和结合性就像孔乙己记忆“回”字的四种写法一样有些没必要,对于技术,简洁即美。但以下场景,记忆C语言的优先级结合性还是有一定好处
场景

  • 记忆一些特定的优先级和结合性,能够帮助从编译器角度来快速识别多级指针和数组指针和函数指针判定指针类型。
  • 某些项目的遗留代码中有一些编程风格不好的片段,总是不写括号,遇到就需要查书,对研究别人代码,有一定影响。记忆优先级和结合性,能够降低开发中重构已有代码的难度。
  • 有的相关试题中,有考优先级的题目的存在。(考优先级不是目的,而是为了解决那些风格不好的遗留代码)
  • 由于编译器语法分析阶段的设计原理,其他语言的优先级也可以在此基础上记忆。
    • C语言的优先级,再添加上一些C++本身的运算符,则很容易转换到记忆C++的优先级
    • Java的运算符的优先级和C语言相似
优先级从高到低,分为15级

2020_5.28_1

2020_5.28_2

# 1.2.优先级记忆

  • 初-单-算-移-关 (出箪蒜一罐)
  • 位-逻-三-赋-逗 (味落三伏豆)

//箪—意指古代盛饭用的圆形竹器

记忆场景
每年三伏天农作时期,要为田间劳作者送饭,送饭者专门用箪盛着食物前往,还专门为其中一个很喜欢吃大蒜的劳作者带了一罐蒜解馋。后来,劳作者吃着发现,今年的三伏天的黄豆也格外美味。

解释:

  • 初:初级运算符
  • 单:单目运算符
  • 算:算数运算符(包含2个优先级)
  • 移:移位运算符
  • 关:关系运算符(包含2个优先级)
  • 位:位运算(除去,位运算中的单目运算符~ 包含3个优先级)
  • 逻:逻辑运算符(包含2个优先级)
  • 三:三目运算符
  • 赋:赋值运算符
  • 逗:逗号运算符

# 1.3.结合性记忆

单三赋 从右左

  • 除以上规则以外,还有一点。
  • 标准的C语言编译器对运算符的解析遵循“最大贪婪”规则。

# 1.4.“最大贪婪”规则

当C语言编译器对源程序进行词法分析时,如果运算符的下一个符号还是运算符,并且能和前一个运算符构成一个新的合法运算符时,编译器的词法分析器必须将其解析为两个运算符符号构成的新的运算符

举一个没太大实际意义的例子来辅证,例如: x=z+++y;
是按照x=z+(++y);来计算,还是按照x=(z++)+y;来计算呢?
根据C编译器的“最大贪婪”规则,是按照x=(z++)+y;来计算,而不是按照x=z+(++y)来计算 可以验证

int z=3,y=5;
int x;
x=z+++y;
printf("%d\n",x);
//输出结果为8,而不是9
//考点:C编译器的“最大贪婪”规则
1
2
3
4
5
6

# 1.4.用法举例

# (1)记忆第一优先级中的和第二优先级

int (*p)[3];    //这是一个数组指针,指向的类型是int (*)[3]
int *p[3];    //这是一个指针数组,里面的是int *类型的指针
int ** p;    //这是一个二级指针,指向的类型是int *
int (*p)(int ,int);  //这是一个函数指针,指向的类型是int (*)(int,int)
int *p(int ,int);    //这是一个返回值为指针的函数
1
2
3
4
5

# (2)比格基地笔试题

//以下程序的k最终值是:
int i = 10;
int j = 20;
int k = 3;
k *= i + j;
//答:90
//解释  =是赋值运算符 *=是赋值运算符  +是算数运算符 
//“出箪蒜一罐,味落三伏豆”知,算数运算符优先级高,先算i+j
//考点:编程语言的优先级
1
2
3
4
5
6
7
8
9

# (3)360公司笔试题

#include <stdio.h>
int main()
{ 
	int i, j, m=6,n=4,  *p=&n, *q=&m;
	//“出箪蒜一罐,味落三伏豆”知,关系运算符==的优先级比赋值运算符=高,知i=0
	i=p==&m;	
	//(-*p)中,-和*都是单目运算符,结合性是“单三赋,从右左”,所以,相当于(-n)
	j=(-*p)/(*q)+7;
	printf("i=%d,j=%d\n", i,j);
	return 0;
}
//输出为 i=0,j=7
//考点:编程语言的优先级,结合性
1
2
3
4
5
6
7
8
9
10
11
12
13

# 1.5.分析方法

1、借助“最大贪婪”规则进行“词法分析”
2、利用优先级进行分析词义

# ✅2.C++优先级和结合性记忆

# ✅3.纯C语言的典例训练

# 二、典型例题训练

# 例题2

a=(b=4)+(c=6) 是一个合法的赋值表达式。请问这句话的说法是正确的吗?
选项:A.正确	B.错误
答案:A
1
2
3

解析:

1、贪婪原则,知,经过词法解析后变为了

a = (b=4) + (c=6)
1

2、优先级和结合性,知,()up + up =

3、结合性,知,除了=之外,其他都是从左边到右边

平行宇宙第1步:(b=4)
平行宇宙第2步:(c=6)	
平行宇宙第3步:a=b+c,其中由于优先级和结合性,先算(b+c)
平行宇宙第4步:a=(b+c)	//赋值
1
2
3
4

# 例题3

以下程序的k最终值是:
int i = 10;
int j = 20;
int k = 3;
k *= i + j;
选项:A.90		B.50	C.70	D.30
答案:A
1
2
3
4
5
6
7

解析:

1、贪婪原则,知,经过词法解析后变为了

k *= i + j
1

2、优先级和结合性,知,+ up *=

平行宇宙第1步:(i+j)
平行宇宙第2步:k*=1步结果
1
2

# 例题4

已知int x=5;,执行下列语句后,x的值为(  )。
x+=x-=x*x;
选项:A.25	 B.40	C.40	D.20
答案:C
1
2
3
4

解析:

1、贪婪原则,知,经过词法解析后变为了

k += x -= x * x
1

2、优先级和结合性,知,* up +=和-=,然后结合性,-=和+=是从右到左

平行宇宙第1步:(x*x)
平行宇宙第2步:x-=(1步结果)
平行宇宙第3步:k+=(第2步结果)
1
2
3

# 例题5

设以下变量均为int类型,表达式的值不为9的是()
A.(x=y=8,x+y,x+1)
B.(x=y=8,x+y,y+1)
C.(x=8,x+1,y=8,x+y)
D.(y=8,y+1,x=y,x+1)
答案:C
1
2
3
4
5
6

解析:

核心,优先级

()是第高

是最低

# 例题6

宏的复合题型

#include<iostream.h>
#define SUB(X,Y) (X)*Y
int main()
{
    int a=3,b=4;
    cout<<SUB(a++,++b);
    return 0;
}

答案:输出15
1
2
3
4
5
6
7
8
9
10

解析:

1、预处理

cout<<(a++)*++b;
1

2、3步和2注意

(a++) * ++b
平行宇宙第1步:(a++)
平行宇宙第2步:(++b)
平行宇宙第3步:第1步结果*2步结果
1
2
3
4

# 例题7

写出表达式b!=3&&5/a>a+b的结果,设a=3,b=4。
答案是:false
1
2

解析:

优先级知道,算术up关系up逻辑up赋值

b != 3 && 5 / a > a + b

    b != 3 && 5 / a > a + b
    (4)(5)(1)(3) (2
1
2
3
4

# 补充(博客遗漏之处):

关系运算符中优先级:>和<和>=和<=比 != 优先级高

# 三、题库

# 例1

int a=5,b=4,c=3,d;
d=(a>b>c);
printf("%d\n",d);
答案:0
1
2
3
4

解析:

d = ( a > b > c )

>的结合性是从左边到右边

# 例2

若整型变量a、b、c、d中的值依次为:1432。则条件表达式a<b?a:c<d?c:d的值 (   )  。
答案:1
1
2

解析:

等价于

if( a<b )
{
    return a;
}
else
{
    if( c<d )
    {
        return c;
    }
    else
    {
        return d;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 五、相似却不是这个考点的题目

# (2)例2(你真的搞懂了我编的口诀?)

运算符的优先级正确排序是:
赋值运算符<逻辑运算符<关系运算符<算术运算符。
答案:错误
1
2
3

解析:可能有人记忆我的口诀会误以为,逻辑运算符全在算术运算符后面。

但是,请深刻理解口诀,单目运算符在算法运算符前面,比如我们的既是单目运算符又是逻辑运算符的!就在前面

# (3)你以为我是在考优先级?

  • 其实是在考“短路原则”
若定义了int m=1,n=2;在执行了--m&&n++;++m||n++;2条语句后,n的值为?
答案: A
A 2
B 3
C 4
D 1
1
2
3
4
5
6

# (4)(易错!)这次我真的在考“优先级”,顺路考“短路原则”(短路优先原则的逆袭)

int main()	//360公司 招聘真题
{
    int a=1,b=2,m=0,n=0,k;
    
    k=(n=b<a)&&(m=a) ;
    printf("%d,%d\n",k,m);
    
    return 0;
}
答案:A	易错选:B(结合性的理解出错?)
A 0,0
B 0,1
C 1,0
D 1,1
1
2
3
4
5
6
7
8
9
10
11
12
13
14

解释:

  • 错误分析方式:

1、优先级,初级运算符先开始,从左边到右边

平行宇宙第1( n= (b<a) )这个之后,n=0, m=0
平行宇宙第2( m=a )这个m=1 (错误的地方在这)
1
2
  • 正确分析方式:

错误的地方,我们需要理解结合性,到这个地方,我们???(这个,我也不会解释。。。)

  • 思考了一阵子之后,还是想,前面的方法,都是我的猜测,有点忽略了“编译器”是用“DFA”自动机,进行识别的。
  • 所以,修正前面的方法,我们在进行词法分析之后,优先级啥的分析之前,我们需要将“&&和||”这样的具有“短路原则”的情况,额外拿出。
int a=5,b=3;	//招商银行信用卡中心 招聘真题
!a&&b++; 
	
printf("%d %d",a,b);//输出是5 3
1
2
3
4

解析:

正确分析

! a && b ++
然后0 && b ++
短路原则得到0
1
2
3

错误分析

! a && b ++
然后0 && b ++
优先级,然后0 && 4 //b=4
短路原则得到0
1
2
3
4

# (5)结合性和指针结合

*p++ 自增p 还是 p 所指向的变量?
答案:A
A 先自增p
B 先自增p所指向的变量
C 和机器相关

1
2
3
4
5
6

解析:优先级,*和++同级,但是结合性是从右边到左边,等价于

*( p++ )
1

# (6)优先级和类型升级结合

假定x和y为double型,则表达式x=2,y=x+3/2的值是
答案: D 
A 3.500000
B 3
C 2.000000
D 3.000000
1
2
3
4
5
6

# (7)易错

int a=4,则对于表达式++(a++)的结果说法正确的是()
答案: D 
A 结果为5
B 结果为6
C 结果为7
D 以上不都是
1
2
3
4
5
6

解析

a++的结果是4,然后进行++4是不对的,++运算只是针对于变量的,不能对常量来进行++运算。

# 六、C++中运算符

若有int i=3,*p;p=&i;输出结果为3的是 cout<<*p?
答案:正确
1
2