算数操作符
算数操作符包括,四则操作符 +
-
*
/
和 取模操作符 %
+
-
*
的运算规则与数学中相同,重点介绍 /
和 %
的注意点。
/
号两边得操作数如果都是整数,执行的是整数除法,结果为整数商,余数被舍弃。
int main()
{
// 两边为整数
int a = 5 / 2; // == 2
// 有一个浮点数
float f = 5 / 2.0; // == 2.500000 结果默认小数点后六位
return 0;
}
%
号两边的操作数必须都是整数,结果为整除后的余数
int main()
{
// 两边为整数
int a = 5 % 2; // == 2
// 有一个浮点数
int b = 5 % 2.0; // 非法,左右操作数必须为整型
float f = 5 % 2.0; // 非法
return 0;
}
- 除了
%
其他的算数操作符都可以作用于整数和浮点数。
移位操作符
移位操作符包括,右移操作符>>
和左移操作符<<
。
移位操作符移动的是数二进制位,移位操作符只能作用与整数。
二进制数
我们通常是使用10进制来表示一个数,例如1234,千位1,百位2,十位3,个位4,个位的权重为100,十位为101,百位为102,千位为103 …
用每一位上的数 × 权重,再相加就能得到这个10进制数。
1234 = 1 × 1 0 3 + 2 × 1 0 2 + 3 × 1 0 1 + 4 × 1 0 0 1234 = 1 \times 10^3 + 2 \times 10^2 + 3 \times 10^1 + 4 \times 10^0 1234=1×103+2×102+3×101+4×100
而计算中在存储数据是以二进制数来存储,二进制数只有1,0二种表示方式,逢2进1。例如二进制数0111,它的第0位的权重是20,第1位的权重是21,第二位的权重是22。
用每一位的数 × 权重,再相加就能得到这个2进制数的十进制表示。
7 = 1 × 2 2 + 1 × 2 1 + 1 × 2 0 7 = 1 \times 2^2 + 1 \times 2^1 + 1 \times 2^0 7=1×22+1×21+1×20
原码、反码、补码
计算机中用二进制数的最高位来表示正负,也称为符号位,0表示正数,1表示负数,剩余的位用来表示二进制数的绝对值。
有符号位数的二进制表示有:原码、反码、补码。
原码
最高位表示数的符号,其他位表示数值
7原 == 00000111
-7原 == 10000111
反码
-
正数的反码和原码相同
7原 == 00000111 == 7反
-
负数的反码等于原码符号位不变,其他位按位取反。
-7原 == 10000111 <==> 11111000 == -7反
补码
-
正数的补码、反码、原码相同
7原 == 7反 == 7补 == 00000111
-
负数的补码等于反码
+
1-7原 == 10000111 <=> 11111000 = -7反
<=>
11111001 = -7补
计算机在存储数据时中存储的是二进制的补码,使用补码的方式存储是因为可以用加法同时处理加法与减法运算。
例如: 1 - 1 = 1 + (-1) = 0
-
用原码方式相加
1原 = 00000001,-1原 = 10000001
00000001 + 10000001 = 10000002 = -2 (错误)
-
用反码方式相加
1反 = 00000001,-1反 = 11111110
00000001 + 11111110 = 11111111反 = 10000000原 = -0
-0 虽然和 0 相同,但是用2个组合来表示一个数有些浪费,因此不可取。
-
用补码方式相加
1补 = 00000001,-1补 = 11111111
00000001 + 11111111 =
100000000补 = 0 (正确)经过计算,1会进到第8位,多出的最高位溢出被舍弃,0则变为符号位,因此得出的结果为正数,他的原反补相同。
右移操作符 >>
因为计算机中存储的是补码,所以在移位操作时,操作的也是补码的位。
右移操作又分为:
-
算数右移
右边舍弃,左边补原符号位。
-
逻辑右移
右边丢弃,左边补0。
VS中为算数右移
// 正数原反补相同
int main()
{
int a = 16;
int b = a >> 1; // 右移操作符,将16的二进制位向右移动一位
//16 == 00000000000000000000000000010000 >> 1
// b == 00000000000000000000000000001000 == 8
int c = a >> 2;
//16 == 00000000000000000000000000010000 >> 2
// c == 00000000000000000000000000000100 == 4
printf("%d\n", b); // 8
printf("%d\n", c); // 4
// 右移一位相当于 十进制数 / 2;
}
// 负数
int main()
{
int a = -16;
int b = a >> 1;
// -16原码 == 1000 0000 0000 0000 0000 0000 0001 0000
// -16反码 == 1111 1111 1111 1111 1111 1111 1110 1111
// -16补码 == 1111 1111 1111 1111 1111 1111 1111 0000 >> 1
// b的补码 == 1111 1111 1111 1111 1111 1111 1111 1000
// b的反码 == 1111 1111 1111 1111 1111 1111 1111 0111
// b的原码 == 1000 0000 0000 0000 0000 0000 0000 1000 == -8
printf("b = %d\n", b);
int c = a >> 5;
// -16补码 == 1111 1111 1111 1111 1111 1111 1111 0000 >> 5
// c的补码 == 1111 1111 1111 1111 1111 1111 1111 1111
// c的反码 == 1111 1111 1111 1111 1111 1111 1111 1110
// c的原码 == 1000 0000 0000 0000 0000 0000 0000 0001 == -1
printf("%d\n",c);
return 0;
// c的结果可以看出,负数右移时最大为-1,因为每次补的都是1,最终补码的值是全1,而全1转换为原码就是-1
}
左移操作符 <<
左移操作的规则只有:左边丢弃,右边补0
// 正数
int main()
{
int a = 16;
int b = 16 << 1;
// a == 0000 0000 0000 0000 0000 0000 0001 0000 << 1
// b == 0000 0000 0000 0000 0000 0000 0010 0000 == 32
printf("b = %d\n", b);
// 左移操作符在移动时可能会改变符号
short c = 16385; // 0100 0000 0000 0001
short d = c << 1;
// c的补码 == 0100 0000 0000 0001 << 1
// d的补码 == 1000 0000 0000 0010 得到负数
// d的反码 == 1000 0000 0000 0001
// d的原码 == 1111 1111 1111 1110 == -32766
printf("d = %d\n",d)
short e = -1;
short f = e << 1;
// e的原码 == 1000 0000 0000 0001
// e的反码 == 1111 1111 1111 1110
// e的补码 == 1111 1111 1111 1111 << 1
// f的补码 == 1111 1111 1111 1110
// f的反码 == 1111 1111 1111 1101
// f的原码 == 1000 0000 0000 0010 == -2
return 0;
}
注意
移位操作符,不要移动负数位,这个标准是未定义的。
int a = 10;
int b = a >> -1; // 未定义,在VS2019中结果为0
位操作符
位操作符:按位与&
按位或|
按位异或^
,
位操作符,同样是操作补码得二进制位,并且只能操作整数。
按位与&
同二进制位都为1
时得1
,其余情况得0
二进制数1 |
二进制数2 |
按位与& 结果 |
---|