????????C语言是一种很奇妙的语言,它既有高级语言的特点,又有低级语言的特点,支持位运算让它更方便于硬件编程。
????????左移运算就是将一个二进制位的操作数按指定位数整体向左移位,移出位被丢弃(是否丢弃也不一定,得看接收结果的数据类型范围),右边的空位一律补0。
语法:x << n,其中 x 是要移动的数字,n 是要移动的位数。
关联的数学公式:位左移结果 = 要移动的数字 * 2的n次方(n 是要移动的位数)。该公式不是总有效啊!
完整代码在后面。
这里我们用char类型,因为它范围是1个字节,8bit位,方便查看结果。
?? ??? ?char ch = 10; ????????????????// 00001010
?? ??? ?// 左移1位
?? ??? ?char r1 = ch << 1; ????????// 00010100
?? ??? ?printf("10 << 1 = %d\n", r1);?? ?// 20 (10*2的1次方)
结果如下:
? ? ? ? // 左移2位
?? ??? ?char r2 = ch << 2; // 00101000
?? ??? ?printf("10 << 2 = %d\n", r2);?? ?// 40 (10*2的2次方)
?? ??? ?// 左移3位
?? ??? ?char r3 = ch << 3; // 01010000
?? ??? ?printf("10 << 3 = %d\n", r3);?? ?// 80 (10*2的3次方)
?? ???结果如下:
==== 下面是重点 ====
左移4位-->关键地方到了,因为左移4位后最左边是1了,也就是说符号位为1,可能变负数了。
?? ??? ?char r4 = ch << 4; // 10100000,转换为十进制为 -32,但真的是-32吗?
????????printf("10 << 4 = %d\n", r4);
结果如下:
? ? ? ? 答案是-96,正数左移真的有可能变成负数了,但为什么不是-32呢。
?? ??? ?因为数值在内存中存储的补码,正数的原码、反码、补码是一样的。负数的补码=原码取反+1。
?? ??? ?我们对内存中的数左移时实际上都是对补码进行左移,实际取值时还要转换为原码的。
?? ??? ?10100000,符号位是1,负数,取原码时要先减去1,再取反才行。
?? ??? ? ? ? 10100000
?? ??? ? ?-? ? ? ? ? ? ? ? 1?? ?
?? ??? ? ?-----------------
? ? ? ? ? ? ? 10011111?
? ? ?取反 ?11100000 -> -96
?? ??? ?原码、反码、补码不懂的可以看我以前写的文章。
?? ??? ?10 << 4 一定是负数吗?看下面代码
?? ??? ?short r44 = ch << 4;
?? ??? ?printf("10 << 4 = %d\n", r44);?? ?// 160 (10*2的4次方)
怎么样,没变负数吧,也就是说左边移动的4位并没有被舍弃,这是因为我们用short类型来接收的,该类型为2个字节,你左移8位都能完整接收,更何况仅仅是左移4位了,因此不会舍弃。
完整代码如下:
int main()
{
char ch = 10;
// 左移1位
char r1 = ch << 1;
printf("10 << 1 = %d\n", r1);
// 左移2位
char r2 = ch << 2;
printf("10 << 2 = %d\n", r2);
// 左移3位
char r3 = ch << 3;
printf("10 << 3 = %d\n", r3);
// 左移4位
char r4 = ch << 4;
printf("10 << 4 = %d\n", r4);
// 左移4位
short r44 = ch << 4;
printf("10 << 4 = %d\n", r44);
return 0;
}
?? ??? ?注意:负数在内存中存储的是补码,对补码进行左移,再换算成原码才是我们要的结果。挺闹心的啊!但没办法,这不是我们能决定的。
? ? ? ? char ch = -10; // 原码10001010 --> 补码 11110110 (负数要看补码啊!!!!!!!!!)
?? ??? ?// 左移1位
?? ??? ?char r1 = ch << 1;?? ?// 补码11101100 --> 原码 10010100
?? ??? ?printf("-10 << 1 = %d\n", r1);?? ?// -20 (-10*2的1次方)
结果如下:
? ? ? ? // 左移2位
?? ??? ?char r2 = ch << 2;?? ?// 补码 11011000 --> 原码 10101000
?? ??? ?printf("-10 << 2 = %d\n", r2);?? ?// -40 (-10*2的2次方)
?? ??? ?// 左移3位
?? ??? ?char r3 = ch << 3;?? ?// 补码 10110000 --> 原码 11010000
?? ??? ?printf("-10 << 3 = %d\n", r3);?? ?// -80 (-10*2的3次方)
结果如下:
======== 重点又到了 ========
????????左移4位-->关键地方又到了,因为左移4位后最左边是0了,也就是说符号位为0,可能变正数了。
?? ??? ?char r4 = ch << 4;?? ?// 补码 01100000 --> 原码 01100000
?? ??? ?printf("-10 << 4 = %d\n", r4);?? ?// 96 (数学公式不好用了)
结果如下:
? ? ? ? -10 << 4 一定是正数吗?看下面代码
?? ??? ?short r44 = ch << 4;
?? ??? ?printf("-10 << 4 = %d\n", r44);?? ?// -160 (-10*2的4次方)
结果如下:
?? ??? ?怎么样,没变正数吧,也就是说左边移动的4位并没有被舍弃,这是因为我们用short类型来接收的,该类型为2个字节,你左移8位都能完整接收,因此不会舍弃。
完整代码如下:
int main()
{
char ch = -10;
// 左移1位
char r1 = ch << 1;
printf("-10 << 1 = %d\n", r1);
// 左移2位
char r2 = ch << 2;
printf("-10 << 2 = %d\n", r2);
// 左移3位
char r3 = ch << 3;
printf("-10 << 3 = %d\n", r3);
// 左移4位
char r4 = ch << 4;
printf("-10 << 4 = %d\n", r4);
// 左移4位
short r44 = ch << 4;
printf("-10 << 4 = %d\n", r44);
return 0;
}
? ? 总结:左移运算时正数可能会变负数,负数也可能会变正数,左移的位数也未必舍弃,就看你用什么类型接收它,这个要注意啊。