*简介:*在计算机中所有的数据都是以二进制的形式储存的。位运算其实就是直接对在内存中的二进制数进行操作,因此处理数据的速度非常快。
在实际编程中,如果能巧妙运用位操作,完全可以达到四两拨千金的效果,正是因为这些优点,所以位操作的应用非常广泛,同时掌握位带操作对于我们理解STM32的原理非常有用。
**
1.计算中的符号位: 计算机的符号位就是在处理二进制数据时,专门规定有一位,是用来确定数据的正负,符号位是1表示负数,是0表示正数。当然这里说的是有符号数,这个符号位通常是数据的最高位,如8位数据,左边第一位是符号位,后面7位用来表示数据大小。
2.补码: 注意,此处的“==”是相等的意思。
在机器的世界里:正数的最高位是符号位0,负数的最高位是符号位1。
对于正数:反码 ==补码 ==原码
对于负数:反码 ==除符号位以外的各位数取反
补码 ==反码+1
原码 ==(补码-1)后的反码 ==补码的反码+1
如:-15的二进制
<1>先取-15的原码:1000 1111
<2>得反码:1111 0000
<3>得补码:1111 0001
可见,-15在计算机里的二进制表达式就是1111 0001
16进制为:0xF1
3.二进制数右移:
把一个二进制数右移N位,规则为:
除符号位外,全部右移N位,如果数字是一个无符号位数值,则用0填补最左边的N位;如果数字是一个有符号位数值,则用1填补最左边的N位
也就是说,如果数字原先是一个正数,则右移之后在最左边补N个0;如果数字原先是个负数,则右移之后在最左边补N个1
如:-15=1111 0001
右移二位,最高位由符号位填充将得到 1111 1100即-4
一、位操作基础知识,位操作符的应用规则表及使用要点
二、位操作符的技巧,包括判断奇偶、交换两数、变换符号、求绝对值
位操作符还有许多应用,但对于学习STM32,理解这些知识应该足够了。
一、位操作的基础知识
基本的位操作符有与、或、异或、取反、左移、右移这6种,它们的运算规则如下所示:
|
|
符号 | 运算规则 |
---|---|
&(与) | 两个都为1时,结果才为1 |
l(或) | 两个位都为0时,结果才为0 |
^(异或) | 两个位相同为0,相异为1 |
~(取反) | 0变1,1变0 |
<< (左移) | 各二进制位全部左移若干位,高位丢弃,低位补0 |
>>(右移) | 各二进制位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移) |
注意以下几点:
1、在这六种操作符中,只有取反是单目操作符,其他5种都是双目操作符。
2、位操作只能用于整型数据,对float和double类型进行位操作会被编译器报错。
3、对于移位操作,在微软的VC6.0和VS2008编译器都是采取算术称位即算术移位操作,算术移位是相对于逻辑移位,它们在左移操作中都一样,低位补0即可,但在右移中逻辑移位的高位补0而算术移位的高位是补符号位。
如下面代码会输出-4和3。
int a = -15, b = 15;
printf("%d %d\n", a >> 2, b >> 2);
因为15=0000 1111(二进制),右移两位,最高位由符号位填充将得到0000 0011即3。 -15=1111 0001(二进制),右移两位,最高位由符号位填充将得到1111 1100 即-4。
4、位操作符的运算优先级比较低,因而尽量使用括号来确保运算顺序,否则可能得到名莫名其妙的结果。
比如说要得到像1,3,5,9这些2^i+1的数字。写成int a=1< 5、另外位操作还有一些复合操作符,如&=、|=、 ^=、<<=、>>=。
二、位操作符的技巧
下面对位操作的一些常见应用作个总结,有判断奇偶、交换两数、变换符号及求绝对值。这些小技巧应用易记,应当熟练掌握。
1、判断奇偶
只要根据最末位是1还是0来决定 ,为0就是偶数,为1就是奇数。因此可以用if ((a&1)==0)代替if(a%2 ==0)来判断a是不是偶数。
下面程序将输出0到100之间的所有奇数。
for (i=0;i<100;++i)
if(i&1)
printf ("%d",i);
putchar("\n");
分析:if(i&1)表示如果i是奇数,则执行if中函数,否则不执行
i&1,按位与取运算,取二进制整数i的最低位,如果最低位是1则得1,如果最低位是0则得0。奇数i的最低位是1,偶数i的最低位是0
如:i(二进制)&1
(0) 0000 0000&1得0 (偶数)
(1)0000 0001&1得1(奇数)
(2)0000 0010&1得0 (偶数)
2、交换两数
一般的写法是:
void Swap(int &a, int &b)
{
if (a != b)
{
int c = a;
a = b;
b = c;
可以用位操作来实现交换两数而不用第三方变量:
void Swap (int &a,int &b)
{
if (a != b)
{
a ^= b;
b ^= a;
a ^= b;
}
}
分析: 设 a= 1111 000
b= 1100 1100
则由 a ^= b 知 a = a ^ b
即 a = 0011 1101 <1>
由 b ^= a 知 b = b ^ a
{ b = 1100 1100
{ a = 0011 1101
则 b = 1111 0001 <2>
第3步, a ^= b 即 a = a ^ b
又 { a = 0011 1101
{ b = 1111 0001
则 a = 1100 1100 <3>
a与b的值实现调换。
3、变换符号
变换符号就是 正数变成负数,负数变成正数。
如:对于-11和11,可以通过下面的变换方法将-11变成11。
1111 0101 (-11的二进制)- - -<取反> 0000 1010 (二进制)
- - -<加1> 0000 1011 (11的二进制)
同理,可以将11变成-11。
0000 1011 (11的二进制)- - -<取反> 1111 0100 (二进制)
- - -<加1> 1111 0101 (-11的二进制)
因此变换符号只需要取反后加1即可。
完整代码如下:
#include
int SignReversal(int a)
{
return ~a + 1;
}
int main()
{
printf("对整数变换符号 --- by MoreWindows( http://blog.csdn.net/MoreWindows ) ---\n\n");
int a = 7, b = -12345;
printf("%d %d\n", SignReversal(a), SignReversal(b));
return 0;
}
4、求绝对值
位操作也可以用来求绝对值,对于负数可以通过对其取反后加1来得到正数。对于-6可以这样:
1111 1010 (-6的二进制)- - -<取反> 0000 0101(二进制)
- - -<加1> 0000 0110 (6的二进制)
来得到6。
因此先移位来取符号位,int i = a >> 31;要注意如果a为正数,i等于0,为负数,i等于-1。然后对i进行判断——如果i等于0,直接返回。否之,返回~a+1。
完整代码如下:
int my_abs(int a)
{
int i = a >> 31;
return i == 0 ? a : (~a + 1);
}
现在再分析下。对于任何数,与0异或都会保持不变,与-1即0xFFFFFFFF异或就相当于取反。因此,a与i异或后再减i(因为i为0或-1,所以减i即是要么加0要么加1)也可以得到绝对值。
所以可以对上面代码优化下:
int my_abs(int a)
{
int i = a >> 31;
return ((a ^ i) - i);
}
注意,上述这种方法没用任何判断表达式,故推荐使用。
分析: 在C语言中" ? : “是什么意思?
是条件运算符。条件运算符是C语言中唯一的三目三目运算符,就是说它有三个运算对象。条件运算符的形式是” ? : ",由它构成的表达式称为条件表达式。
形式为:表达式1 ?表达式2 :表达式3
运算功能是:先计算表达式1的值,若值为非0,则计算表达式2的值,
并将表达式2的值作为整个条件表达式的结果;
若计算表达式1的值为0,则计算表达式3的值,
并将表达式3的值作为整个条件表达式的结果。
比如,有以下表达式(a > b)? a + b : a - b
a = 8, b = 4时,计算 a + b = 12,所以表达式结果为12;
a = 4, b = 8时,计算 a - b = -4 ,所以表达式结果为 -4。
转载请标明出处,原文地址:
https://blog.csdn.net/qq_41689379
如果觉得本文对您有帮助,请点击‘顶’支持一下,您的支持是我写作最大的动力,谢谢。
— 科技小丫,点点滴滴。