位运算
#include <stdio.h> #include <stdlib.h> //位运算 取反 void main1(){ unsigned char ch = 15; //0000 1111 unsigned char fch = ~ch; //1111 0000 取反运算,取反并不会改变ch的值,赋值运算才会改变(ch = ~ch;) unsigned char ffch = ~fch; //0000 1111 再次取反运算,两次取反就变成原来的值了 printf("%d,%d,%d\n",ch,fch,ffch); //15,240,15 } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //位运算 位与& 1&1->1 0&1,1&0,0&0->0 根据这一特性可以得出以下两种功能 //1、二进制位与0相与,执行清零操作: 0 & 0 ->0 1 & 0->0 与0相与,把第一个操作数全部置为0 //2、二进制位与1相与,保留某些位不变: 0 & 1 ->0 1 & 1 ->1 与1相与 保留第一个操作数不变 //高级用法:与取反运算相结合,可以实现不同类型(char,short,int)数值的最低为清零,还可以实现取余的运算(不用%运算符) void main2(){ unsigned char ch = 35; //0010 0011 unsigned char ch1 = 0; //0000 0000 unsigned char ch2 = 15; //0000 1111 unsigned char ch3 = 240; //1111 0000 unsigned char ch4 = 60; //0011 1100 printf("%d\n",ch&ch1); // 0000 0000 全部清零 0 printf("%d\n",ch&ch2); // 0000 0011 清零高四位,保留低四位 3 printf("%d\n",ch&ch3); // 0010 0000 保留高四位,清零低四位` 32 printf("%d\n",ch&ch4); // 0010 0000 取出中间四位数 32 } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //位运算 位或| 1|1,0|1,1|0 ->1 0|0->0 根据这一特性可以得出以下两种功能 //1、二进制位与1相或,执行置1操作: 0 | 1 ->1 1 | 1->1 与1相或,把第一个操作数全部置为1 //2、二进制位与0相或,保留某些位不变: 0 | 0 ->0 1 | 0 ->1 与0相或 保留第一个操作数不变 void main3(){ unsigned char ch = 35; //0010 0011 unsigned char ch1 = 0; //0000 0000 unsigned char ch2 = 15; //0000 1111 unsigned char ch3 = 240; //1111 0000 unsigned char ch4 = 60; //0011 1100 printf("%d\n",ch|ch1); // 0010 0011 全部保持不变 35 printf("%d\n",ch|ch2); // 0010 1111 保留高四位,低四位全部为1 47 printf("%d\n",ch|ch3); // 1111 0011 高四位全部为1,保留低四位` 243 printf("%d\n",ch|ch4); // 0011 1111 中间四位数全部为1,保留收尾两位 63 } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //位运算 位异或^ 1 ^ 0=1 0 ^ 1=1,相同为0,不同为1 1 ^ 1=0 0 ^ 0=0 根据这一特性可以得出以下两种功能 //1、二进制位与1相异或,发生反转,0变1,1变0: 0 ^ 1 ->1 1 ^ 1->0 //2、二进制位与0相异或,保持位数不变: 0 ^ 0 ->1 1 ^ 0 ->1 与0相异或 保持位数不变: void main4(){ unsigned char ch = 35; //0010 0011 unsigned char ch1 = 0; //0000 0000 unsigned char ch2 = 15; //0000 1111 unsigned char ch3 = 240; //1111 0000 unsigned char ch4 = 60; //0011 1100 printf("%d\n",ch^ch1); // 0010 0011 全部保持不变 35 printf("%d\n",ch^ch2); // 0010 1100 前四位保持不变,后四位发生反转 44 printf("%d\n",ch^ch3); // 1101 0011 前四位发生反转,后四位保持不变 211 printf("%d\n",ch^ch4); // 0001 1111 中间四位反转,保持首尾两位不变 31 } //异或可以用于交换变量,不借助中间变量,一般用于嵌入式开发和需要节约内存的场合 // x = x ^ y; y = x ^ y; x = x ^ y; 记住 不会溢出 //类似于: x = x + y; y = x - y; x = x ^ y; 类比记忆 可能会溢出
//x = x * y; y = x / y; x = x / y; 可能会溢出
void main5(){ unsigned char x =10; unsigned char y= 20; x = x ^ y; y = x ^ y; x = x ^ y; // x = x + y; y = x - y; x = x ^ y; printf("%d,%d\n",x,y); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /*移位运算表达式的基本形式为: A << n; *//*左移*//* 或 A >> n; *//*右移*//* A称为操作数,其必须为数字型变量或数字型常量,此处的数字型包括整型和char型,A中存储的0、1序列向左或右移动n位, 移动后的值作为整个表达式的输出,执行移位运算并不改变操作数A的值。存储在寄存器中*/ void main6(){ unsigned char ch = 1; //0000 0001 1 printf("%d\n",ch << 1); // 0000 0010 2 printf("%d\n",ch << 2); // 0000 0100 4 //左移一位就乘以2,计算结果在寄存器中,不会改变ch的值 printf("%d\n",ch << 7); // 1000 0000 128 printf("%d\n",ch); // 0000 0001 printf("%d\n",ch << 8); // 10000 0000 0 左移8位溢出了,读取了无效数据 } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void main(){ //位运算的复合运算 char ch = 1; ch >>=2 ; //ch = ch >> 2; ch <<=2 ; //ch = ch << 2; ch &=2 ; //ch = ch & 2; ch |=2 ; //ch = ch | 2; ch ^=2 ; //ch = ch ^ 2; ch = ~ch; }
1、类型不匹配:
无符号数据在进行位运算时,类型不匹配时,需要进行自动类型转换
低字节向高字节转换时,会自动填充 0
有符号数据在进行位运算时,类型不匹配时,需要进行自动类型转换
低字节向高字节转换时,
正数会按照符号位自动填充 0
负数会按照符号位自动填充 1 位运算操作的是补码/
2、移位运算
左移时,右边都是填充0
右移时:无符号数据,左边填充0
有符号数据:正数按照符号位填充0
负数按照符号位填充1
3、 ~ | & ^ 不关心操作数的符号,符号位会被当做普通的0和1处理
#include <stdio.h> int func(x){ int countx = 0; while(x){ countx ++; x= x & (x-1); //实际上就是求二进制数有多少个 1 } return countx; } void main(){ int x=9999; //100111 0000 1111 用除K取余法转换成二进制 printf("%d\n",func(x)); //8 };
输入一个整数,打印其补码,用位运算实现
#include <stdio.h> #include <stdlib.h> //输入一个整数,打印其补码,用位运算实现 //0010 1110 0000 0001 0000 1111 0011 1101 任意一个整数的补码 //1000 0000 0000 0000 0000 0000 0000 0000 在计算机内部,补码是低字节位数在前面,让其与这个数相与,取出第一位数 void main(){ int num; scanf("%d",&num); int data = 1 << 31; //构造被与数 1000 0000 0000 0000 0000 0000 0000 0000 for (int i = 0; i < 32; ++i) { printf("%c",((num & data)? '1': '0')); num <<=1; //num再右移一位 if((i+1) % 4 == 0){ printf("\n"); } } }
#include <stdio.h> #include <stdlib.h> //输入一个整数,打印其源码补码,用结构体实现 struct bits{ //根据结构体对齐原则,此结构体占一个字节 unsigned char b1 : 1; unsigned char b2 : 1; unsigned char b3 : 1; unsigned char b4 : 1; unsigned char b5 : 1; unsigned char b6 : 1; unsigned char b7 : 1; unsigned char b8 : 1; }; void main(){ struct bits *pBits = (struct bits *)malloc(sizeof(struct bits) * 4); //分配四个字节,每一个字节是一个结构体 int *pInt = (int *)pBits; //把四个字节的结构体// 共享内存 按照int的解析接受整数的输入 *pInt =0; //初始化数据 scanf("%d",pInt); for(int i = 3 ; i >= 0 ; i-- ){ //低字节在前,高字节在后,从后面开始循环 printf("%d%d%d%d %d%d%d%d \n", pBits[i].b8, //按照结构体解析,输出每一位数字,每个i是一个结构体 pBits[i].b7, pBits[i].b6, pBits[i].b5, pBits[i].b4, pBits[i].b3, pBits[i].b2, pBits[i].b1 ); } }
输入一个浮点数,打印补码
#include <stdio.h> #include <stdlib.h> //输入一个浮点数,打印补码 //第一种方法:自动分配浮点数据内存,转换地址成 unsigned char类型,共享内存,按照unsigned char 类型解析,取出里面的每一位 void main1(){ float f; scanf("%f",&f); //接收浮点数据输入 //浮点数据也是按照4个字节存储的二进制数,现在定义一个 unsigned char的指针类型,取出里面的每一个字节,再利用位运算&,取出每个字节中的每一位 //unsigned char *p = (unsigned char *)malloc(sizeof(unsigned char) * 4); unsigned char *p = (unsigned char *)&f; //把f的地址存储在p指针中,并按照unsigned char来解析数据,共享内存 for (int i = 3; i >=0 ; i--) { //高字节在前,低字节在后,遍历浮点数据的每一个字节 unsigned char ch= p[i]; //p[i]是这个字节的首地址,&p[i]是这个字节第一位的首地址 for (int j = 7; j >=0 ; j-- ) { //高位在前,低位在后,遍历每一个字节中的每一位 if(ch & (1 << j)){ //1 <<j 把第j位置为1,与ch相与,从高位到低位,依次取出每一位的值 printf("%c",'1'); } else{ printf("%c",'0'); } } printf("\n"); //一个字节换行 } } //第二种方法,手动分配浮点数据的内存 void main(){ //浮点数据也是按照4个字节存储的二进制数,现在定义一个 unsigned char的指针类型,取出里面的每一个字节,再利用位运算&,取出每个字节中的每一位 unsigned char *p = (unsigned char *)malloc(sizeof(unsigned char) * 4); //分配四个字节的内存,存储浮点数的二进制数 scanf("%f",p); //接收浮点二进制数输入,二进制已经定型,后面按照unsigned char的类型去解析 for (int i = 3; i >=0 ; i--) { //高字节在前,低字节在后,遍历浮点数据的每一个字节 unsigned char ch= p[i]; //p[i]是这个字节的首地址,&p[i]是这个字节第一位的首地址,unsigned char的类型去解析数据 for (int j = 7; j >=0 ; j-- ) { //高位在前,低位在后,遍历每一个字节中的每一位 if(ch & (1 << j)){ //1 <<j 把第j位置为1,与ch相与,从高位到低位,依次取出每一位的值 printf("%c",'1'); } else{ printf("%c",'0'); } } printf("\n"); //一个字节换行 } }