C语言_位运算2

文章目录

    • 0.写在前面
    • 2. C位运算符
        • 2.3 位移运算符
          • 2.3.1 左移运算符:`<<`
          • 2.3.2 右移运算符:`>>`
        • 2.4 位移运算符使用场景
          • 2.4.1 乘除法应用
    • 3.关于位运算的编程实例
        • 3.1 例题1:
        • 3.2 例题2
        • 3.3 例题3
    • 4. 位字段
        • 4.1 位子段使用方法
        • 4.2 位字段使用实例

0.写在前面

本博文为C语言_运算符1的继续,因此其编号顺序继承自原博文

2. C位运算符

2.3 位移运算符

位移运算符包括左移动<<运算符 和 右移>> 运算符

2.3.1 左移运算符:<<

左移运算符<< 将其左侧运算对象每一位的值向左移动其右侧运算对象指定的位数。左侧运算对象移出左末端位的值丢失,用0填充空出的位置。

如:

10001010<< 2 //表达式
00101000 //结果值

该操作产生新的位值,但不改变原来运算对象的值

如:

int stonk=138;//stonk=10001010
int moveVal=2;
int newStonk = stonk<<moveVal;
printf("stonk=%d\nnewStonk=%d\n",stonk,newStonk);

C语言_位运算2_第1张图片

2.3.2 右移运算符:>>

右移运算符>> 将其左侧运算对象每一位的值向右移动其右侧运算对象指定的位数。左侧运算对象移出右末端位的值丢失。

对于无符号类型,用0填充空出的位置;
对于有符号类型,其结果取决于机器,空出的位置可以用0填充,或者用符号位的副本填充

无符号类型实例:

(10001010)>>2 //表达式,无符号值
00100010  //所有系统都得到该值

有符号型实例:

(10001010) >> 2 //表达式 有符号值
00100010    //某些系统上的结果
同样的1000101/
(10001010)>> 2 //表达式值,有符号
11100010      //在另一些系统上的结果值

注意:

右移运算符在对有符号型数进行运算过程中,会因为机器型号问题,产生计算结果的差别

2.4 位移运算符使用场景

2.4.1 乘除法应用

使用位移运算符进行乘除法运算要比普通的乘除法运算符*、/ 运算效率更高

乘法运算:

unsigned int val=150;//val=1010110
int n=2;
unsigned int newVal= val << 2;
printf("val=%d\nnewVal=%d",val,newVal);

C语言_位运算2_第2张图片

除法运算

unsigned int val=133;//val=10100101
int n=2;
unsigned int newVal= val >> 2;
printf("val=%d\nnewVal=%d",val,newVal);

C语言_位运算2_第3张图片
总结:

由于基于位移运算符的乘除法,其底层是移动位,所以其乘除法运算是对2的幂进行乘除

3.关于位运算的编程实例

3.1 例题1:

假设用一个unsigned long类型的值表示颜色值,低阶位字节存储红色的强度,下一个字节存储绿色强度,第3个字节存储蓝色强度。
那么,如何将每种颜色的强度分别存储在3个不同的unsigned char 类型的变量中

编程思路:
用有移运算符将8位颜色值移动到低阶字节,然后用掩码技术将低阶字节赋值给指定变量

#include
using namespace std;
#define BYTE_MASK 0xff
int main()
{
     
	 unsigned long color = 0x002a162f;
	 unsigned char blue, green, red;
	 red = color&BYTE_MASK;
	 green = (color >> 8)&BYTE_MASK;
	 blue = (color >> 16)&BYTE_MASK;
	 printf("red=%x\n", red);
	 printf("green=%x\n", green);
	 printf("blue=%x\n", blue);
	 cin.get();
	 return 0;
}

C语言_位运算2_第4张图片

3.2 例题2

将十进制数字转换为二进制,要求使用位移运算符符解决问题

解题思路

  1. 从接盘输入值进行读取十进制数,以任意的非数字结束程序
  2. 十进制转换为二进制思路是用掩码获取输入值的最后一位,然后将获得的最末尾值存入数组,
    由于本例中定义数组为字符型,所以需要在存入数组前将其+‘0’完成字符值的‘1’ 和‘0’转换
  3. 为便于阅读,将结果以四位四位的形式输出

