【C 语言】简单通俗掌握位运算符

文中整数都以Int8为例,Int8大小为1字节,区间值为-128至127

8比特位 = 1符号位 + 7数值位

即二进制表示为:


-128

至

127

多说几句
符号位0正数1负数

127 = 26 + 25 + 24 + 23 + 22 + 21 + 20
-128 = - (27 - |0|)

二进制转十进制
正数的二进制转十进制不用多说了。
负数的二进制转十进制时符号位是 1 表示负数,2n 减去数值位十进制值的绝对值(n为位数)。

再举个栗子:
- (27 - |26 + 25 + 24 + 22 + 20|) = -10

-10

十进制转二进制
正数的十进制转二进制不用多说了。
负数的十进制转二进制是正数的补码。

原码 反码 补码


位与运算符(&)

两个数进行位与运算,每一个比特位依次计算,只有数值位对应的两个二进制值都是 1 的时候才是 1

&


位或运算符(|)

&同理,|是只要数值位对应的两个二进制值任意一个是 1 的时候就是 1

|


位异或运算符(^)

&同理,^是只有数值位对应的两个二进制值不相等时是 1

^


位取反运算符(~)

一个数进行位取反运算,每一个比特位依次取反,即10

~

可以知道~就是原码转反码的过程。


位左移运算符(<<)

位左移运算简单来说是一个数所有数值位的二进制值向左移动确定位数。

  1. 无符号整数 例如:10<<5

    10<<5

  2. 有符号整数 例如:-10<<5

    -10<<5.png

  • 所有比特位按指定的位数进行左移。
  • 空白位用0补充。
  • 超出整形存储边界都舍弃,例如上图中Int8类型的10位左运算置灰部分应舍弃,值为64。若是Int64类型的10位左运算置灰部分不应舍弃,值为320
  • 可以看出一个数位左移 1后在整型存储范围内(没有舍弃部分)相当于将这个数x2
例如:
var i: Int = 20
print(I<<2)  
运行结果:80
 
即:20*2*2 = 80

位右移运算符(>>)

同理,位左移运算简单来说是一个数所有数值位的二进制值向右移动确定位数。

  1. 无符号整数 例如:10>>5

    10>>5.png

  2. 有符号整数 例如:-10>>5

    -10>>5

  • 所有比特位按指定的位数进行右移。
  • 无符号整数空白位用0补充,有符号整数空白位用符号位数值补充。
  • 超出整形存储边界都舍弃。
  • 可以看出一个数位左移 1后在整型存储范围内(没有舍弃部分)相当于将这个数/2,若位左移导致结果<1无符号整数结果为0有符号整数中的负数结果为-1
例如:
var i: Int =20
print(i<<2)
运行结果:5

即:20/2/2 = 5

几个常见的位运算符操作总结

  • 两个数 ^ 得到的结果,再 ^ 其中任意一个数,能得到另一个数。
  • 两个相等的数 ^ 得到的结果为 0。
  • 0 和任何一个数 ^ 得到的都是这个数本身。

应用: 交换两个变量的值

var a = 1
var b = 2
a = a ^ b
b = a ^ b
a = a ^ b
print("a = \(a), b = \(b)")

  • i & 1 可以判断 i 最后一位是不是 1。

应用:求无符号整数二进制中 1 的个数

func countOfOnes(num: UInt) -> UInt {
    var count: UInt = 0
    var temp = num
    while temp != 0 {
        count += temp & 1
        temp = temp >> 1
    }
    return count
}
var a: UInt = 3
print(countOfOne(num: a))

  • i & (i - 1) 消除 i 最后一位的 1。

应用:判断无符号整数是否为 2 的整数次幂

func isPowerOfTwo(num: UInt) -> Bool {
    return (num & (num - 1)) == 0
}
print(isPowerOfTwo(num: 2))

  • i & j == 0 可以知道 i 的 第 j 位不是 1。

应用:分组

func divideTwoGroups(nums: [UInt], flag: UInt)  {
    var lostNum1Array: Array<Int> = []
    var lostNum2Array: Array<Int> = []

    for num in nums {
        if (num & flag) == 0 {
            lostNum1Array.append(Int(num))
        } else {
            lostNum2Array.append(Int(num))
        }
    }
    print(lostNum1Array)
    print(lostNum2Array)
}
divideTwoGroups(nums: [1,2,3,3,5,8,9,9], flag: 2)

  • (i >> j) & 1 == 1 可以知道 i 右移 j 位后等于 1。

应用:给定一个集合,返回这个集合所有的子集

func getSubsets<T>(_ set: Set<T>) -> Array<Set<T>> {
    // 1 << set.count 相当于 2^set.count 
    let count = 1 << set.count
    let elements = Array(set)
    var subsets = [Set<T>]()
    for i in 0..<count {
        var subset = Set<T>()
        for j in 0..<elements.count {
            // i = 5, j = 0时  101 >> 0  =>  101 & 001  =>  001 满足
            // i = 5, j = 1时  101 >> 1  =>  010 & 001  =>  000 不满足
            // i = 5, j = 2 时 101 >> 0  =>  001 & 001  =>  001 满足
            if ((i >> j) & 1) == 1 {
                subset.insert(elements[j])
            }
           // subset : ["A", "B"]
        }
        subsets.append(subset)
    }
    return subsets
}
print(getSubsets(["A", "B", "C"]))
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。