浅析位运算符(左移、右移、与、或、异或)

浅析位运算符(左移、右移、与、或、异或)位运算符 左移 右移 与 或 异或 左移运算符

大家好,欢迎来到IT知识分享网。

        C语言是一种很奇妙的语言,它既有高级语言的特点,又有低级语言的特点,支持位运算让它更方便于硬件编程。

一、左移运算符(<<)

        左移运算就是将一个二进制位的操作数按指定位数整体向左移位,移出位被丢弃(是否丢弃也不一定,得看接收结果的数据类型范围),右边的空位一律补0。

浅析位运算符(左移、右移、与、或、异或)

语法:x << n,其中 x 是要移动的数字,n 是要移动的位数。

关联的数学公式:位左移结果 = 要移动的数字 * 2的n次方(n 是要移动的位数)。该公式不是总有效啊!

1、正数左移举例:

完整代码在后面。

这里我们用char类型,因为它范围是1个字节,8bit位,方便查看结果。

结果如下:

浅析位运算符(左移、右移、与、或、异或)

浅析位运算符(左移、右移、与、或、异或)

==== 下面是重点 ====

结果如下:

浅析位运算符(左移、右移、与、或、异或)

浅析位运算符(左移、右移、与、或、异或)

怎么样,没变负数吧,也就是说左边移动的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、负数左移举例:

        注意:负数在内存中存储的是补码,对补码进行左移,再换算成原码才是我们要的结果。挺闹心的啊!但没办法,这不是我们能决定的。

结果如下:

浅析位运算符(左移、右移、与、或、异或)

结果如下:

浅析位运算符(左移、右移、与、或、异或)

======== 重点又到了 ========

结果如下:

浅析位运算符(左移、右移、与、或、异或)

结果如下:

浅析位运算符(左移、右移、与、或、异或)
        怎么样,没变正数吧,也就是说左边移动的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; }

    总结:左移运算时正数可能会变负数,负数也可能会变正数,左移的位数也未必舍弃,就看你用什么类型接收它,这个要注意啊。

二、右移运算符(>>)

    1、正数右移举例:

结果如下:

浅析位运算符(左移、右移、与、或、异或)

完整代码

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、负数右移举例:

结果如下:

浅析位运算符(左移、右移、与、或、异或)

完整代码:

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位(强转) char r44 = (unsigned char)ch >> 4; printf("(unsigned char)-10 >> 4 = %d\n", r44); // 负数无论右移多少次,-1也就是到头了啊! short a = -1207; printf("-1207 >> 100 = %d\n", -1207 >> 100); return 0; }

三、与运算符(&)

int main() { char a = 10; char b = 3; char c = a & b; printf("10 & 3 = %d\n", c); return 0; }

结果如下:

浅析位运算符(左移、右移、与、或、异或)
那么与(&)到底有什么用呢?
我们可以把二进制中的0与1当作电灯的开关(0-关灯,1-开灯),1个字节中的8bit当作8个开关,与(&)可以控制指定位置的开关为0,即关灯,其他位置不变。这点在电路上非常有用。
    认识关羽吗?想要灯(设置该bit位为0)吗?请用(与&),关二爷保佑你!
    比如说:
    二进制 0 1 0 0 1 1 0 1  -> 77(4D)
    我想让其第1、4、5位置为0(从右往左数),其它位置不变,只要让77(4D)和数-26(E6)【该数的1、4、5为0,其他位置位1】做与运算即可。





