本篇截图内容(除负数的补码运算以外)全部摘自谭浩强老师的《C程序设计(第五版)学习辅导》一书中的第12章。
位运算是C语言的重要特点,是其他计算机高级语言所没有的。所谓位运算是指以二进制位为对象的运算。在系统软件中,常要处理二进制位的问题。例如,将一个存储单元中的各二进制位左移或右移一位、两个数按位相加等。
1.1位运算和位运算符
注意:参加位运算的对象只能是整型或字符型的数据,不能为实型数据
1.1.1按位“与”
特殊用途:
(要想将哪一位保留下来,就与一个数进行&运算,此数在该位取1)
1.1.2按位“或”
特殊用途:
常用来对一个数据的某些位定值为1。例如,a是一个整数,有表达式:a|0377,则低8位全置为1,高8位保留原样。
1.1.3按位“异或”
特殊用途:
1.1.4按位“取反”
特殊用途:
注:~运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他位运算符都高。例如:~a&b,先进行~a运算,再进行&运算
1.1.5左移
特殊用途:
左移1位相当于该数乘以2,左移2位相当于该数乘以2*2。但此结论只适用于该数左移时被溢出舍弃的高位中不包含1的情况。由下表可以看出,64在左移1位后相当于乘2,左移2位后,值就等于0了。
1.1.6右移
注:右移时,对于无符号数左边高位补0;对于有符号数,若原来符号位为0(该数为正),则左边也补0。若符号位原来为1(即负数),则高位补0还是1,要取决于所用的计算机系统,有的系统补0,有的系统补1。补0的称为“逻辑右移”,即不考虑数的符号问题,补1的称为“算数右移”,即保持原有的符号。
特殊用途:
右移1位相当于除以2,右移n位相当于除以2的n次方。
1.1.7位运算赋值运算符
1.1.8不同长度的数据进行位运算
1.2位运算举例
- 从一个整数a中把从右端开始的4~7位取出来。
【思路】
【代码实现】
#include<stdio.h>
int main()
{
void print_bin1(int);
unsigned a,b,c,d;
printf("please enter a:");
scanf("%o",&a);
b=a>>4;
c=~(~0<<4);
d=b&c;
printf("%o,%d\n%o,%d\n\n",a,a,d,d);
print_bin1(a);
printf("\n");
print_bin1(d);
return 0;
}
//输出二进制——位运算法
void print_bin1(int numb)
{
char *buff = (char*)malloc(32*sizeof(char));
int count = 0;
int index = 0;
count = 0;
do{
buff[count++] = numb&1;
numb = numb >> 1;
if(numb == 0){
break;
}
index ++;
}while(index < 32);
for(count--;count >= 0; count--){
printf("%d",buff[count]);
}
printf("\n");
free(buff);
}
- 循环移位。假设用两个字节存放一个短整型(short int型),将a右移循环移n位,即将左面16-n位右移n位,原来右端n位移到最左面n位。
【思路】
【代码实现】
注意:以下实现方法并非实际意义上的循环移动,int是32位,则八进制157653的二进制0000 0000 0000 0000 1101 1111 1010 1011左移时,是前面的0换到右边了,并不是110换到右边
#include<stdio.h>
int main()
{
void print_bin1(unsigned int numb);
unsigned int a,b,c; //默认是unsigned int(32位)
int n;
a=0157653;
n=3;
printf("循环右移->\n");
b=a<<(32-n);
c=a>>n;
c=c|b;
printf("\na:%o\nc:%o\n\n",a,c);
printf("循环左移->\n");
a=0157653;
b=a>>(32-n);
c=a<<n;
c=c|b;
printf("\na:%o\nc:%o\n\n",a,c);
return 0;
}
实际意义上的循环移动代码实现如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
int main()
{
void print_bin1(int numb);
unsigned int a,b,c; //默认是unsigned int(32位)
int n,count; //count为n的二进制位数(不包括前面的0)
char str[32];
a=0157653; //八进制
n=3; //移动的位数
itoa(a,str,2);
count=strlen(str);
printf("循环右移->\n");
b=(a<<(count-n))&(int)(pow(2.0,count)-1);
c=a>>n;
c=c|b;
printf("\na:%o\nc:%o\n\n",a,c);
printf("循环左移->\n");
a=0157653; //八进制
b=a>>(count-n);
c=(a<<n)&(int)(pow(2.0,count)-1);
c=c|b;
printf("\na:%o\nc:%o\n\n",a,c);
return 0;
}
1.3位段
对内存中信息的存取一般是以字节为单位。实际上,有时存储一个信息不必用一个或多个字节,例如,“真”或“假”用0或1表示,只需1个二进制位即可。在计算机用于过程控制、参数检测或数据通信领域时,控制信息往往只占1个字节中的1个或几个二进制位,常常在一个字节中放几个信息。
但是,以上方法太麻烦,故可以使用以下的位段结构体的方法。
C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或“位域(bit field)”。利用位段能够用较少的位数存储数据。
【注:】