原码、反码、补码
补码怎么来的借鉴b站视频
对于无符号数(unsigned)来说,没有符号位,所有位都是有效位。
以下是有符号数的情况。
整数的二进制表示形式有三种:原码、反码、补码。
正数:原码 = 反码 = 补码。
负数则需要计算。
符号位(最高位)0为正数,1为负数。
1.原码:直接写出二进制序列。
以一个字节为例:
2.但我们发现原码正负相加不正确。2+(-2)=-4
,这显然不对啊。
所以,我们通过绝对值相同的正负数相加等于0得到了负数的补码
负数的反码:原码的符号位不变,其他位按位取反。
负数的补码:反码的二进制+1.
原码还可以直接通过补码除了符号位以外取反 再加1得出。
加1取反把加的1变成0,相当于减1,之后加上1就行
P.S.
- 0可以当成无符号数(都是0)。
- 内存中存的是补码的二进制序列
- 整数计算时用的也是补码
移位操作符(都是对补码进行操作,原数不会变)
1. 左移操作符:<<
- 左边丢弃,右边补0
- 原数不会变
- 相当于×2(不要越界)
以int类型的2和-2为例(32bit位)
2 <<1==4
-2 <<1 == -4
2.右移操作符:>>
右移分为逻辑右移和算术右移:(大部分编译器是算术右移)
- 逻辑右移,最高位直接补0,右边丢弃。
- 算术右移,最高位补原符号位,右边丢弃。
原数不会变
相当于÷2(不要越界)
⚠警告
对于移位操作符,不要移动负数位
int num=10;
num>>-1;//error
位操作符(都是对补码进行操作)
种类
1.按位与(&):
2.按位或(|)
3.按位异或(^)
1.两个相同的数异或的结果一定为0。(a^a== 0)
2.任何数与0异或都等于它自己。(a^0== a)
3.异或满足交换律;
用法
(1)不能创建临时变量,实现两个数的交换:
方法一(缺点:当 a 和 b 很大时,把 a+b 的结果放在 a 中,会导致int类型存放不下溢出而发生截断,从而损失精度。)
方法二 根据按位异或(^)的性质(可读性差,效率低,一般都是用创建临时变量的方法)
相当于a变成了中间变量。
(2)指定二进制位置,把0变成1再改回去
(3)求整数在内存中二进制的1的个数
方法一(利用十进制与二进制的转换)
- 这种方法其实是有问题的,它不能求负数二进制中1的个数。(负数%2不可能==1)
- 但是可以一开始就用无符号整型来接收或者隐式类型转换,这样所有位都是有效位,负数的补码变成原码,求的是很大正数二进制中1的个数
1.无符号整型来接收
2.隐式类型转换
3.强制类型转换(错误的案例)只有强制类型转换的n才变成无符号类型,原本的n没变。
方法二(右移操作符:>>加上按位与:&)- 一个整数按位与(&)上一个1得到的结果就是该数最低二进制位所代表的数。
- 右移操作符(>>)可以让一个数的任意二进制位来到最低位。
方法三(效率最高,最推荐)
无论正负,减1相当于补码减1,然后与原数按位与。每次循环去掉最低位的那个1。- 前面提到过移位操作符的操作数不会变(不是>>=),所以方法二的n不会变。
- 这个效率最高(有多少1执行几次循环),但会改变原数(所以可以封装成函数用形参来计算)
逻辑操作符
- 逻辑与(&&)
- 逻辑或(||)
注意:逻辑操作符在特定情况下会发生"短路"。
当条件1 && 条件2&&条件3……,若条件1为假时,此时整个逻辑表达式直接为假,条件2以及之后的条件将不会被执行。
当条件1 || 条件2 || 条件3……,若条件1为真时,此时整个逻辑表达式直接为真,条件2以及之后的条件将不会被执行。
德·摩根定理
x && y 和 ! ( ! x || ! y )相等
x || y 和 ! ( ! x && ! y )相等
接下来看例题
a++是后置++,先使用后++,所以表达式为假,后面不执行,i为0。
++b为真,后面不执行。i的值不为任何操作数的值,为真是1,为假是0。
sizeof
- 计算类型或类型创建的变量占用内存的大小,单位是字节
- sizeof计算的结果是size_t类型
size_t是无符号整型
对size_t类型的数据进行打印,可以使用%zd
- sizeof后面括号中不是类型的时候,括号可以省略。这说明sizeof不是函数。
- 所得出的值在编译期确定
++没有被执行。
逗号运算符 ( , )
逗号运算符 ( , )是C语言运算符中优先级最低的一种运算符,结合顺序是从左至右,用来顺序求值(最后一个逗号后面表达式的值作为整个表达式的值)
举例说明
逗号运算符的优先级低于赋值运算符,所以c先被赋值,a>b为假,表达式为0,所以c被赋值成0。
括号的优先级高于赋值运算符,所以先算括号内的表达式,此时计算结果为最后一个表达式的值