操作符详解
分类
很多知识在5.27的学习之中已经有讲解,不多赘述。
点击进入我的学习总结
算术操作符
这里只稍微对除法与取余运算做一个解释
除法运算时若两者都为整数则只能进行整数除法,但若有一者为小数我们则可以用浮点数的方式存储
但是取模运算两者都必须为整数,不能进行小数的运算。
移位操作符
左移操作符<<
右移操作符>>
二进制位的移动,前面已有讲解,懂得二进制位就很容易理解。
左移操作符直接在末尾补0
右移操作符理论上我们区分两种,
第一种为算术右移,即右边丢弃,左边补原符号位
第二种为逻辑右移,右边丢弃,左边补0。
在我们VS编译器中使用的是算术右移的方法。
注意一下负整数在计算机存储的内存是补码!
·不要移动负数位,比如左移负1位,这会直接报错。
位操作符
三者操作必须为整数,二进制位的“逻辑电路操作”
一道面试题:
不创建临时变量(第三个变量)实现两个数的交换。
一、加减法:
a=a+b;
b=a-b;
a=a-b;
弊端:a+b若超出整型可表示最大值则无法使用
二、异或法
a=a^b;
b=a^b;
a=a^b;
可读性较差、效率低,只是用于考试。
求一个整数存储在内存中的二进制中1的个数。
int main()
{
int num = 0;
int count = 0;
scanf("%d", &num);
int i = 0;
for (i = 0; i < 32; i++)
{
if (1 == ((num >> i) & 1))
count++;
}
printf("%d\n", count);
}
赋值操作符
a=x=y+1
连续赋值的可读性不如分开写且易于调试,注意要与等于区分!
复合赋值符
前面的+、-等双目操作符基本都可以这样子使用
单目操作符(见之前的学习)
其实我们前面的学习中基本都有用到了,这里再补充一下类型的强制转换
int a = (int)3.14;
3.14不属于整数类型,我们在其之前用(int)强制将其转变为整型,不报错。
关系操作符、逻辑操作符、条件操作符
隐式类型转换
C的整型算数运算总是至少以缺少整型类型的精度来进行的
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
这是个理解起来需要点时间的知识。为了方便表示这里把64位机的64位二进制数我们只表示后面16位。
a的原、反、补码
0000000000000011
b的原、反、补码
00000000011111111
这里会将他们“截断”,因为char是只有一个字节大小的类型,
00000011
011111111
a、b两者在内存中的存储形式也如此
而两者相加的时候又会将前面的0补全,这是一次整型提升。
将这两者进行相加得到
10000010
这便是c存储在内存中的形式,打印c就要对其整型提升,前面都补1
1111111110000010
作为其补码,这是个负数再求其反码,得到
1000000001111110
这个结果就是我们最后的-126
整型提升的原则就是有符号数前面补充的就是符号数,没有就补0
意义:适配CPU的运算。
算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换
long double
double
float
unsigned long int
long int
unsigned int
int
从下往上大小递增。这样从下往上类型的转换从而适应于两者的计算就为对应的算术转换。
操作符的属性
复杂表达式求值有三个影响因素:
1、操作符的优先级(如乘除高于加减)
2、操作符的结合性(同优先级时判断何者先)
3、是否控制求值顺序
L与R的顺序表示了结合的优先级
一个问题表达式
ab+cd+e*f
这时就有两种可能的计算路径
因为操作符的优先级只能保证与之相邻的操作符,第一个加号和最后一个乘号中间相隔了两个操作符,所以就会产生这种差别。
当只是变量的时候这两种计算方式都是没有问题的,但当其为表达式的时候,如果某一者的先后运算会影响之后的运算那这个先后顺序就相当重要了!
这样子的表达式我们就无法确定它的唯一计算路径,不要使用!