移位操作符
移位操作符分为左移操作符和右移操作符,移位操作符的操作数必须为整数,移动的是二进制位。
数值15
15----十进制(1* 10^1)+(5*10 ^0 )
17----八进制(1 * 8^1)+(5 * 8 ^0)
……
整数的二进制表示分为原码、反码、补码,
其中正数的原反补相同,符号位为0是正数,为1是负数
15----假设是int型变量,int类型在内存中占用4字节,32个bit
00000000 00000000 00000000 00001111-原码
00000000 00000000 00000000 00001111-反码
00000000 00000000 00000000 00001111-补码
-15
10000000 00000000 00000000 00001111-原码
11111111 11111111 11111111 11110000-反码(原码符号位不变,其他位按位取反)
11111111 11111111 11111111 11110001-补码(反码基础上+1)
整数在内存中是以补码进行存放的,移位操作符通过补码进行运算
左移操作符
移动规则:二进制补码左边丢弃,右边补0(正数负数规则一样)
int a = 4;
00000000 00000000 00000000 00000100补码
int b = a<<1;
000000 00000000 00000000 000001000左移后的结果
a<<1表达式不会让a自身发生变化,a本身是4还是4,但是把表达式的值赋给b,这时b的值就是8,左移一位相当于*2。
int a = -4;
10000000 00000000 00000000 00000100-原码
11111111 11111111 11111111 11111011-反码
11111111 11111111 11111111 11111100-补码
int b = a<<1;
11111111 11111111 11111111 11111000(左移后结果)
printf("%d",b);//屏幕上显示的值是原码
11111111 11111111 11111111 11110111(左移后的补码转反码)
10000000 00000000 00000000 00001000(原码)
b在屏幕上的结果为-8
右移操作符
右移操作符的规则有两种
逻辑右移:右边舍弃左边补0
算术右移:右边舍弃左边补符号位
绝大数编译器采用算术右移!
int a = -4;
int b = a>>1;
10000000 00000000 00000000 00000100-原码
11111111 11111111 11111111 11111011-反码
11111111 11111111 11111111 11111100-补码
在补码的基础上右移
11111111 11111111 11111111 11111110(右移后的补码)
printf("%d",b);
10000000 00000000 00000000 00000001(反码)
10000000 00000000 00000000 00000010(原码)
b在屏幕上输出结果为-2
右移一位除以2
对于移位操作符禁止移位负数位如:
int a = 10;
a = a>>-1;❌
上面这种写法是错误的
位操作符
运算对象只能是整数
与运算
&运算规则:全1出1,有0出0,二进制补码运算
int a=3;
int b=2;
int c=a&b;
printf("%d",c);
为了方便8个比特位表示
00000011-a
00000010-b
结果:00000010-c
-->屏幕显示2
或运算
|运算规则:有1出1,全0出0,二进制补码运算
`c
int a=8;
int b=7;
int c=a|b;
printf("%d",c);
为了方便8个比特位表示
00001000-a
00000111-b
结果:00001111-c
-->屏幕显示15
异或运算
异或运算规则:相同为0,不同为1,二进制补码运算
`c
int a=12;
int b=2;
int c=a^b;
printf("%d",c);
为了方便8个比特位表示
00001100-a
00000010-b
结果:00001110-c
-->屏幕显示14
若结果补码为负数,输出是按原码输出,内存中存放的是补码
不使用中间变量实现两个数值交换
int main()
{
int a = 8;
int b = 2;
a = a^b;
b = a^b;
a = a^b;
分析:8^8=0,8^0=8
8^2^2=8
8^2^8=2
}
赋值操作符
int a = 10;
int b = 0;
b=a=b+1;
//连续赋值,虽然一条语句赋值完成,但不如分开赋值代码清晰,易于调试
非操作符
int x=0;
if(!x)//x==0
{
printf("haha");
}
else if(x)//x!=0
{
printf("hehe");
}
执行结果:haha
sizeof
int x;
int arr[10];
sizeof(x);//4
sizeof(arr);//40
sizeof(arr[10])//4 虽然下标超出,但是编译器还是会认为该元素是 int类型
short s = 10;
int a = 2;
printf("%d",sizeof(s=a+5);)//2,根据s类型判断大小
printf("%d",s);//10,sizeof内表达式不会计算
按位取反~
int a=0;
00000000 00000000 00000000 00000000-补码
11111111 11111111 11111111 11111111-~a
11111111 11111111 11111111 11111110
10000000 00000000 00000000 00000001
-1
逗号表达式
逗号表达式计算方向从左向右依次计算,整个表达式的结果是最后一个表达式的结果
int a=0;
int b=0;
int c=(a+3,b+=5,b+3);
c的结果就是3
整形提升
C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升
有符号数整形提升补符号位,无符号数补0,截断是在补码的基础上进行截断
结果为:1
4
4
4
解释:只要参与运算就会发生整形提升
sizeof表达式不是运算吗?
sizeof是不产生计算结果,也就是说括号内的操作并不会真的对相应表达式内的元素所在地址的值产生改变,总的来说会开辟一个新空间去计算表达式的值提供给sizeof,计算完就释放空间,不会对参数有影响。