目录
1.算数运算符:+ - * / %
2.移位操作符
(1)<<左移操作符
(2)>>右移操作符
算术移位:右边丢弃,左边补原符号位
逻辑移位:右边丢弃,左边补零
3.位操作符
(1)& - 按(二进制)位与
(2)| - 按(二进制)位或
(3)^ - 按(二进制)位异或
(4)用法
<1>位异或用法
<2>位与用法
<3>位或运算
4.赋值操作符:"="
复合赋值符+=,-=,*=,/=,%=,>>=,<<=,&=,|=,^=
5.单目操作符(只有一个操作数)
!,-,+,&,sizeof,~,--,++,*,(类型)
6.关系操作符:>,>=,<,<=,!=,==
7.逻辑操作符:&&(逻辑与),||(逻辑或)
8.条件(三目)操作符:exp1? exp2 : exp3
9.逗号表达式
10.下标引用,函数调用,和结构成员
(1)[ ] 下标引用操作符
(2)()函数调用操作符
(3)访问一个结构的成员
11.表达式求值
(1)隐式类型转换
(2)算术转换
(3)操作符的属性
1.算数运算符:+ - * / %
- “/”操作符如果结果要得到浮点数,则至少有一个数应该是浮点数
- “%”取模操作符的两端必须是整数,负数运算与实际运算结果也相同
2.移位操作符
警告:不要移动负数位,这是标准未定义型
例如:
a >> -1
移位操作符移的是补码二进制位,int型四个字节,所以32位二进制位
注:移位操作符的操作数只能是整数
(1)<<左移操作符
运算规则:向左移动,左边丢弃,右边补零
结果都是原来数的2^n倍,n即为位移量 ,负数向下取整
代码样例:
int a = 3; int b = a << 1;
a(补码) = 00000000000000000000000000000011
b(补码) = 00000000000000000000000000000110
输出:
由此可见,移位之后a不变,变化的结果赋值给b,值传递
(2)>>右移操作符
不同编译器采取的右移方式不同,但一般都是算术移位
算术移位:右边丢弃,左边补原符号位
结果是由原来的数除以2^n得到,正数向下取整,负数四舍五入
代码样例:
int a = 8; int b = a >> 2;
a(补码)= 00000000000000000000000000001000
b(补码)= 00000000000000000000000000000010
输出:
a = 8,b = 2
逻辑移位:右边丢弃,左边补零
3.位操作符
int型32位每一位都参与运算
(1)& - 按(二进制)位与
(2)| - 按(二进制)位或
(3)^ - 按(二进制)位异或
(4)用法
<1>位异或用法
- 不创建两个临时变量,实现两个数的交换
(3)^ - 按(二进制)位异或
(4)用法
<1>位异或用法
- 不创建两个临时变量,实现两个数的交换
<1>位异或用法
- 不创建两个临时变量,实现两个数的交换
省一个变量,但开发中不常用,运行速度比定义一个变量实现交换要慢
int a = 3; int b = 5; a = a ^ b; b = a ^ b; a = a ^ b;
结果打印:
//交换前:a = 3,b = 5 //交换后:a = 5,b = 3
原理:位操作符满足交换律,a ^ a == 0,a ^ 0 = a;
b = a ^ b ^ b == a;
a = a ^ b ^ a == b;
<2>位与用法
- 求一个整数存储在内存中的二进制中1的个数(即求补码中的一的个数)
(1)方法一
int a = 3;
int count = 0;
for(int i = 0; i < 32; i++){
if(a & 1 == 1) count++;
a >>= 1;
}
结果打印:
//a的补码中1的个数为2
原理:1的补码为00000000000000000000000000000001,1与其他数的位与运算都只与最后一位有关,若最后一位是0,则结果是0,否则是1,再利用右移操作符循环32次,依次判断每一位的数字
(2)方法二
int a = 3;
int count = 0;
while(a){
a = a & a(a-1);
count++;
}
结果打印:
原理:a & a-1得到的结果会让a的二进制位失去一个1,多次循环就可得到1的个数,不需要挨个判断
举例:11——1011
11-1——1010 11 & (11-1)== 1010
1010 - 1——1001 1010&1001 == 1000
1000 - 1——0111 1000&0111 == 0000
<3>位或运算
- 将一个数补码中的某一位赋值为1
int a = 10;
//假设赋值的是第5位
int count = 5;
int b = 1;
b <<= count - 1;
a = a | b;
结果打印:
//a的二进制为0000000A //a的二进制为0000001A
原理:1的补码为00000000000000000000000000000001,1可通过左移操作符找到目标位置,此时与其他数的位或运算都只与目标位有关,此时目标位由于或运算被赋值为1
4.赋值操作符:"="
注意”=“与”==“的区别
赋值运算符可以连续使用,但一般不使用
a = b = 3;
复合赋值符+=,-=,*=,/=,%=,>>=,<<=,&=,|=,^=
5.单目操作符(只有一个操作数)
!,-,+,&,sizeof,~,--,++,*,(类型)
- sizeof 操作符的类型长度(以字节为单位)
!,-,+,&,sizeof,~,--,++,*,(类型)
- sizeof 操作符的类型长度(以字节为单位)
当在自定义函数地址传递时,sizeof只会计算指针的字节数,无论是数组指针,int型指针,char型指针,都一视同仁
使用形式:
sizeof(a); //a为变量 sizeof(int); //数据类型 sizeof a; //可以不加括号 //但sizeof int这种写法不允许
- ~ 对一个数的二进制按位取反(补码)
举例:将整数的补码中的某一位赋值为0
int a = 16; //假设赋值的是第5位 int count = 5; int b = 1; b <<= count - 1; a = a & ~b;
结果打印:
原理:与“将整数的补码中的某一位赋值为1”类似,但增加了一个~取反运算,且变为位与运算
- * 间接访问操作符(解引用操作符,常用形式为*p)
- (类型) 强制类型转换
6.关系操作符:>,>=,<,<=,!=,==
- 再强调一次,注意”=“与”==“的区别
- 字符串无法用“==”比较(这样比较的是两个字符串首字符的地址),可以用
头文件下的strcmp库函数来比较
7.逻辑操作符:&&(逻辑与),||(逻辑或)
对“&&”而言,当操作符左边的表达式为0(假)时,不再判断之后的表达式
对“||”而言,当操作符左边的表达式为1(真)时,不再判断之后的表达式
8.条件(三目)操作符:exp1? exp2 : exp3
9.逗号表达式
exp1,exp2 , exp3...expn;
在整个表达式里从左向右依次计算
10.下标引用,函数调用,和结构成员
(1)[ ] 下标引用操作符
例:
int arr[10]; arr[9] = 10;
[ ]的两个操作数为arr和9,因此arr[9]也可写成9[arr](类似交换律),但一般不这样写
(2)()函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数,因此至少有一个操作数
(3)访问一个结构的成员
- “.” 结构体.成员名
- “->” 结构体指针->成员名
11.表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定的
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型
(1)隐式类型转换
C的整型算数(再强调!char类型也是属于整型家族)运算总是至少以缺省(默认)整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作符在使用之前被转换为普通整型,这种转换称为整型提升
整型提升的意义:
- 表达式的整型运算要在CPU的相应运算寄存器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度
- 因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度
- 通用CPU是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算
整型提升是按照字符的符号位进行整型提升的
例如:
- char类型的-1的八位(补码)表示为:11111111
整型提升后前24位补符号位1变为11111111111111111111111111111111
- char类型的1的八位(补码)表示为:00000001
整型提升后前24位补符号位0变为00000000000000000000000000000001
- unsigned char类型无符号位,前面全部补0,-1(此时不再存在负值,即使赋值-1,实际表示255)可表示为:
00000000000000000000000011111111
经历运算之后若赋值给char类型,结果被截断,取后八位
经典例题:
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;
}
运行结果:
C
原因:在判断是否相等运算中,只有int型的c不需要整型提升,类似的,定义a为char型,用sizeof(-a)计算字节数,会发现结果为4,原因就是发生了整型提升,但sizeof(a)结果为1
(2)算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算数转换(排名采用升序)
| int |
| unsigned int |
| long int |
| unsigned long int |
| float |
| double |
| long double |
但是算数转换要合理,不然会有一些潜在问题,比如精度丢失
(3)操作符的属性
复杂表达式的求值有三个影响因素:
- 操作符的优先级
- 操作符的结合性
- 是否控制求值顺序
两个相邻的操作符先执行哪个,取决于他们的优先级,如果两者的优先级相同,取决于他们的结合性。
必须注意!!!哪怕这三个影响因素能够完全灵活运用,但是仍然会出现无法确定它的唯一运算路径得到唯一结果,即问题表达式。不同编译器规定有所不同,因此日常写代码时应避免编写过于复杂且可能出现歧义的代码
暑期编程PK赛 得CSDN机械键盘等精美礼品!


