目录
操作符在编程运算中有重要作用,本文就来学习一下C语言的操作符。
1.算术操作符
+ - * /
1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
2.移位操作符
<< >>
在介绍移位操作符前先学习一下二进制的表示方法,二进制表示方法有3种:
原码,反码,补码
例子:5的二进制码
第一位数表示正负。0为正数,1为负数。
整数在内存中存的是二进制的补码。接下来回到移位操作符。
左移
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a = 5;
a = a<< 1;
printf("%d", a);
return 0;
}
上段代码将 5 的补码左移一位,再最后一位补上0
此时二进制补码变成了 2³+2=8+2=10 。
输出结果
10
这次对-5左移一位
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a = -5;
//-5 的补码1111 1111 1111 1111 1111 1111 1111 1011
a = a<< 1;
printf("%d", a);
return 0;
}
打印时,将打印原码,因此会先将补码转成原码。正数因为源反补都相同,不需要再换算。
计算发现 -5左移1位的原码就是 - (2³+2)= -10 ,打印结果自然也是-10。接下来介绍右移,右移分为算术右移和逻辑右移。
右移
算术右移:右边丢弃,左边补符号位。
逻辑右移:右边丢弃,左边补0 。
+5 -5 的原反补码及 对补码算术右移1位并转为原码
截图时截少了,左边的0没截到
代码:
int a = -5;
a = a >> 1;
printf("%d", a);
结果:
-3
右移运算是算术还是逻辑右移,是取决于编译器的,此次结果表明当前编译器采用的是算术右移。
注意:无法移动负数位,标准里未定义。
3. 位操作符
| & ^ ~
&位与:类似且的逻辑,两者都为1,得1。
| 位或:二进制运算,有1得1,两者只要有1就得1。
^位或与:两者相同为0,相异为1。
~位取反 对一个数的二进制按位取反。
& 位与
程序
int a = -3;
int b = 5;
int c = a
printf("%d", c);
同样先算出a,b的补码
a=-3,b=5
a 10000000000000000000000000000011 原
a 11111111111111111111111111111100 反
a 11111111111111111111111111111101 补
b 00000000000000000000000000000101 补
按位与 a&b,每位两者都为1,才得1,否则0, 得:
a 11111111111111111111111111111101 补
b 00000000000000000000000000000101 补
c 00000000000000000000000000000101 补
因为打印时会打印原码,这里再将补码转为原码,即:
因为c为正数所以正反补都相同
c 00000000000000000000000000000101 补
c 00000000000000000000000000000101 反
c 00000000000000000000000000000101 原
运行结果
5
| 位或
同理
程序
int a = -3;
int b = 5;
int c = a | b;
printf("%d", c);
运行结果
-3
^ 位或与
相同为0,相异为1。
程序
void test4() {
int a = -3;
int b = 5;
int c = a ^ b;
printf("%d", c);
}
运行结果
-8
或与特殊的两种用法,任何数与0或与是它本身,任何数与它本身或与是0。
程序
int a = 3;
printf("%d\n", a ^ 0);
printf("%d", a ^ a);
运行结果
3
0
运算过程
相同取0 相异取1
a 000000000000000000000000000000000011 原
a 111111111111111111111111111111111100 反
a 111111111111111111111111111111111101 补
0 000000000000000000000000000000000000 补
a^0 111111111111111111111111111111111101 补
a 111111111111111111111111111111111101 补
a 111111111111111111111111111111111101 补
a^a 000000000000000000000000000000000000 补
可以发现a^0=a,a^a=0 。由此又延伸出一种很常用的算法,或与一组数据就能得到其中不成对的数字。
对1234321这组数据或与
1^2^3^4^3^2^1=4
成对的数字相抵消为0,最后剩下一个4与0或与,就是4本身。
这里能听懂,那这道题对你来说那必是轻而易举(~ ̄▽ ̄)~力扣
有了这样的基础,你甚至能拿两个变量做更nb的操作——无需第三个变量就能交换两个变量的值。
int a = 3;
int b = 5;
a = a ^ b;
b = a ^ b;//此时a = a ^ b;
a = a ^ b;//此时b=a^b^b =a,也就是a =a^b^a 最终等于b
printf("a=%d b=%d", a, b);
运行结果
a=5 b=3
当然这里也可以用求和的方式实现交换,但这种方法有缺陷的,当a ,b的值非常大时,他们的和就会溢出,最终得不到想要的值,算权宜之计。
~ 按位取反
将补码全部取反,打印的时候同样要转换成原码打印。
1.可以将二进制某位从0变成1或者从1变成0 ,具体的过程看注释。
000000000000000000000000000000001011
↓
000000000000000000000000000000001111
↓
000000000000000000000000000000001011
程序
//~取反
//000000000000000000000000000000001011
int a = 11;
a |= (1 << 2);//对1左移2位
//000000000000000000000000000000001011 a
//000000000000000000000000000000000100 (1 << 2)
//000000000000000000000000000000001111 进行位或运算得到结果
printf("变形:%d\n", a);
a ~(1 << 2);
//000000000000000000000000000000001011 a
//111111111111111111111111111111111011 ~(1 << 2)
//000000000000000000000000000000001011 进行位与运算得到结果
printf("还原:%d\n", a);
运行结果
变形:15
还原:11
2. 可以用来终止获取输入 while(~scanf() )
scanf 读取失败时返回 -1,而-1的补码是32/64位1,取反后为0,即返回假,所以条件终止。
4.赋值操作符
赋值,字面理解可以给变量赋值的操作符。
复合赋值符
5.单目操作符
6.关系操作符
关系操作符
7.逻辑操作符
区分逻辑与和按位与,区分逻辑或和按位或。
逻辑与遇到假就停止判断后面的操作数;
逻辑或遇到真就停止判断后面的操作数。
8.条件操作符
判断是否满足exp1,真执行exp2,假执行exp3。可以做比大小的函数。
9.逗号表达式
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
c是多少?
c最后将收到最后一个表达式,b的返回值,变成13。
10.下标引用、函数调用和结构成员
1. [ ] 下标引用操作符
操作数:一个数组名 + 一个索引值
2. ( ) 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
3. 访问一个结构的成员
11.表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定,操作过程可能需要转成其他类型。
11.1 隐式类型转换
精度较小的类型数据与精度大的类型数据运算时,会把精度小的类型转为精度大的类型再运算。C的整型算术运算总是至少以缺省整型类型的精度来进行的。
整形提升
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0
整形提升的例子:
//实例
int main()
{
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;//0xb6,0xb600,0xb6000000这些都是整形常数
if(a==0xb6) //a整形提升后前面为一大串1,0xb6前面为一大串0
printf("a");
if(b==0xb600)
printf("b");
if(c==0xb6000000)
printf("c");
return 0;
}
c
11.2 算术转换
当操作符的各个操作数的类型不相同时,按下面的顺序,将一个操作数类型转换成另一个操作数类型再进行运算。
排名在下的类型将往上转换
如:float 见double 会先将float转换成double再运算。
11.3 操作符优先级
优先级 | 运算符 | 名称或含义 | 使用形式 | 结合方向 | 说明 |
1 | [] | 数组下标 | 数组名[常量表达式] | 左到右 | -- |
() | 圆括号 | (表达式)/函数名(形参表) | -- | ||
. | 成员选择(对象) | 对象.成员名 | -- | ||
-> | 成员选择(指针) | 对象指针->成员名 | -- | ||
2 | - | 负号运算符 | -表达式 | 右到左 | 单目运算符 |
~ | 按位取反运算符 | ~表达式 | |||
++ | 自增运算符 | ++变量名/变量名++ | |||
-- | 自减运算符 | --变量名/变量名-- | |||
* | 取值运算符 | *指针变量 | |||
& | 取地址运算符 | &变量名 | |||
! | 逻辑非运算符 | !表达式 | |||
(类型) | 强制类型转换 | (数据类型)表达式 | -- | ||
sizeof | 长度运算符 | sizeof(表达式) | -- | ||
3 | / | 除 | 表达式/表达式 | 左到右 | 双目运算符 |
* | 乘 | 表达式*表达式 | |||
% | 余数(取模) | 整型表达式%整型表达式 | |||
4 | + | 加 | 表达式+表达式 | 左到右 | 双目运算符 |
- | 减 | 表达式-表达式 | |||
5 | << | 左移 | 变量<<表达式 | 左到右 | 双目运算符 |
>> | 右移 | 变量>>表达式 | |||
6 | > | 大于 | 表达式>表达式 | 左到右 | 双目运算符 |
>= | 大于等于 | 表达式>=表达式 | |||
< | 小于 | 表达式<表达式 | |||
<= | 小于等于 | 表达式<=表达式 | |||
7 | == | 等于 | 表达式==表达式 | 左到右 | 双目运算符 |
!= | 不等于 | 表达式!= 表达式 | |||
8 | & | 按位与 | 表达式&表达式 | 左到右 | 双目运算符 |
9 | ^ | 按位异或 | 表达式^表达式 | 左到右 | 双目运算符 |
10 | | | 按位或 | 表达式|表达式 | 左到右 | 双目运算符 |
11 | && | 逻辑与 | 表达式&&表达式 | 左到右 | 双目运算符 |
12 | || | 逻辑或 | 表达式||表达式 | 左到右 | 双目运算符 |
13 | ?: | 条件运算符 | 表达式1? 表达式2: 表达式3 | 右到左 | 三目运算符 |
14 | = | 赋值运算符 | 变量=表达式 | 右到左 | -- |
/= | 除后赋值 | 变量/=表达式 | -- | ||
*= | 乘后赋值 | 变量*=表达式 | -- | ||
%= | 取模后赋值 | 变量%=表达式 | -- | ||
+= | 加后赋值 | 变量+=表达式 | -- | ||
-= | 减后赋值 | 变量-=表达式 | -- | ||
<<= | 左移后赋值 | 变量<<=表达式 | -- | ||
>>= | 右移后赋值 | 变量>>=表达式 | -- | ||
&= | 按位与后赋值 | 变量&=表达式 | -- | ||
^= | 按位异或后赋值 | 变量^=表达式 | -- | ||
|= | 按位或后赋值 | 变量|=表达式 | -- | ||
15 | , | 逗号运算符 | 表达式,表达式,… | 左到右 | -- |
来源