C语言之位操作

一,位操作与逻辑操作

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

运算符如下:
这里写图片描述

二,数据的二进制表示

1,八位二进制的补码
这里写图片描述
2,二进制打印

功能:显示二进制补码
这里写图片描述

三,位操作

1,位于(&)

这里写图片描述

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

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

2,位或(|)

这里写图片描述

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

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

3,位取反(~)

这里写图片描述

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

4,位异或(^)

这里写图片描述

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

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;
  }
}

猜你喜欢

转载自blog.csdn.net/rdgfdd/article/details/79742566
今日推荐