int main() { // 10进制进行与运算 char a = 77; char b = -26; char c = a & b; printf("10进制与运算:%d\n", c); // 16进制进行与运算 char a1 = 0x4D; char b1 = 0xE6; char c1 = a1 & b1; printf("16进制与运算:%X\n", c1); return 0; } 

结果如下:

浅析位运算符(左移、右移、与、或、异或)

任何一个数与0做与(&)运算,结果一定是0。

四、或运算符(|)

int main() { char a = 10; char b = 3; char c = a | b; printf("10 | 3 = %d\n", c); return 0; }

    结果如下:

浅析位运算符(左移、右移、与、或、异或)

    代码如下:

int main() { // 10进制进行或运算 char a = 77; char b = -94; char c = a | b; printf("%d\n", c); // 16进制进行或运算 char a1 = 0x4D; char b1 = 0xA2; char c1 = a1 | b1; //  printf("%X\n", (unsigned char)c1); // c1结果的符号位是1,表示负数,正常printf()输出是4个字节,那么另外3个字节会自动补符号位,即     -> FFFFFFEF,如果我们只想显示1个字节可以强转为unsigned char类型,变为无符号char型,不影响显示。 return 0; }

结果如下:

浅析位运算符(左移、右移、与、或、异或)

这里特意将符号位数字改为1,就是为了能练习一下负数的处理方式,否则用unsigned char变量会看起来更简单明了一些。

任何一个数与0做或(|)运算,结果一定是那个数。

五、异或运算符(^)

int main() { char a = 10; char b = 3; char c = a ^ b; printf("10 ^ 3 = %d\n", c); return 0; }

结果如下:

浅析位运算符(左移、右移、与、或、异或)

举一些关于异或的小例子:

    1、可以使用异或来交换两个变量的值(不允许创建第3个临时变量来帮忙)。

int main() { int a = 10; int b = 20; printf("交换前:a=%d b=%d\n", a, b); a = a ^ b; b = a ^ b; a = a ^ b; printf("交换后:a=%d b=%d\n", a, b); return 0; }

结果如下:

浅析位运算符(左移、右移、与、或、异或)

2、利用异或加解密

int main() { // 原理:pwd ^ key ^ key = pwd ^ (key ^ key) = pwd ^ 0 = pwd char pwd[] = "Lag234!@#"; char key = 100; // 秘钥 printf("加密前的密码:%s\n", pwd); int len = strlen(pwd); for (int i = 0; i < len; i++) { pwd[i] ^= key; // pwd ^ key } printf("加密后的密码:%s\n", pwd); for (int i = 0; i < len; i++) { pwd[i] ^= key; // pwd ^ key ^ key } printf("解密后的密码:%s\n", pwd); return 0; }

结果如下:

浅析位运算符(左移、右移、与、或、异或)

六、取反运算符(~)

取反很简单,就是二进制中的0变1,1变0。

~10 = ?   // 10的取反是多少?

~(-10) = ?  // -10的取反是多少?

int main() { char a = 10; char b = -10; printf("~10 = %d\n",~a); printf("~(-10) = %d\n", ~b); return 0; }

结果如下:

浅析位运算符(左移、右移、与、或、异或)

常见综合案例

1、计算一个二进制数里有几个1?

我们用char类型举例,1个字节,方便查看结果。

#include <stdio.h> int bitOneNum(char val) { int num = 0; char arr[8]; for (int i = 0; i < 8; i++) { char a = (val >> i) & 1; if (a == 1) { num++; } arr[i] = a; } for (int i = 7; i >= 0; i--) { printf("%d ",arr[i]); } return num; } int main() { printf("请输入一个数值(-128到127):"); int val; scanf("%d",&val); int num = bitOneNum((char)val); printf("\n您输入的数值是%d,它的二进制码包含 %d 个1。\n",val,num); return 0; }

显示结果:

浅析位运算符(左移、右移、与、或、异或)

代码解析:

以-23举例,二进制是,因为是负数,所以内存中存储的是补码(原码取反+1),二进制的位运算没有遍历功能,因此我们将该二进制依次向右移1位,然后和1进行与(&)运算,将其前面的位都置为0,结果就是第1位上的值了,共右移8次即可。

二进制就是这么遍历的。

2、消失的数字

// 消失的数字 int main() { // 有0到9连续10个数字,下面的数组中好像丢了1个,你知道是哪个吗? int arr[9] = {9,4,7,1,6,5,2,3,0}; int x = 0; for (int i = 0; i < 9; i++) { x = x ^ arr[i] ^ i; } x ^= 9; printf("丢失的数字是:%d\n",x); return 0; }

代码解析:

x = x ^ arr[i] ^ i; // 这个是重点也是难点
 我们先不要每次循序都计算x的值,把式子都保留在一起最后计算,每次循环后积累的式子结果如下:

x = 0 ^ 9 ^ 0 ^ 4 ^ 1 ^ 7 ^ 2 ^ 1 ^ 3 ^ 6 ^ 4 ^ 5 ^ 5 ^ 2 ^ 6 ^ 3 ^ 7 ^ 0 ^ 8 ^ 9   // 补充x ^= 9;因为数组只有9个数,差1个。

上面是数组的9次循环再补充 ^= 9的式子展开结果。每次循环用不用颜色标记一下能看得清楚些。

异或就是消消乐,把相同的消掉,结果就是

x = 0 ^ 8 = 8  // OK

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/120475.html

(0)
上一篇 2025-10-29 15:20
下一篇 2025-10-29 15:33

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信