操作符详解

本文详细探讨了C/C++中的三目运算符、不同类型运算符的优先级和行为,包括算数、移位、位操作符,以及赋值、单目、关系和逻辑操作符。特别关注了条件运算符和隐式类型转换在复杂表达式中的作用,适合开发者理解运算符的使用和技巧。
摘要由CSDN通过智能技术生成

目录

三目运算符(条件操作符)的细节

一、操作符分类:

二、算数操作符:

三、移位操作符:

1.左移操作符

2.右移操作符

四、位操作符:

按位与:

按位或:

按位异或:

 五、赋值操作符:

连续赋值:

复合赋值符:

 六、单目操作符:

1.单目操作符介绍:

sizeof: 

2.sizeof和数组:

七、关系操作符:

八、逻辑操作符:

 九、条件操作符:

十、逗号表达式:

 十一、下标引用、函数调用和结构成员:

1.[] 小标引用操作符

2.() 函数调用操作符

3.访问一个结构体的成员

 十二、表达式求值:

1.隐式类型转换

 2.算术转换

3.操作符的属性

操作符优先级:

最后要说的话:


三目运算符(条件操作符)的细节

一个很重要的内容,上个星期发现的,三元运算符。

exp1 ? exp2 : exp3

看一下问题是怎么发现的,我是在写Java的时候发现的(现在时间2022/04/24 15:47)。

Java代码

于是我就去看一下C语言是不是这样

 

C语言也是这样的情况,是让最终结果转换为64位的无符号类型。 

于是我就想继续探索下去

 看来最终的结果是根据问好后面的两个结果来判断的。不信可以看下面的例子

 

主要想讲的是Java,Java中三目运算符是想看全部的整体类型,double的优先优先级比int高,所以结果一定是double类型的。

注意移位操作符和位操作符需要了解原码、补码、反码,如果不了解可以访问我上一篇文章。

一、操作符分类:

1.算数操作符

2.移位操作符

3.位操作符

4.赋值操作符

5.单目操作符

6.关系操作符

7.逻辑操作符

8.条件操作符

9.逗号表达式

10.下标引用、函数调用和结构成员

二、算数操作符:

1.除了%操作符以外,其他操作符可以用于整型和和实型的运算(整型、实型见上一篇文章开头)。

2.对于/操作符如果两个操作数都为整型,与数学数学计算的除法相同,如果有一个操作数是实型,则结果一定是小数。

3.%操作符的两个操作数必须是整型,返回的是整除后的余数。

三、移位操作符:

1.左移操作符

操作数1 << 操作数2

操作数1被执行的数

<< 左移操作符

操作数2要执行多少位

移位规则:左边丢弃,右边补0

例如:

 注意:a的不会发生变化,只是让a这个数变化赋给b而自身没有改变,除非a = a << 1,这样是对a进行操作,a自己的值发生了变化。

2.右移操作符

操作数1 << 操作数2

操作数1被执行的数

<< 左移操作符

操作数2要执行多少位

右移运算分为两种逻辑移位算术移位

逻辑移位左边用0填充,右边丢弃。

算术移位左边用原来的符号位填充,右边丢弃。

例如:

 由图三、图四可看出在VS2019的环境下是算术移位,可以在评论区说出你们的环境是那种运算方式。

警告
对于移位运算符,不要移动负数位,这个是标准未定义的。
例如:

 为了满足你们好奇心我就试一试

 没错,我也不知道为什么。

再来一个

 还是这样,所以说不要尝试了,谁没事知道有错还总是犯错啊。

四、位操作符:

按位与:

两个数的二进制位相同位是1时为1,否则为0。

按位或:

两个数的二进制位相同为只要有1就为1,否则为0.

按位异或:

两个数的二进制位相同位相同为0相反为1(这里认为0与1相反)。

 五、赋值操作符:

感觉这个没什么好讲的,感觉只有两点:连续赋值复合赋值

连续赋值:

例如:

 这里我突然想到一个常见错误

 这个应该有人中过招把,让我来细细拆解。

 如何正确表达?

这算是个小插曲吧,突然想到的,不认识&&的就看后面我的讲解,文章会讲到这个操作符。

复合赋值符:

 我只讲一个,其他的是一样的使用。

 六、单目操作符:

1.单目操作符介绍:

 !:让真变为假,让假变为真。

 -、+:感觉没什么讲的。

&:这里不是按位与,不要搞混了。

*:这里不是乘法运算的*,不要搞混了。

sizeof: 

 括号里面是看的类型,不看里面的值,而且括号里面不进行运算(重点)。

而且sizeof的返回类型为unsign int 这个也应该注意,尤其是参与运算时,例如 

 -1与sizeof(a)比较时要发生整型提升(不了解可以参考我的上一篇文章)。-1会提升为无符号类型

 ~:连同符号位,1变为0,0变为1。

 原码、反码、补码不熟悉一定要看我上一篇文章。其他看不懂的话,只用看原码、反码、补码就行。其他的可能需要让这篇文章看完才行。

2.sizeof和数组:

数组如果一个也不初始化,则全部都是随机数。再讲一个细节全局变量没有初始化默认为0(假设int类型)

七、关系操作符:

 比较简单,感觉只有一点要讲的,一种不细心错误:

 如果这样编译器也不报错,怎么解决这个问题呢?

