一、介绍内容
操作符和操作数:如表达式3+2,+是操作符,+左右的3和2是操作数。 下面介绍几个相对麻烦的操作符
- 移位操作符:左移和右移操作符的区别
- 位操作符:按位与、按位或、按位异或
- 单目操作符:几个需要注意的单目操作符
- 逻辑操作符:逻辑操作符所带来的语句结束
而在表达式中求值中,关注
- 隐式类型转换和显式类型转换对表达式值的影响
- 表达式中的优先顺序
- 介绍一些错误表达式写法产生的不唯一值。
二、操作符详解
1.移位操作符
操作符的操作数必须是整数,也不能是负数先来看看什么是原码、反码、补码
左移操作符:
补码向左移动一位,右边补一位0。
右移操作符:
右移运算分为两种:
1.逻辑移位:左边用0填充,右边丢弃
2.算术移位:左边用原符号位填补,右边丢弃(大多数情况都是)
int main()
{
int a = 7;
int b1 = a >> 1;
int b2 = a << 1;
printf("%d\n", a);//7
printf("%d\n", b1);//3
printf("%d\n", b2);//14
int c = -7;
int d1 = -7 >> 1;
int d2 = -7 << 1;
printf("%d\n", c);//-7
printf("%d\n", d1);//-4
printf("%d\n", d2);//-14
}
2.位操作符
- & 按位与( & 左右操作数有0就为0,只有1 & 1才为1)
- | 按位或( | 左右操作数有1就为1,只有0 | 0才为0)
- ^ 按位异或( ^ 左右操作数相等如1 ^ 1就为0,左右不相等如1 ^ 0就为1)
- 注:他们的操作数必须是整数。
并且通过以下代码,可得到结论:
- 相同数异或得0
- 一个数异或2次同一个数,会等于它本身,0和一个数异或会等于数本身
所以或许可以通过异或交换两个变量
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
a = a^b;
b = a^b; //b=a^b^b 为原来a的值
a = a^b; //a=a^a^b 为原来b的值
printf("a = %d b = %d\n", a, b); //a=20 b=10
return 0;
}
3.单目操作符
![](https://i-blog.csdnimg.cn/blog_migrate/fc8fce779e367f4cc8f085aba62da570.png)
先来看看sizeof 操作符,sizeof可能会被认为是一个函数,但下方sizeof a去括号的时候也能得到值,这说明sizeof是一个操作符,并且可以注意一下sizeof返回值实际是无符号整型(unsigned int)。
下面来看看取反操作符 ~
4.逻辑操作符
&& 逻辑与
|| 逻辑或
因为在&&中操作数有0结果一定就为0
因为在 || 中操作数有非0结果一定非0
三、表达式求值中的问题
1.隐式类型转换
C的整型算术运算总是至少以默认整型类型的精度来进行的
也就是精度较小的char或short类型,在进行表达式运算,需要转换成至少int类型的精度来进行计算。
下面来看这个代码
int main()
{
char c1 = 5;
char c2 = 126;
//00000000 00000000 00000000 00000101 c1
//00000101 char类型一个字节的取断 c1
//00000000 00000000 00000000 01111110 c2
//01111110 c2
//c3=10000011 c1+c2 整型提升的时候补的是符号位1
//11111111 11111111 11111111 10000011 补码
//11111111 11111111 11111111 10000010 反码
//10000000 00000000 00000000 01111101 //-125
char c3 = c1 + c2;
printf("%d\n", c3); //-125
return 0;
}
还有一个例子
+c和-c,使得c整型提升为4个字节
当然sizeof不加括号也是可以的。
int main()
{
char c = 1;
printf("%u\n", sizeof(c));//1
printf("%u\n", sizeof(+c));//4
printf("%u\n", sizeof(-c));//4
return 0;
}
2.显式类型转换
在大于等于int类型精度的其它类型中,例如有以下层次体系:
- long double
- double
- float
- unsigned long int
- long int
- unsigned int
- int
如果一个float和一个int类型进行计算的话,最终结果将返回一个float类型。
如果一个float和一个double类型进行计算的话,最终结果将返回一个double类型。
来看下面这个程序,对未初始化的全局变量初始值是0,那么i–的话是-1,又sizeof(i)返回的
类型是unsigned int 类型,int和unsigned int 类型比较会将-1提升至对应的无符号整型,这个数非常大,所以结果是>。
#include <stdio.h>
int i;
int main()
{
i--;
if (i > sizeof(i))
{
printf(">\n");
}
else
{
printf("<\n");
}
return 0;
}
3.问题表达式
复杂表达式的求值有三个影响因素。
- 操作符的优先级
- 操作符的结合性
- 是否控制求值顺序
如果两个相邻的操作符优先级相同,取决于它们的结合性(结合性就是从左到右顺序,还是从右到左的顺序)。
是否控制求值顺序:例如三目运算符? :
,控制True和False情况的取值。
那么通过这三个影响因素是否能确定表达式唯一的值呢?答案是不一定
下面来看看几个表达式
表达式1:
c + --c;
操作符优先级只能决定- -的运算在+的前面,但是并不能知道,先确定c的值,还是先- -c再确定c的值,是有歧义的。
表达式2:
#include <stdio.h>
int main()
{
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("%d\n", ret);
printf("%d\n", i);
return 0;
}
第一种算法:先经过三次++i后,i=4,再进行加法,最后得12。
第二种算法:先经过前两次++i后,i=3,再进行第一个加法得6,再进行剩下的++i,i=4,最后得10
以上两种结果分别是vs和linux环境下的结果。
总结:如果不能通过操作符的三个属性确定唯一的计算结果,那么这个表达式就是有问题的,但一般人都不会这样写代码,所以也需要避免这样写,以免有歧义。
本章完~