1. 操作符分类
操作符有这么几类:
算术操作符、移位操作符、位操作符、赋值操作符、单目操作符、关系操作符、逻辑操作符、条件操作符、逗号表达式、下标引用、函数调用、结构成员。
下面对这些操作符一一细细道来。
2. 算术操作符
+ - * / %
这五个都是常见的算术操作符,但是有两个符号需要特别注意:/和%。
看图得结论:
1.除了%操作符,其他的四个操作符可以用于整数和浮点数。
2.对于/操作符如果两个操作数都是整数,执行整数除法。而只是有浮点数执行的就是浮点数除法。
3.%操作符的两个操作数必须是整数,返回的是整除之后的余数。
3. 移位操作符
了解移位操作符,首先必须了解一下权值是什么?原码、反码、补码又是什么,怎么得来的?下面一步步展开。
3.1 初步了解权值是什么
举一个例子就明白:
一个数字10用二进制表示为1010,所表示的意思是:10=1* 23 +0* 22 +1* 21+ 0*20.这里的1010的第一个1的权值就是23。第二个0的权值就是22。用八进制表示10,就是10=12,这里12中1的权值就是81,2的权值是80。下面我们再来了解原反补。
3.2 初步了解原码、反码、补码
一个数分为正数和负数,计算机中是不能识别负号的,所以计算机用补码的形式存储正数和负数,规定最高符号位对数分为正数和负数,正数最高符号位为0,负数为1,这样区分正数和负数。正数的原码、反码、补码都是一样的,负数的原码、反码、补码要进行运算(其中补码=反码+1,反码=原码按位取反(除了最高符号位)。
正数的最高符号位是0,负数的最高符号位是1。
举个例子:
正数10(因为是int类型的,所以有4个字节,也就是32个比特位)
00000000000000000000000000001010 - 10的原码
00000000000000000000000000001010 -10的反码
00000000000000000000000000001010 - 10的补码
正数的原码反码补码都相同。
负数-10(32个比特位)
10000000000000000000000000001010 - -10的原码
11111111111111111111111111110101 - -10的反码
11111111111111111111111111110110 - -10的补码
负数的补码=反码+1,反码=原码按位取反(除了最高符号位不变)
3.3 左移操作符
移位规则:左边抛弃,右边补0。
先看现象,在分析:
分析:
10的补码(原码)是:
00000000000000000000000000001010
左移:
00000000000000000000000000010100 = 20(原码=补码)(因为是正数)
再来看一个奇怪的现象:
我们发现左移操作符可以对正数或者负数乘以2。
3.4 右移操作符
移位规则:右移运算分为两种:1.逻辑移位:左边用0填充,右边丢弃;2.算术移位:左边用原来该值的符号位填充,右边丢弃。其中编译器到底用哪一种呢?这是取决于编译器本身,绝大多数是用的算术移位。
还是一样先看现象再分析:
10的补码(原码)是:
00000000000000000000000000001010
右移:
00000000000000000000000000000101 = 5(原码=补码)
再来看一个现象:
我们发现右移操作符可以对正数或者负数除以2。其中是取整方式是向0取整,不懂向取整可以点击文章开始的链接。
注意:对于移位操作符,不要移动负数位,这个是标准为定义。
这段话什么意思?看这个:
4. 位操作符
& //按位与 | //按位或 ^ //按位异或 注:他们的操作数必须是整数。
符号用法;
//&-按位与 //比特位同时为1时才为1 #include <stdio.h> int main() { int a = 10; //00000000000000000000000000001010(原反补相同) int b = -3; //10000000000000000000000000000011(原码) //11111111111111111111111111111100(反码) //11111111111111111111111111111101(补码) int c = a & b; //00000000000000000000000000001010(a) //11111111111111111111111111111101(b) //00000000000000000000000000001000=8(补码)(最高符号位是0,所以是正数,补码=原码) // printf("%d\n", c); return 0; }
//|-按位或 //比特位同时为0时才为0 #include <stdio.h> int main() { int a = 5; //00000000000000000000000000000101 (5的原码也是补码) int b = -2; //10000000000000000000000000000010 (-2的原码) //11111111111111111111111111111101(-2的反码) //11111111111111111111111111111110(-2的补码) int c = a | b; //-2的补码|5的补码 //11111111111111111111111111111111 (c的补码) //11111111111111111111111111111110(反码) //10000000000000000000000000000001=-1 (原码) printf("%d\n", c); return 0; }
//按位异或 //比特位相同为0,不同为1 #include <stdio.h> int main() { int a = -8; //10000000000000000000000000001000 (-8的原码) //11111111111111111111111111110111(-8的反码) //11111111111111111111111111111000 (-8的补码) int b = 6; //00000000000000000000000000000110(6的原码,也是补码) int c = a ^ b; //-8的补码^6的补码 //11111111111111111111111111111110(c的补码) //11111111111111111111111111111101(反码) //10000000000000000000000000000010=-2(原码) printf("%d\n", c); return 0; }
4.1 练习:不能创建临时变量(第三个变量),实现两个数的交换
方法一(用按位异或来解决):
#include <stdio.h> int main() { int a = 10; //00000000000000000000000000001010(原码=补码) int b = 20; //00000000000000000000000000010100(原码=补码) a = a ^ b; //a^b=00000000000000000000000000011110=30=a b = a ^ b; //b=0000000000000000000000011110(a)^b=00000000000000000000000000001010=10; a = a ^ b; //a=00000000000000000000000000011110(a)^00000000000000000000000000001010(b)=20(a) return 0; }
方法二(用加法):
int main() { int a = 3; int b = 5; int c = 0; c =a + b;//c=8 a =c - a;//a=5 b =c - a;//b=3 return o; }
4.2 练习:编写代码实现:求一个整数存储在内存中的二进制中1的个数
//方法一: #include <stdio.h> int main() { int num = 10; int count = 0;//计数 while (num) { if (num % 2 == 1) count++; num = num / 2; } printf("二进制中1的个数 = %d\n", count); return 0; }
//方法二: #include <stdio.h> int main() { int num = -1; int i = 0; int count = 0;//计数 for (i = 0; i < 32; i++) { if (num & (1 << i)) count++; } printf("二进制中1的个数 = %d\n", count); return 0; }
//方法三: #include <stdio.h> int main() { int num = 3; int i = 0; int count = 0;//计数 while (num) { count++; num = num & (num - 1); } printf("二进制中1的个数 = %d\n", count); return 0; }
5. 赋值操作符
这里再提一次:初始化和赋值的区别:初始化是创建变量的同时给定一个值,赋值时创建变量后对其给定一个值。
如:
#include <stdio.h> int main() { int a; a = 20;//这是赋值 int b = 10;//这是初始化 return 0; }
5.1 连续赋值问题
#include <stdio.h> int main() { int a = 10; int b = 5; int c = 0; c = a = b + 8; printf("%d\n", c); return 0; }//最终输出13
#include <stdio.h> int main() { int a = 10; int b = 5; int c = 0; a = b + 8; c = a; printf("%d\n", c); return 0; }最终输出13
通过上述两段代码可以看出:
连续赋值的阅读体验极差,往往给人一种不想看的感觉。
5.2 复合赋值符
+=、-=、*=、/=、%=、>>=、<<=、&=、|=、^=
看一例子吧:
#include <stdio.h> int main() { int c = 0; c = c + 10; //c += 10;(相当于c = c + 10) return 0; }