其实不不难。

 这样据很好的解决这个问题了,编译器直接报错。但是,为什么报错呢?其实很简单=的左边必须是变量,常量是不行的。这里有人可能有要问了我给它一个常变量行不行?马上满足你们的要求。

八、逻辑操作符:

 

 &&、||一般用于if语句:

这两个操作符的操作数都是两个,&&两个操作数都为真(非0)结果才为真,||两个操作数只要有一个为真(非0)则结果为真。

但是,&&的优先级比||的高。即:先执行&&再执行||。

&&的运算细节是如果左操作数为假,则&&右侧的操作数不执行。

||的运算细节是如果左操作数为真,则||右侧的操作数不执行。

 九、条件操作符:

exp1 ? exp2 : exp3
如果表达式1结果为真执行表达式2,否则执行表达式3。

十、逗号表达式:

exp1, exp2, exp2, exp2,...expN

最终的结果是expN的值。

 十一、下标引用、函数调用和结构成员:

1.[] 小标引用操作符

操作数有两个:一个数组名、一个索引值(就是[]里面的数)。

2.() 函数调用操作符

接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

 上面的()操作符有三个操作数:Max、a、b。

 上面()操作符的操作数有一个test

3.访问一个结构体的成员

 .实例:

 ->实例:

 十二、表达式求值:

表达式求值的顺序一部分是由操作符的优先级结合性决定。但是这两个结合并不能解决所有问题。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。

1.隐式类型转换

 255变为char类型是发生截断,因为打印格式是%d所以要进行整型提升,从最左边的符号位进行提升,左边每提升的都是符号位的数字(如果是负数则左边都补1,如果是正数或者无符号数则补0)。

重新回顾sizeof实例中的if(-1 < sizeof(int))。

 应该是编译阶段编译器发现了不对。编译器不允许明着让整型提升,真是对应了隐式类型转换这个性质了。

不要害怕报错,我经常报错。所有的高手在自己敲代码时都有报错过,我们只是比他们走的慢了点,艰难了点而已。我们要做到快速找到报错我原因,以防以后不出现这样的错误。打了一波鸡血。

sizeof的返回类型为size_t,我们看一下size_t是什么东西。鼠标放在size_t的上面,左键点击,右键点击转到定义。

 

 没错,就是让unsigned int 这个类型用 size_t表示了。意思就是sizeof()的返回类型是unsigned int,根据向上转型的规则(这个好像忘记讲了)

 来一道题:

#include<stdio.h>

//实例1
int main()
{
	char a = 0xb6;
	short b = 0xb600;
	int c = 0xb6000000;
	if (a == 0xb6)
		printf("a");
	if (b == 0xb600)
		printf("b");
	if (c == 0xb6000000)
		printf("c");
	return 0;
}

 

 

 

 

 再来道题:

#include<stdio.h>

//实例2
int main()
{
	char c = 1;
	printf("%u\n", sizeof(c));
	printf("%u\n", sizeof(+c));
	printf("%u\n", sizeof(-c));
	return 0;
}

 

 2.算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
警告:
但是算术转换要合理,要不然会有一些潜在的问题。

 好吧我没思考就实验了:

 至于为什么是这个数就看我的上一篇文章最后的实型再内存存储。那么怎么才能表达我想要表达的意思呢?

 

 这两种方法都可以证明高精度向低精度转换精度会丢失。

3.操作符的属性

复杂表达式的求值有三个影响的因素。
1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序。
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。


操作符优先级:

操作符描述结合性是否控制求值顺序
()聚组N/A
()函数调用L/R
[]下标引用L/R
.访问结构成员L/R
->访问结构指针成员L/R
++后缀自增L/R
--后缀自减L/R
!逻辑反R/L
~按位取反R/L
+单目,表示正值R/L
-单目,表示负值R/L
++前缀自增R/L
--前缀自减R/L
*间接访问R/L
&取地址R/L
sizeof取其长度,以字节表示R/L
(类型)类型转换R/L
*乘法L/R
/除法L/R
%整数取余L/R
+加法L/R
-减法L/R
<<左移位L/R
>>右移位L/R
>大于L/R
>=大于等于L/R
<小于L/R
<=小于等于L/R
==等于L/R
!=不等于L/R
&位与L/R
^位异或L/R
|位或L/R
&&逻辑与L/R
||逻辑或L/R
?:条件操作符N/A
=赋值R/L
+=以…加R/L
-=以…减R/L
*=以…乘R/L
/=以…除R/L
%=以…取模R/L
<<=以…左移R/L
>>=以…右移R/L
&=以…与R/L
^=以…异或R/L
|=以…或R/L
,逗号L/R

有了优先级和结合性运算是一定唯一的吗?

实例一:

 没错我就是自己想的,所以尽量越难越好😀。

你们是不是以为是这样?

看看结果吧~

 没错,编译器都不知道怎么运算了,也不能这么说,是不是按照我们这样想的运算。所以表达式结果不是简单的用优先级和结合性就可以计算结果,当然这是困难的表达式,一般谁会这样出题,如果以后运算表达式害怕那就用(),减少不必要的麻烦。

再举一个例子:

直接公布结果吧,不同编译器有不同的结果,VS2019编译器的结果是i = 4。linux gcc编译器结果是-63,可以@出你们的结果。

最后要说的话:

制作不易,这篇文章大概耗时8个小时(两次连续3小时),我非常希望有小伙伴找到并纠正我的错误(我暂时还没有发现)。

  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值