五、操作符详解
分类
- 算数操作符
- 移位操作符
- 位操作符
- 赋值操作符
- 单目操作符
- 关系操作符
- 逻辑操作符
- 条件操作符
- 逗号表达式
- 下标引用、函数调用和结构成员
算数操作符
+ - * / %
int a = 5/2;
//a=2
double a = 5.0/2
//a=2.500000 默认打印六位小数
int a = 5%2;
//a=1 (注意:取模%只能用于整数!!)
移位操作符
右移操作符
除2
- 算术右移(主要)
右边丢弃,左边补原符号位,原来是正数,补0;原来是负数,补1
-
逻辑右移
右边丢弃,左边补0
int a = 16;
a >> 1; // >> 右移操作符,移动的是二进制位
//16的二进制: 00000000000000000000000000010000
//右移1位: (0)0000000000000000000000000001000
//如果是负数
int a = -1;
int b = a >> 1;
//此时移动的是补码 a的补码: 原码:10000000000000000000000000000001
反码:11111111111111111111111111111110//符号位不变,其他位按位取反
补码:11111111111111111111111111111111//反码+1
将
a的补码右移:右边丢一个1,左边补符号位,为1,补码还是全1
左移操作符
乘2
左移操作符:左边丢弃,右边补0
int a = 5;
int b = a << 1;
// a:00000000000000000000000000000101
// b:00000000000000000000000000001010
// b = 10;
// 左移:左边丢弃,右边补0
位操作符
-
& 按位 与 —— 全都要1
-
| 按位 或 —— 至少有1个1
-
^ 按位 异或 —— 相异为1,相同为0
注意:它们的操作数必须是整数!!
例题
1. 不创建临时变量,交换两个数的值(此方法相比加减法来说不会溢出)
#include<stdio.h>
int main()
{
int a=3,b=5;
a=a^b;
b=a^b;
a=a^b;
printf("a=%d,b=%d",a,b);
return 0;
}
2. 统计数字补码中1的个数
// #include<stdio.h>
// int main()
// {
// int num = 0;
// int count = 0;
// scanf("%d",&num);
// while(num)
// {
// if(num%2 == 1)
// count++;
// num = num/2;
// }
// printf("count=%d",count);
// return 0;
// }
//无法计算负数
#include<stdio.h>
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)) //此处num右移发生在if的判断条件内,不改变num的实际值,所以每次位移i;
//如果将num的右移写在if外,则会改变num的值,则每次只能右移1位;但对于一开始末尾为1的数字来说,一开始就右移1,会导致结果少算了1个
//num & 1的意思即为num和00000000000000000000000000000001做与操作
{
count++;
}
}
printf("count=%d",count);
return 0;
}
赋值操作符
变量创建时给值叫初始化;
变量已经有值了再给值叫赋值;
赋值操作符可以 连续使用,即连续赋值
复合赋值符
+=
-=
/=
*=
%=
>>=
<<=
&=
|=
^=
单目操作符
! 逻辑反
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 解引用
(类型) 强制类型转换
注:
- sizeof内部的表达式并不实际参与运算
关系操作符
逻辑操作符
&& 逻辑与
|| 逻辑或
关注的是数字本身为真/假,而不是关注二进制
&&左边如果为假,则右边的都不执行
int i=0,a=0,b=1,c=2;
i = a++ && ++b && ++c;
//结果: a=1,b=1,c=2
//原因:a++时先使用a,a此时为0;&&前为0,则后面所有的都不执行;但a++执行时还是要对a+1,则a = 1;
||左边为真,则右边也不执行
int i=0,a=1,b=2,c=3;
i = a++||++b||c++;
//结果:a=2,b=2,c=3
条件操作符(三目操作符)
exp1 ? exp2 : exp3
逗号表达式
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
下标引用、函数调用和结构成员
下标引用操作符
[ ]
操作数:一个数组名+一个索引值
函数调用操作符
调用函数的时候使用的()
操作数:函数名(+参数)
结构成员
符号: 结构体变量.成员名
结构体指针 -> 成员名
使用:
方法一
方法二
方法三
表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定的,同样,有些表达式的操作数在求值的过程中需要转换为其他类型
隐式类型转换
C的整型算术运算总是以缺省整型类型的精度来进行的
整型提升
表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升
整型提升是按照变量的数据类型的符号位来提升的(若是无符号类型,直接补0)
#include <stdio.h>
int main()
{
char a = 3;
// a本来的值: 00000000000000000000000000000011
//char类型只有1个字节8位,发生截断,实际a的值则为截断后最末尾的8位: 00000011
char b = 127;
//b本来的值:000000000000000000000000011111111
//截断后b的实际值为: 011111111
char c = a+b;
//a+b的过程发生了整型提升
// char类型一般都是有符号位的,则对a来说,00000011的符号位为0,则将a整型化的时候前面都补0,即补为00000000000000000000000000000011
//对b来说,011111111,符号位为0,则将b整型化前面全部补0,即为000000000000000000000000011111111
//再执行c=a+b, 00000000000000000000000000000011+000000000000000000000000011111111=00000000000000000000000010000010
//c再进行截断,转为char类型 c=10000010
printf ( "%d" ,c);
//打印c,先对c进行整型提升,c的符号位为1,则前面全部补1,为11111111111111111111111110000010
//打印的是变量的原码,存储的是变量的补码
//11111111111111111111111110000010 - 补码
//11111111111111111111111111111101 - 反码
//10000000000000000000000000000010 - 原码
//原码 = -126
return 0;
}
例子2
#include <stdio.h>
int main()
{
char a = 0xb6;
if(a == 0xb6)
printf("a");
else
printf("no!");
return 0;
}
/* 1.定义变量 char a = 0xb6;
在这一步,我们将一个十六进制常量 0xb6 赋值给 char 类型的变量 a。0xb6 是一个八位的十六进制数,它的二进制表示为:10110110。
2.表达式 a == 0xb6 中的整型提升
在进行比较运算时,a 会被提升为 int 类型。整型提升将 char 类型的 a 扩展为 int 类型,并使用符号扩展。
扩展后的 a 变为:11111111 11111111 11111111 10110110
3.0xb6 的二进制表示不变
在这里,0xb6 本就是一个常量,因此其二进制表示不会发生改变,仍然是:00000000 00000000 00000000 10110110
4.进行比较运算 a == 0xb6
现在,我们有两个相同大小的整数,一个是 a 经过整型提升后的结果,另一个是常量 0xb6 的二进制表示。
此时进行比较运算 a == 0xb6,实际上比较的是两个整数的值。因为 a 的值与 0xb6 不相等,所以条件不成立,printf("a") 不会被执行。*/
只要参与表达运算,就会发生整型提升,如加减乘除等
例子3
#include<stdio.h>
int main()
{
char c = 1;
printf("%u ",sizeof(c));
printf("%u ",sizeof(+c));
return 0;
}
//结果为1 4
//c没有进行运算,不用整型提升,char类型占1个字节
//+c进行了运算,需要整型提升,即变为了整型,占4的字节
算术转换
如果某个操作符的操作数属于不同的类型,那么除非其中一个操作数能转换为另一个操作数的类型,否则操作就无法进行。
寻常算术转换
层次体系:
long double
double
float
unsigned long int
long int
unsigned int
int
默认下转上
操作符的属性
优先级
1,优先级1级
结合方向 左结合(自左至右) :()圆括号 , []下标运算符 , ->指向结构成员运算符 , .结构体成员运算符 。
2,优先级2级
结合方向 右结合(自右向左) : !逻辑非运算符 , ~按位取反运算符 , ++自增运算符 , --自减运算符 ,-负号运算符 ,类型转换运算符 , *指针运算符 , &地址与运算符 , sizeof长度运算符。
3,优先级3级
结合方向 左结合 双目运算符 : *乘法运算符 ,/除法运算符 , %取余运算符 。
4,优先级4级
结合方向 左结合 双目运算符 :+加法运算符 , -减法运算符。
5,优先级5级
结合方向 左结合 双目运算符:<<左移运算符 ,>>右移运算符 。
6,优先级6级
结合方向 左结合 双目运算符:< , <=,> , >=关系运算符。
7,优先级7级
结合方向 左结合 双目运算符:==等于运算符(判断) , !=不等于运算符(判断)。
8,优先级8级
结合方向 左结合 双目运算符:&按位与运算符。
9,优先级9级
结合方向 左结合 双面运算符:^按位异或运算符。
10,优先级10级
结合方向 左结合 双目运算符:|按位或运算符。
11,优先级11级
结合方向 左结合 双目运算符:&&逻辑与运算符。
12,优先级12级
结合方向 左结合 双目运算符:||逻辑或运算符。
13,优先级13级
结合方向 右结合 三目运算符:?:条件运算符。
14,优先级14级
结合方向 右结合 双目运算符:=赋值运算符 , +=加后复值运算符 ,-=减后赋值运算符 ,
*=乘后赋值运算符 , /=除后赋值运算符 ,%=取模后赋值运算符 , <<=左移后赋值运算符 ,
>>=右移后赋值运算符 ,&=按位与后赋值运算符 , ^=按位异或后赋值运算符 ,
|=按位或后赋值运算符。
15,优先级15级
结合方向 左结合 :,逗号运算符。
结合性
是否控制求值顺序
有问题的表达式:
a*b+c*d+e*f
只能保证*比各自相邻的+号先执行,但无法保证第一个+和第三个*谁先执行
c + --c;
只能保证先--再+ 但无法确定左边的c的取值是在--c之前取得还是--c之后取得
表达式必须要有唯一的计算路径
标签:优先级,int,结合,运算符,详解,操作符,整型 From: https://www.cnblogs.com/arongsec/p/17589713.html