c语言提供了6个位运算,C语言位运算详解

main()

{

int a=0113755;

printf("%d",a>>1);

}

7、位运算赋值运算符

位运算符与赋值运算符可以组成复合赋值运算符。

例如: &=, |=, >>=, <<=, ∧=

例: a & = b相当于 a = a & b

a << =2相当于a = a << 2

位运算的应用和实例

分类: 编程之美2011-08-27 20:13 2050人阅读 评论(2) 收藏 举报

位运算应用口诀

清零取数要用与,某位置一可用或

若要取反和交换,轻轻松松用异或移位运算

要点 1 它们都是双目运算符,两个运算分量都是整形,结果也是整形。

2 "<>"右移:右边的位被挤掉。对于左边移出的空位,如果是正数则空位补0,若为负数,可能补0或补1,这取决于所用的计算机系统。

4 ">>>"运算符,右边的位被挤掉,对于左边移出的空位一概补上0。

位运算符的应用 (源操作数s 掩码mask)

(1) 按位与-- &

1 清零特定位 (mask中特定位置0,其它位为1,s=s&mask)

2 取某数中指定位 (mask中特定位置1,其它位为0,s=s&mask)

(2) 按位或-- |

常用来将源操作数某些位置1,其它位不变。 (mask中特定位置1,其它位为0 s=s|mask)

(3) 位异或-- ^

1 使特定位的值取反 (mask中特定位置1,其它位为0 s=s^mask)

2 不引入第三变量,交换两个变量的值 (设 a=a1,b=b1)

目标 操作 操作后状态

a=a1^b1 a=a^b a=a1^b1,b=b1

b=a1^b1^b1 b=a^b a=a1^b1,b=a1

a=b1^a1^a1 a=a^b a=b1,b=a1

a ^= b

b ^= a

b ^= b

这样3步,即可交换两个数字

且没有占用空间.

二进制补码运算公式:

(看到这些功能,似乎没必要了解补码的原理)

-x = ~x + 1 = ~(x-1)

~x = -x-1

-(~x) = x+1

~(-x) = x-1

x+y = x - ~y - 1 = (x|y)+(x&y)

x-y = x + ~y + 1 = (x|~y)-(~x&y)

x^y = (x|y)-(x&y)

x|y = (x&~y)+y

x&y = (~x|y)-~x

x==y: ~(x-y|y-x)

x!=y: x-y|y-x

x< y: (x-y)^((x^y)&((x-y)^x))

x<=y: (x|~y)&((x^y)|~(y-x))

x< y: (~x&y)|((~x|y)&(x-y))//无符号x,y比较

x<=y: (~x|y)&((x^y)|~(y-x))//无符号x,y比较

应用举例

(1) 判断int型变量a是奇数还是偶数

a&1 = 0 偶数

a&1 = 1 奇数

(2) 取int型变量a的第k位 (k=0,1,2……sizeof(int)),即a>>k&1 (先右移再与1)

