c语言学习之位运算知识总结和实例分析

先给大家讲个笑话吧,说世界上有10种人,一种知道二进制,而另一种不知道二进制。
位运算的基础就是二进制。

二进制的位运算包括五种运算:

与,或,异或,左移,右移
与(&) 0&0 = 0,0&1 = 0;1&0 = 0;1&1 = 1;
或(|) 0|0 = 0, 0|1 = 1; 1|0 = 1; 1|1 = 1;
异或(^)0^0 =0; 0^1 = 1; 1^0 = 1; 1^1 = 0;
左移(<<) 00001001 << 2 =00100100
右移(>>) 10001010 >> 3 = 00010001
前面已经提到过,右移分为算术右移和逻辑右移,主要在于无符号数和有符号数的区别。
逻辑右移:右移后,左端补零
00001010 >> 2 = 00000010
算术右移:右移后,左端补最高位数
有符号数10001010 >>3 = 11110001
对于无符号数按照逻辑右移,有符号数一般为算术右移

1.位运算的特点

1,按位与(&)
清零特定位,10001010 & 01111111 = 00001010 (首位清零)
取指定位, 10001010 & 10000000 = 10000000 (取首位)
2,按位或(|)
将某一位置置一,10001010 | 00000001 = 10001011 (末尾置一)
3,位异或(^)
使特定位取反 ,10001010 ^ 10001010 = 00001010 (首位取反)
不引入第三变量,交换两个变量的值
a = a^b ;b = a^b;a = a^b

4,补码运算
-x = ~x +1

2.位运算的应用

应用举例:
1,判断int型变量a 是奇数还是偶数
a & 1 = 0 偶数(末尾为零)
a & 1 = 1 奇数
2,取变量的第k位 a>> k & 1
3,将变量的第k位清零,a = a& ~(1<< k)
4,将变量的第k 位置1,a = a |(1<< k)
5,将变量循环左移k次,就是从左移出的从右端进,a = a << k | a >>16-k 设(sizeof(int) = 16);
6,整数的平均值
对于两个整数x,y ,如果用(x + y)/2求平均值,会产生溢出,因为x+y可能超出范围。
int average(int x,int y)
{
return (x&y) +((x^y) >> 1);
}
7,乘法转换为位运算
a *(2^n) = a << n
8,除法运算转换为位运算
a/(2^n) = a>> n

3.由计算绝对值引出补码,原码,反码,有符号数的讨论

int abs(int x)
{
int y;
y = x>> 31; (若x为负数,1111111111111111)有符号数为-1
return (x^y)-y; //
假如x= -2;(1111111111111110)
y= 1111111111111111
x^y = 0000000000000001
x^y-y = 0000000000000010 表示+2
如果求原码:如果是求原码,符号位不变,其他位按位取反,再加一
~x=1000000000000001
~x+1 = 1000000000000010
原码:符号+值,表示-2。
上述求绝对值方法和求原码的方法类似,相当于是把原码的符号位从1变为零,如果是正数,则不变,这一些都是由x>>31,获得符号位开始。正数的补码=原码=绝对值
注意:原码和补码只是有符号数的两种不同的表示方式。
求补码:
若x < 0,x = x+2^w; 若x> 0,x = x
x = -1,补码为(1111111111111111),无符号数表示的 Umax和补码表示的-1有相同的位模式。

4.c与指针第五章编程练习三

题目:请编写函数
unsigned int reverse_bit(unsigned int value);
这个函数的返回值是把value的二进制模式从左到右变换一下后的值,例如在32位机器上,25这个值包含下列各个位:
00000000000000000000000000011001
函数的返回值是2550136832,它的二进制模式是:
10011000000000000000000000000000
编写函数时要注意不要让它依赖于你的机器上整型值的长度

unsigned int reverse_bit(unsigned int value)
{
    unsigned int answer;
    unsigned int i;
    answer = 0;
    for(i = 1;i != 0;i <<=1)  //通过是1一直左移到越过最高位,不依赖机器长度
    {
        answer << 1;   //左移挪出最右端位置
        if(value & 1 != 0) //如果value最后一位为1
            answer |= 1;   //就把这个1放在answer的最右端
        value >> 1;   //value已经被挪走的数就丢弃掉
    }
    return answer;
}
//利用的是从value右端出数,然后从answer右端进数,然后再左移,就将二进制位模式顺序改变。

5.剑指offer 面试题10 求二进制中1的个数

题目:请实现一个函数,输入一个整数,输出该数二进制表示中1的个数,例如把9表示成二进制是1001,有2位1
常规思路:将整数右移一次,然后&1,判断最后一位是否为1,为1,则计数加1.
代码如下:

int NumberOf1(int n)
{
    int count =0;
    while(n)
    {
        if(n & 1 )
            count ++;
        n >>=1;
    }
    return count;
}

看起来似乎很合理,但是如果n为负数,作为有符号数,右移将在左端补1,一直右移,将陷入死循环。
为了避免死循环,我们可以不右移输入的数字n。

int NumberOf1(int n)
{
    int count = 0;
    unsigned int flag = 1;
    while(n)
    {
        if(n & flag)
            count++;
        flag << = 1;
    }
    return count;
}

32位的整数需要循环32次,效率不高
方案三:

int NumberOf1(int n)
{
    int count = 0;
    while(n)
    {
        ++count;
        n = (n-1) & n;
    }
    return count;
}

关键点就是把一个整数减去1之后再和原来的整数做位与运算,得到的结果相当于是把整数的二进制表示中的最右边一个1变成0.

  • 0
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值