C语言之位操作

304次阅读

一,位操作与逻辑操作

位操作不同于逻辑操作。逻辑操作是一种整体操作,而位操作是针对内部数据位补码的操作。逻辑操作只有真假,位操作只有0和1。

运算符如下:
C语言之位操作

二,数据的二进制表示

1,八位二进制的补码
C语言之位操作
2,二进制打印

 

功能:显示二进制补码
C语言之位操作

三,位操作

1,位于(&)

C语言之位操作

x & 1 = x;        x & 0 = 0;
  • 1

参考用途:在某些位保持不变的情况下,其余位置为0。

2,位或(|)

C语言之位操作

 x | 1 = 1;             x | 0 = x;
  • 1

参考用途:某些位置不变,其余位为1。

3,位取反(~)

C语言之位操作

参考用途:间接的构造某个特别的数(如最大有符号正数),以增强程序的可读性。

4,位异或(^)

C语言之位操作

参考用途:某些位保持不变,其余位取反。

5,位左移(<<)

用法:x << n (n表示左移的位数)
规则:使操作数的各位左移,低位补0,高位溢出。
例:
5<<2=20
0101 <<2 = 010100

6,位右移(>>)

用法:x >> n
规则
使操作数的各位右移,移出的低位被舍弃。

对于高位而言,当是无符号数或有符号正数时,高位补0,当是负数时,则取决于所使用的系统:补0的为“逻辑右移”,补1的为“算术右移”。

例:
5 >> 2 =1
0101 -> 0001

20 >> 2 =5
10100 -> 0101

四,应用

1,掩码

掩码就是掩盖一些东西,留下一些东西。

2,功能

MASK=1<<1;
flag=0x96;

MASK -> 0000 0010
flag -> 1001 0110
& -> 0000 0010

打开位(使某位置1)flag |= MASK

  • 关闭位(使某位置0)flag &= ~MASK
  • 转置位(位反转)flag ^= MASK
  • 查看某一位的值 (flag & MASK) == MASK ?1:0;

3,遮罩码的生成

int mask = 0;
//假设要生成一个第3-6位的遮罩码;
int mask = (1<<6) | (1<<5) | (1<<4) | (1<<3) ;
/*真实过程如下:
0100 0000
| 0010 0000
| 0001 0000
| 0000 1000
= 0111 1000
*/
//////////////////////////////////////////////
int mask = 0;
for(int i=6; i>2; i–)
{
mask |= (1<<i);
}

4,练习

题目1:从键盘上输入 1 个正整数给 int 变量 num,输出由 3~6 位构成的数(从低0号开始编号)
基本思路:
1.截取 3~6 位的数,位移到 0~3 位
a)构建 3~6 位上为 1 其余为 0 的数
b)位与输入数
c)得到的结果右移 3 位

2.先将 3~6 位移到 0~3 位,截取 0~3 位
a)输入数右移 3 位
b)构建 0~3 位为 1 其余为 0 的数
c)位与,得到结果

//假设num=0xaa55;
//思路1:
int num=0xaa55;
int mask=0;
for(int i=6;i>=3;--1)
mask |=(1<<i);
num &= mask;
num = num>>3;
//思路2:
num = num>>3;
int mask=0;
for(int i=3;i>=0;--i)
mask |= (1<<i);
num &= mask;

题目2:实现循环移位
void circleMove(int *data ,int n);当 n>0 的时候左移,n<0 的时候循环右移。

void circleMove(int *data,int n)//unsigned int *pdata
{
int m;
m = n>0?n:-n;
unsigned int mask = 0;
while(m--)
mask |=(1<<m);

if(n>0)//左循环n位
*pdata = (*pdata << n) | (mask & *data >> sizeof(*data)*8-n);
else//右循环n位
*pdata = ( (*pata >> -n) & (mask << sizeof(*data*8-(-n)) | (*data << sizeof(*data)*8-(-n));
}

题目3:反转一个数据的最后n位。

void reverse(int *data, int n)
{
int mask=0;
while(n--)
mask |=(1<<n);
mask = mask << sizeof(*data)*8-n;
*data = *data^mask;
}

题目4:.判断一个数是不是 2 的幂数。
我们观察发现:若一个数是2的幂数,则其补码中只有一位为1,其他全部为0。因此,我们可以推出:若n为2的幂数,且其补码为0100 0000 ,则n-1的补码为0011 1111 ,所以,n&n-1=0。
我们可以根据这个特点,来判断一个数是不是2的幂数。

 void check(int *a)
{
return !( n & (n-1) );
}

5,提高

1)交换

(1)有参交换

void swap(int *a, int *b)
{
int t;
t=*a;
*a=*b;
*b=t;
}
缺点:使用了第三个变量。

void swap(int *a, int *b)
{
*a=*a+*b;
*b=*a-*b;
*a=*a-*b;
}
缺点:若a、b较大,有溢出的风险。

(2)无参交换

由异或的真值表可知,a,b,以及a^b(假设为c),知道a,b,c中的任意两个,将其进行异或运算,得到的就是第三个。
因此,我们可以运用这个原理,来实现两个数的交换。

void swap(int *a, int *b)
{
*a=*a^*b;
*b=*a^*b;
*a=*a^*b;
}

总结:无溢出,是交换数据的最高境界。

2)异或加密

函数的参数应有两个:一个是要加密的明文,另一个是密钥。

加密过程:

void encrypt(char *secret, char *key)
{
int kn = strlen(key);
int i = 0;
while(*secret != '\0')
{
if(*secret == key[i]) 
{
secret++;
i++;
}
else
{
*secret++ ^= key[i];
i++;
}
if(i%kn == 0)
i=0;
}
}

解密过程:

void de_encrypt(char *secret, char *key)
{
int kn = strlen(key);
int i=0;
while(*secret != '\0')
{
if(*secret == key[i])
{
secret++;
i++;
}
else
{
*secret ^= key[i];
i++;
}
if(i%kn==0)
i=0;
}
}
3)循环移位加密

位运算的加密应用,才是真正意义上的加密的开始。解决了加减法加密溢出的问题。

加密过程:

void encode(char *secret)
{
int n=strlen(secret);
unsigned char ch;
for(int n=0; i<n; i++)
{
ch = secret[i];
ch = (ch << 1) | (ch >> 7);
secret[i] = ch;
}
}

解密过程:

void decode(char *secret) 
{ 
int n=strlen(secret);
 unsigned char ch; 
for(int n=0; i<n; i++) 
{
ch = secret[i]; 
ch = (ch >> 1) | (ch << 7); 
secret[i] = ch;
 }
 }
yiywain
版权声明:本文于2021-12-16转载自C语言之位操作,共计2796字。
转载提示:此文章非本站原创文章,若需转载请联系原作者获得转载授权。