C语言是一种很奇妙的语言,它既有高级语言的特点,又有低级语言的特点,支持位运算让它更方便于硬件编程。
一、左移运算符(<<)
左移运算就是将一个二进制位的操作数按指定位数整体向左移位,移出位被丢弃(是否丢弃也不一定,得看接收结果的数据类型范围),右边的空位一律补0。
语法:x << n,其中 x 是要移动的数字,n 是要移动的位数。
关联的数学公式:位左移结果 = 要移动的数字 * 2的n次方(n 是要移动的位数)。该公式不是总有效啊!
1、正数左移举例:
完整代码在后面。
这里我们用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;
}
2、负数左移举例:
注意:负数在内存中存储的是补码,对补码进行左移,再换算成原码才是我们要的结果。挺闹心的啊!但没办法,这不是我们能决定的。
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;
}
总结:左移运算时正数可能会变负数,负数也可能会变正数,左移的位数也未必舍弃,就看你用什么类型接收它,这个要注意啊。