一、操作符分类
2. 算数操作符
有 + - * / %
要注意的是
除法
1.整数除法(除号的两端都是整数)
2.浮点数除法(除号的两端只要有一个是小数就执行小数除法)
举例如下
int main() { int r = 7 / 2; printf("%d\n", r);//打印3 double d = 7 / 2; printf("%lf\n", d);//打印3 double d = 7.0 / 2.0; printf("%lf\n", d);//打印3.5 return 0; }
除法中除数不能为零
举例如下
int main() { int n = 0; int r = 5 / n; return 0;//编译器会报错 }
%
得到的是整除后的余数
要注意:% 取模操作符的两个操作数必须都是整数
举例如下
int main() { int r = 15 % 8; printf("%d\n", r);//得到7 return 0; }
3. 位移操作符
3.1 >>
要注意:移位操作符的操作数只能是整数
举例如下
int main() { int a = 15; int b = a >> 1;//移动就是a中的2进制信息,并且>>左右都是整数,不能是小数 return 0; }
要计算移位还要知道一个知识点:
计算机,能够处理的是二进制的信息,如1和0。
15 就是十进制
例如:
那么15的二进制就是:
一个整形是4个字节 = 32bit位
那么15放进a里面要凑够32个bit位要怎么写呢?
规定:最高位是0表示它是正数,最高位是1表示它是负数
所以15的二进制表示形式如下:
00000000000000000000000000001111
而整数的二进制表达形式: 有三种表达形式
1.原码:正数,最高位为0;负数,最高位为1
2.反码:原码的符号位不变,其他位按位取反(也就是说原来是1的变成0,原来是0的变成1)
3.补码:反码+1
正整数的原码、反码、补码是相同的
负整数的原码、反码、补码是要计算的
所以15的原码,反码,补码都是:00000000000000000000000000001111
举例子:
int main() { int a = 15; 00000000000000000000000000001111 - 原码 00000000000000000000000000001111 - 反码 00000000000000000000000000001111 - 补码 int b = -15; 10000000000000000000000000001111 - 原码 11111111111111111111111111110000 - 反码(原码的符号位不变,其他位按位取反得到的就是反码) 11111111111111111111111111110001 - 补码(反码+1就是补码) return 0; //}
要注意:整数在内存中存储的是补码
计算的时候也是使用补码计算的
所以移位移动的是二进制的补码
int main() { int a = 15; int b = a >> 1; printf("%d\n", b);//7 printf("%d\n", a);//15 return 0; }
上面的代码,a向右移动一位,b=7,a=15
下面是移动的方法:
右移分为两种
1.算数右移(右边丢弃,左边补原来的符号位)
2.逻辑右移(右边丢弃,左边直接补0)
C语言没有明确规定到底是算数右移还是逻辑右移,一般编译器上采用的是算术右移
右移之后,就变成了00000000000000000000000000000111
通过计算就得到了b=7,而a依然是15,这个运算不会让a的值变化
举一个负数例子:
int main() { int a = -15; int b = a >> 1; printf("%d\n", b);//-8 return 0; }
我们是对-15的补码进行移动
补码右移得到b的二进制序列:11111111111111111111111111111000
但这是b的补码,我们还要推算出原码
补码-1就是反码:11111111111111111111111111110111
反码按位取反就是原码:10000000000000000000000000001000
再按照二进制的计算方法,结果为b = -8
3.2 <<
左移相对右移就相对简单一点
左移的移动原理:左边丢弃,右边补0
举例:
int main() { int a = 6; int b = a << 1; printf("%d\n", b); return 0; }
a的二进制序列:00000000000000000000000000000110
左移得到b:00000000000000000000000000001100
正数的原反补码相同,所以b=12,a不变为6
警告:对于一位运算符,不要移动负数位,这个是标准未定义的。
nt main() { int a = 5; int b = a >> -2;//标准未定义行为 return 0; }
4.位操作符
也是操作二进制位
4.1 &按位与 计算方法
int main() { int a = 3; //00000000000000000000000000000011 - 补码 int b = -5; //10000000000000000000000000000101 //11111111111111111111111111111010 - 反码 //11111111111111111111111111111011 - 补码 int c = a & b; //& -- 对应二进制位有0则为0,两个同时为1,才是1 //00000000000000000000000000000011 -a补码 //11111111111111111111111111111011 -b补码 //00000000000000000000000000000011 -c补码 //符号位是0为正数,原反补相同 printf("%d\n", c);//计算的是原码,c=3 return 0; }
4.2 | 按位或 计算方法
int main() { int a = 3; //00000000000000000000000000000011 - 补码 int b = -5; //10000000000000000000000000000101 //11111111111111111111111111111010 //11111111111111111111111111111011 - 补码 // int c = a | b; //| - 按(2进制)位或 - 对应的二进制位有1则为1,两个同时为0才是0 //00000000000000000000000000000011 - a补码 //11111111111111111111111111111011 - b补码 //11111111111111111111111111111011 - 得到c补码 //11111111111111111111111111111010 - 反码 //10000000000000000000000000000101 - 原码 -5 printf("%d\n", c);//-5 return 0; }
4.3 ^ 按位异或计算方法
int main() { int a = 3; //00000000000000000000000000000011 - 补码 int b = -5; //10000000000000000000000000000101 - 原码 //11111111111111111111111111111010 - 反码 //11111111111111111111111111111011 - 补码 // int c = a ^ b; //^ - 按(二进制)位异或 -对应的二进制位相同为0,相异为1 //00000000000000000000000000000011 -a补码 //11111111111111111111111111111011 -b补码 //11111111111111111111111111111000 -得到c补码 //11111111111111111111111111110111 -c反码 //10000000000000000000000000001000 -c原码 -8 printf("%d\n", c);//-8 return 0; }
举一个题目看看是怎么应用叭:
不能创建临时变量(第三个变量),如何实现两个数的交换?
交换2个整型变量,正常情况下是这样写的:
①
int main() { int a = 3; int b = 5; printf("交换前:a=%d b=%d\n", a, b); int tmp = a; a = b; b = tmp; printf("交换后:a=%d b=%d\n", a, b); return 0; }
而题目要求不能创建临时变量,我们可以这样写:
②
int main() { int a = 3; int b = 5; printf("交换前:a=%d b=%d\n", a, b); a = a + b; b = a - b; a = a - b; printf("交换后:a=%d b=%d\n", a, b); return 0; }
但缺点就是容易溢出
也有第③种写法,运用到 ^ 的知识
int main() { int a = 3; int b = 5; printf("交换前:a=%d b=%d\n", a, b); a = a ^ b; b = a ^ b; a = a ^ b; printf("交换后:a=%d b=%d\n", a, b); return 0; }
如下图说明:
用异或实现交换的原理就是:
1.两个相同的数异或为0
2.任何数异或0都等于那个数
但这种例子可读性不高,效率也不如第一种好。
5.赋值操作符
赋值操作符是一个很棒的操作符,它可以让你得到一个你之前不满意的值,也就是你可以给自己重新赋值。
int weight = 120;//体重 weight = 89;//不满意就赋值 double salary = 10000.0; salary = 20000.0;//使用赋值操作符赋值
赋值操作符可以连续使用,比如:
int a = 10
int x = 0;
int y = 20;
a = x = y+1;//连续赋值(先把y+1的结果赋给x,再把x的结果赋给a)
但更推荐下面这种赋值方式:
x = y+1;
a=x;
这样的写法是不是更加清晰爽朗而且易于调试。
5.1 复合赋值符
+= -= *= /= %= >>= <<= &= |= ^=
这些运算符都可以写成复合的效果
比如:
int x = 10; x = x+10; x += 10;//复合赋值 //其他运算符一样的道理。这样写更加简洁
6. 单目操作符
只有一个操作数
6.1 单目操作符介绍
! 逻辑反操作 - 负值 + 正值 & 取地址 sizeof 操作数的类型长度(以字节为单位) ~ 对一个数的二进制按位取反 -- 前置、后置-- ++ 前置、后置++ * 间接访问操作符(解引用操作符) (类型) 强制类型转换
! 逻辑反操作 (把假变成真,把真变成假)
int main() { int flag = 0;//假 if (flag == 0)//为真,打印hehe { printf("hehe\n"); } if (!flag)//flag为假,但!flag为真,打印hehe { printf("hehe\n"); } if (flag)//假,不打印 { printf("haha\n"); } return 0; }
-负值 +正值
int main() { int a = 5; int b = -a; printf("%d\n", b);//b=-5 return 0; }
& * 应用于指针
int main() { int a = 10; //pa是指针变量 int* pa = &a;//&-取地址操作符 - 取出a的地址 - 存在pa里-pa的类型写成int * - *告诉我pa是指针变量 - int告诉我pa指向的那个对象a的类型是int *pa = 20;//解引用操作符(间接访问操作符)-单目操作符-通用pa中存放的地址,找到指向的空间(内容) int c = *pa; return 0; }
sizeof
int main() { int a = 10; printf("%d\n", sizeof(int));//4 ❌错误写法:printf("%d\n", sizeof int);//类型时括号不能去掉 printf("%d\n", sizeof(a));//4 printf("%d\n", sizeof a);//括号可以去掉,说明sizeof不是函数-函数后面的括号不能去掉 return 0; }
sizeof还可以计算数组大小
int main() { int arr[10] = {0}; printf("%d\n", sizeof(arr));//40 - 数组名表示单独放在sizeof内部,计算整个数组的大小单位字节 printf("%d\n", sizeof(int [10]));//40 return 0; }
~ 按补码二进制位取反
int main() { int a = 0;//正数 printf("%d\n", ~a); //00000000000000000000000000000000 -a补码 //11111111111111111111111111111111 - ~a补码 //11111111111111111111111111111110 - ~a补码-1得到反码 //10000000000000000000000000000001 - ~a反码按位取反得到原码 return 0; }