(3) 将int型变量a的第k位清0,即a=a&~(1<>16-k (设sizeof(int)=16)

(6) int型变量a循环右移k次,即a=a>>k|a<<16-k (设sizeof(int)=16)

(7)、整数的平均值

对于两个整数x,y,如果用 (x+y)/2 求平均值,会产生溢出,因为 x+y 可能会大于INT_MAX,但是我们知道它们的平均值是肯定不会溢出的,我们用如下算法:

[cpp] view plaincopy

1int average(int x, int y) //返回X、Y的平均值

2{

3 return (x & y) + ( (x^y)>>1 );

4 //x&y 取出x和y二进制都为 1 的所有位,这是x、y都为 1 的部分,因为相同,所以直接加就行了

5 //x^y x和y中有一个为 1 的所有位

6 //后者是x为 1,y为 0的部分,以及y为 1,x为 0 的部分,两部分加起来除以2,然后跟前面的相加就可以了

7}

(8)对于一个数 x >= 0,判断是不是2的幂。

[cpp] view plaincopy

8boolean power2(int x)

9{

10 return ( (x&(x-1))==0) && (x!=0);

11}

(9)不用temp交换两个整数

[cpp] view plaincopy

12void swap(int x , int y)

13{

14 x ^= y;

15 y ^= x;

16 x ^= y;

17}

(10)计算绝对值

[cpp] view plaincopy

18//因为i为0或-1,所以减i即是要么加0要么加1

19int my_abs(int a) //正数的时候,比较好理解

20{

21 int i = a >> 31; //负数的时候,右移数值位补进符号位,导致32个bit都是1,也就是-1,此时 i 为-1

22 return ( (a ^ i) - i); //与 -1 即0xFFFFFFFF异或就相当于取反,然后减去-1,就相当于加1,也就是取反加1,

23}

(11)取模运算转化成位运算 (在不产生溢出的情况下)

a % (2^n) 等价于 a & (2^n - 1)

(12)乘法运算转化成位运算 (在不产生溢出的情况下)

a * (2^n) 等价于 a<< n

(13)除法运算转化成位运算 (在不产生溢出的情况下)

a / (2^n) 等价于 a>> n

例: 12/8 == 12>>3

(14) a % 2 等价于 a & 1

(15) if (x == a)

x= b;

else x= a;

等价于 x= a ^ b ^ x;

(16) x 的 相反数 表示为 (~x+1)

(17)输入2的n次方:1 << 19

(18)乘除2的倍数:千万不要用乘除法,非常拖效率。只要知道左移1位就是乘以2,右移1位就是除以2就行了。比如要算25 * 4,用25 << 2就好啦

实例

功能 | 示例 | 位运算

----------------------+---------------------------+--------------------

去掉最后一位 | (101101->10110) | x >> 1

在最后加一个0 | (101101->1011010) | x < < 1

在最后加一个1 | (101101->1011011) | x < < 1+1

把最后一位变成1 | (101100->101101) | x | 1

把最后一位变成0 | (101101->101100) | x | 1-1

最后一位取反 | (101101->101100) | x ^ 1

把右数第k位变成1 | (101001->101101,k=3) | x | (1 < < (k-1))

把右数第k位变成0 | (101101->101001,k=3) | x & ~ (1 < < (k-1))

右数第k位取反 | (101001->101101,k=3) | x ^ (1 < < (k-1))

取末三位 | (1101101->101) | x & 7

取末k位 | (1101101->1101,k=5) | x & ((1 < < k)-1)

取右数第k位 | (1101101->1,k=4) | x >> (k-1) & 1

把末k位变成1 | (101001->101111,k=4) | x | (1 < < k-1)

末k位取反 | (101001->100110,k=4) | x ^ (1 < < k-1)

把右边连续的1变成0 | (100101111->100100000) | x & (x+1)

把右起第一个0变成1 | (100101111->100111111) | x | (x+1)

把右边连续的0变成1 | (11011000->11011111) | x | (x-1)

取右边连续的1 | (100101111->1111) | (x ^ (x+1)) >> 1

去掉右起第一个1的左边 | (100101000->1000) | x & (x ^ (x-1))

判断奇数 (x&1)==1

判断偶数 (x&1)==0

详细给你解释下:

/*************************************/

负数在位移操作中以补码表示

正负的0补还是0

正数补码原来一样如

64原码、补码,反码0111110

负数:

-64原码1111110

反1000001(符号不变)

补=反码+1:1000010

*///

12的二进制如下: 00001100

取反后: 11110011 这是一个负数的补码形式,但这是哪个负数的补码呢?

我们先看看负数的补码如何表示的。【负数的补码是对其原码逐位取反,但符号位除外;然后整个数加1。】

我们返回去弄:

先把11110011-1=11110010

然后符号位以外取反:10001101

看看除符号外的数:0001101 是13 所以这个数是-13

所以:~12=-13

这是百科上的相关知识点:

【求-7的补码。 】

因为给定数是负数,则符号位为“1”。

后七位:-7的原码(10000111)→按位取反(11111000)(负数符号位不变)→加1(11111001)

所以-7的补码是11111001。

已知一个数的补码,求原码的操作分两种情况:

(1)如果补码的符号位为“0”,表示是一个正数,其原码就是补码。

(2)如果补码的符号位为“1”,表示是一个负数,那么求给定的这个补码的补码就是要求的原码。

再举一个例子:求-64的补码

+64:01000000

11000000

优先权操作者描述关联

1::范围解析左到右

2+ + -后缀/ postfix的递增和递减

()函数调用

[]数组下标

。元素选择参考

- >通过指针元素的选择

3+ + -前缀增量和减量从右到左

+ -一元加号和减号

! 〜逻辑不和位不

(类型)类型转换

*间接(取值)

&地址

SIZEOF尺寸

新的,新的[]动态内存分配

删除,删除[]动态内存释放

4* - > *成员指针左到右

5* / %乘法,除法和余数

6+ -加法和减法

7<< >>按位左移和右移

8< <= =关系运算符>和≥

9== !对于关系=和≠分别

10&按位与

11^按位XOR(异或)

12|按位OR(含)

13&&逻辑与

14| |逻辑或

15?三元条件从右到左

16=直接分配(默认为C + +类)

+ = - =转让和差

* = / = %=转让的产品,商数和余数

<< = >> =按位左移和右移的分配

&= ^ = | =分配按位AND,XOR和OR

17扔抛出运营商(例外抛出)

18,逗号左到右

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值