C语言基础 | 位操作

目录标题:

  • 一、按位逻辑运算符
        • 按位与:&
            • 用法:掩码
        • 按位取反:~
            • 用法:清空位(设置为0)
        • 按位或:|
            • 用法:置位(设置为1)
        • 按位或:^
            • 用法:切换位
        • 移位运算符:
            • `移位用法总结:`
            • number << n number乘以2的n次幂
            • number >> n 如果number为非负数,则用number除以2的n次幂
  • 二、位字段

一、按位逻辑运算符

4个按位逻辑运算符都用于整数数据,包括char 。之所以叫做按位运算,是因为这些操作都是针对每一个位进行的。不影响它左右两边的位。不能混淆于逻辑运算符(&& 、 || 和 !)

  • 按位与:&

(10010011) & (00111101)    //表达式
(00010001)                 //结果值
用法:掩码

所谓掩码指的是一些设置为开(1)或者关(0)的组合位。例如:假设定义符号常量MASK为2(即,二进制为:00000010

flags = flags & MASK;   //等价于flags &= MASK
//00000010 = 10010010 & 00000010  

像这样的过程除了MASK中的一位为1以外其他的数都是0,我们就称为“使用掩码

  • 按位取反:~

  ~(10011010)    //表达式
   (01100101)    //结果值
用法:清空位(设置为0)

1.清空位就是在不影响其它位的情况下关闭指定的某一位。
2.例如:假设flags是00001111,MASK是10110110,利用且取反运算flags00001111 & ~ MASK10110110结果为:10111111
其中MASK为1的位在结果中都被设置(清空)为0,flags中与MASK为0的位相应的位在结果中都为改变。。简化运算代码为:flags &=~ MASK

  • 按位或:|

(10010011) | (00111101)    //表达式
(10111111)                 //结果值
用法:置位(设置为1)

1.置位就是只打开需要的特定位,同时保持其它位不变。
2.例如:假设flags是00001111,MASK是10110110,利用或运算的flags00001111 | MASK10110110结果为:10111111
其中MASK为1的位,flags与其对应的位也为1。MASK中为0的位,flags与其对应的位保持不变。简化运算代码为:flags =| MASK

  • 按位或:^

(10010011) ^ (00111101)    //表达式
(10101110)                 //结果值
  • 用法:切换位

    切换位指的是打开以关闭的位,或者关闭以打开的位。可以使用异或运算符^。
    结果flags ^= MASK

  • 移位运算符:

    • 左移<<
      假定stonk为 1,那么stonk<<2为 4 ,但是stonk本身不变,仍然为 1 。可以用左移赋值运算符<<=来更改变量的值。
    int stonk = 1;
    int onkoo;
    onkoo = stonk <<= 2;      //把 4 赋给onkoo;
    stonk <<= 2;              //把stonk的值改为 4
     
    
    • 右移>>
      右侧运算对象移出有末端位的值丢,对于无符号类型,用0填充空出的位置;对于有符号类型,其结果取决于机器。空出的位可用0填充。
     int sweet = 16;
     int ooosw;
     ooosw = sweet >> 3;    //ooosw = 2,sweet的值仍然为16
     sweet >>= 3;           //sweet的值为2
     
    
移位用法总结:
number << n number乘以2的n次幂
number >> n 如果number为非负数,则用number除以2的n次幂

二、位字段

在C语言中用两种访问位的方法,除了上面的通过按位运算符外,还有一种就是在结构中创造位字段。位字段的结构体格式是在结构体中加入: 表示。
例如:声明建立一个 4 个 1 位的字段

struct{
	unsigned int A:1;
	unsigned int B:1;
	unsigned int C:1;
	unsigned int D:1;
}prnt;

根据该声明可以看出,我们平常使用结构是按类型的字节对齐。但是通过: 添加在结构体里面,我们可以控制位字段。同时还可以通过普通的结构成员运算符.来给这些字段赋值:

prnt.A = 0;
prnt B = 1;

但是要确保所赋的值不超过字段的可容纳范围。
例如下面的结构体字段


/*这里,在stuff,fiel_1和stuff.fiel_2之间,有一个2位的空隙;
 *stuff.fiel_3将存储在下一个unsigned int 中 */
struct {
	unsigned int fiel_1 : 1;
	unsigned int        : 1;
	unsigned int fiel_2 : 1;
	unsigned int        : 1;
	unsigned int fiel_3 : 1;
}stuff;

如果声明的总位数超过了一个unsigned int 类型的大小,则会用到下一个unsigned int 的类型存储位置。一个字段不允许跨越两个unsigned int 之间的边界。编译器后自动移动跨界的字段,保持unsigned int 的边界对称。一旦发生这种情况,第一个unsigned int 中会留下一个未命名的“洞”
即上面的代码就是使用未命名的字宽度“填充”未命名的“洞”。使用一个宽度为0的未命名字段迫使下一个字段与下一个整数对齐。

你可能感兴趣的:(#,C,语言之行)