代码:

#include
#include//supply the define of CHAR_BIT, CHAR_BIT means how many bits in one byte

char* itobs(int, char*);

void show_bstr(const char*);

int main()
{
     
	 char bin_str[CHAR_BIT * sizeof(int) + 1];
	 int number;
	 puts("Enter integers and see them in binary");
	 puts("Non-numeric input terninates program");
	 while (scanf("%d", &number) == 1)
	 {
     
		  itobs(number, bin_str);
		  printf("%d is", number);
		  show_bstr(bin_str);
		  putchar('\n');
	 }
	 getchar();
	 return 0;
}
char* itobs(int n, char* ps)
{
     
	 int i;
	 const static int size = CHAR_BIT * sizeof(int);
	 for (i = size - 1; i >= 0; --i)
	 {
     
		  ps[i] = (01 & n) + '0';
		  ps[size] = '\0';
	  	  n >>= 1;
	 }
	 return ps;
}
void show_bstr(const char* str)
{
     
	 int i = 0;
	 while (str[i])
	 {
     
	  	putchar(str[i]);
	  	if (++i % 4 == 0 && str[i])
	  	{
     
	   		putchar(' ');
	  	}
	 }
}

C语言_位运算2_第5张图片

关键点说明:

  1. 头文件limits.h中的CHAR_BIT宏,表示一个char字节的位数。表达式CHAR_BIT* sizeof(int)表示int类型的位数,+1表示数组需要给数组末尾留空字符位置
  2. itobs函数返回的地址与传入的地址相同,该函数中首次执行for循环时,对01&n求职。01表示一个八进制形式的掩码,该掩码除了0号位为1之外,其它所有位都为0。因此,01&n就是n的最后一位的值。该值位0或1,但对于字符型数组而言,需要的是字符‘0’或‘1’。 所以该值+‘0’可以完成这种转换
  3. 为方便阅读,将其结果四位一空格的形式输出

3.3 例题3

请写一个函数用于切换一个值中的后n位, 要求:待处理值和n都是函数的参数

解题思路:

首先创造一个满足n位需求的掩码
其次使用^运算符进行求解

int invert_end(int num, int bits)
{
     
 	int mask = 0;
 	int bitVal = 1;
 	while (bits > 0)
 	{
     
  		mask |= bitVal;
  		bitVal <<= 1;
  		bits--;
 	}
 	return num^mask;
}

代码解释:
while循环用来创造所需要的掩码,最后num^mask运算获得结果

4. 位字段

操作位的第二种方法为位字段(bit field)

位字段是一个signed int 或 unsigned int类型变量中的一组相邻的位

位字段通过一个结构声明来建立,该结构声明为每个字段提供标签,并确定该字段的宽度。

4.1 位子段使用方法

创建两个1位的字段,一个2位的字段和一个8位的字段

struct
{
     
	unsigned int code1 : 1;
	unsigned int code2 : 1;
	unsigned int code3 : 2;
	unsigned int code4 : 8;
}prcode;

给位字段赋值的方式为:

prcode.code1=1;
prcode.code3=3;
prcode.code4=20;

注意:

  1. 由于结构prcode中将code1设置为1位,因此在赋值时其最大值为1;同样的code3的最大值为3
  2. 如果所赋值字段超过了其所允许的范围,会占用下一个字段的空间,但是不容许跨越两个字段。

发生跨越时,会产生未命名的“洞”,解决这个问题的方法是定义一个未命名的字段来填充洞

以下代码用来解决字段跨越问题:

struct
{
     
	unsigned int field1 : 1;
	unsigned int 	    : 2;//解决字段1的跨越问题
	unsigned int field2 : 1;
	unsigned int field3 : 8;
}stuff;

说明:
C语言_位运算2_第6张图片

4.2 位字段使用实例

位字段和位运算是两种可以互相替代的方式,因此此处不在论述,详细请参见《C primer plus》P1161
带书签课本资源请联系[email protected] 费用1元/次

你可能感兴趣的:(C/C++)