嵌入式初学-C语言-三十

位运算

什么是位运算

针对数据的二进制位的相关操作,位运算在嵌入式开发领域有着非常重要的应用

位运算常用的运算符

符号

说明

符号

说明

&

按位与

~

按位取反

|

按位或

<<

按位左移

^

按位异或

>>

按位右移

说明:

(1) 位运算符中除~以外,均为双目(二元)运算符,即要求两侧各有一个运算量。

(2) 运算量只能是整型或字符型的数据,不能为实型数据。

位运算符的运算规则

按位与(&)

运算规则:

对于左右操作数,只有相应二进制数据都为1时,结果数据对应位数据位1,简单来说,你为1我为1结果就是1,否则0来凑

作用:

  1. 获取某二进制位的数据
  2. 将指定二进制位数据清零
  3. 保留指定位

举例:

 // 代表二进制位

1 & 1 = 1

1 & 0 = 0

0 & 1 = 0

0 & 0 = 0

按位或(|)

运算规则:

       对于左右操作数,只要相应二进制位数据有一个为1,结果数据对用数据为1,简单来说你为1或者我为1,结果就是1

作用:

  1. 置位某二进制位数据

 举例:

// 代表二进制位

1 | 1 = 1

1 | 0 = 1

0 | 1 = 1

0 | 0 = 0

 

按位异或(^)

运算规则:

       对于左右操作数,只要相应二进制位数据相同,结果数据对应数据位为0,不同为1,简单来说,你我相同结果为0,你我不同结果为1

作用:

  1. 使特定位翻转
  2. 值交换

例题:

 

按位取反(~)

运算规则:

       原操作数将相应二进制位数据:0变1,1变0

按位左移(<<)

运算规则:

       原操作数所有的二进制位数向左移动指定位,移出的数据舍弃,右边用0补全

注意:

  1. 如果移出的高位都是0,我们可以这样理解a<<n,可以看作是a*2^n
  2. 如果移出的高位包含1,不能使用以上计算方式
按位右移(>>)

运算规则:

       原操作数所有的二进制位数向右移动指定位,移出的数据舍弃。

  1. 如果操作数是无符号数,左边用0补全即可,
  2. 如果是有符号数,左边用什么去补全,取决于计算机系统
  1. 逻辑右移:用0补全
  2. 算数右移:用1补全

大部份情况下,系统遵循算术右移

位运算赋值符

运算符:&=, |=, >>=, <<=, ∧=

 举例:

a &= b 相当于 a = a & b

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

不同长度数据进行位运算

如果两个数据长度不同(例如long型和short型),进行位运算时(如a & b,而a为long型,b为short型),系统会将二者按右端对齐。

  1. 如果b为正数,则左侧16位补满0;
  2. 若b为负数,左端应补满1;
  3. 如果b为无符号整数型,则左侧添满0。

位运算符的应用场景

  1. 将某个数据从右侧(低位)指定的二进制位(n1,n2,n3……)清零
    1. 公式:a &= ~(1<<(n1 - 1) | 1<<(n2-1) | 1<<(n3-1),…);

  1. 获取某个数据制定的二进制位(n)上的数据是0还是1
    1. 公式:(a & (1<<n)) >> n
    2. 说明:如果n的取值从右侧开始,从0开始计数
  2. 将某个数据从右侧指定的二进制位(n1,n2,n3)设置为1
    1. 公式:a |= (1 << n1 | 1 << n2 | 1 << n3…);

  1. 将某个数据指定的二进制位(n)翻转
    1. 公式:a ^=(1 << n)

位段(位域)

概念:

在结构体中,以位(bit)伟大为的成员,称为位段或位域

(1) 位段成员的类型不能为浮点型类型。

(2) 若某一位段要从另一个字开始存放,可用以下形式

 说明:

// eg: STM32中电源控制寄存器(PWR_CR)
struct PWR
{
unisgned int ldps : 1;
unsigned int pdds : 1;
unsigned int cwuf : 1;
unsigned int csbf : 1;
unsigned int pvde : 1;
unsigned int pls : 3;
unsigned int pvde : 1;
unsigned short d ;
};
struct Stu stu = {1,0,1,1,1,7,1};
// 应用:
// 方便给寄存器中的某些数据位设置数据
注意

1.位段不能取地址

struct S
{
char a : 2;
unsigned char b : 4;
};
struct S s;
printf("%p\n", &s); //s是结构体变量,可以取地址
printf("%p\n", &(s.a)); //s.a是结构体成员位段a,不能对它取地址

2.给位段成员赋值时,不能超出成员所占内存

struct S
{
char a : 2;
unsigned char b : 4;
unsigned char c : 5;
};
struct S s = {1, 3};
s.a = 1; //a占两个bit位,并且是有符号数,它能被赋值为-2, -1, 0, 1,2, 3; 除此之外的都会报错
s.b = 10; //b占4个bit位,并且是无符号数,它的取值范围为:[0,15]; 如果取值是[-8,15]编译正常,只是-8
在输出是值为15
s.c = 20; //c占5个bit位,并且是无符号数,它的取值范围为:[0,31];如果取值是[-16,31]编译正常,只
是-16在输出是值为31

3.位段成员定义时不能是浮点数; 只能是整型和字符型或者他们的无符号形式

struct A
{
int a:2;
unsigned char b:4;
float c:4; //错误,位段成员不能定义成float或者double
};

4.一个位段必须存放在一个内存单元中,不能跨两个单元

struct A
{
char a:7; //a在一个内存单元中,剩余一个bit位
char b:7; //b得从下一个内存单元开始,因为上一个内存单元剩余一个bit,放不下7个bit
char c:3; //c得从下一个内存单元开始,因为上一个内存单元剩余一个bit, 放不下3个bit
char d:3; //d和c可以在同一个内存单元中,因为c只占了3个还剩余5个bit,能够放下d的3个bit不用跨内存
单元
};

5.位段的长度不能大于存储单元的长度

struct A
{
char a:1;
char b:8; //一个char占一个字节,最大分配8个bit位
char c:9; //一个char占一个字节,最大分配8个bit位,如果分配9个就是错误的,无法通过编译
};

6.如果一个位段要从另一个存储单元开始,可以在它前面定义一个匿名成员占0个字节

struct A
{
char a:1;
char b:2; //a,b总共占3个字节,在一个内存单元中就可以存放,所以不用跨内存单元
char :0; //匿名成员,占0个字节
char c:2; //c不和a,b在一个字节(内存单元)中,而是在另外一个字节(内存单元)中
};

 说明:如果成员c上面没有匿名成员,c就和a,b在同一个字节(同一个内存单元中)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值