Linux c编程一站式学习笔记(06), 位移操作

本文源于本人<<Linux c 一站式学习>>总结笔记

(一) 位运算

注意,&|^运算符都是要做UsualArithmetic Conversion(其中有一步是Integer

Promotion),~运算符也要做IntegerPromotion,所以在C语言中其实并不存在8位整数的位运

,操作数在做位运算之前都至少被提升为int型了,上面用8位整数举例只是为了书写方便。比

:

#include <stdio.h>
int main(void)
{
unsigned char c =0xfc;
printf("%x\n",c);
unsigned int i = ~c;
printf("%x\n",i);
return 0;
}

gdb调试结果为:


计算过程是这样的:常量0xfcint型的,赋给c要转成unsignedchar ,值不变;c的十六进制表示

fc,计算~c时先提升为整型(000000fc)然后取反,最后结果是ffffff03。注意,如果把~c看成

8位整数的取反,最后结果就得3,这就错了。为了避免出错,一要尽量避免不同类型之间的赋

,二是每一步计算都要按类型转换规则仔细检查。


移位操作:

移位运算符(BitwiseShift)包括左移<<和右移>>。左移将一个整数的各二进制位全部左移若干

,例如0xcfffffff3<<2得到0x3fffffcc:


最高两位的11被移出去了,最低两位又补了两个0,其它位依次左移两位。但要注意,移动的位数

必须小于左操作数的总位数,比如上面的例子,左边是unsignedint,如果左移的位数大于等

32,则结果是Undefined。移位运算符不同于+- * / ==等运算符,两边操作数的类型不要求一

,但两边操作数都要做IntegerPromotion,整个表达式的类型和左操作数提升后的类型相同。

在一定的取值范围内,将一个整数左移1位相当于乘以2,因为如果左移改变了最高位(符号位),那么结果肯定不是乘以2,这条规律对有符号数和无符号数均成立。


当操作数是无符号数时,右移运算的规则和左移类似,例如0xcfffffff3>>2得到0x33fffffc:


最低两位的11被移出去了,最高两位又补了两个0,其它位依次右移两位。和左移类似,移动的位

数也必须小于左操作数的总位数,否则结果是Undefined。在一定的取值范围内,将一个整数右

1位相当于除以2,小数部分截掉。


当操作数是有符号数时,右移运算的规则比较复杂:

如果是正数,那么高位移入0

如果是负数,那么高位移入1还是0不一定,这是Implementation-defined的。对于x86平台

gcc编译器,最高位移入1,也就是仍保持负数的符号位,这种处理方式对负数仍然保持

右移1位相当于除以2”的性质。

由于类型转换和移位等问题,有符号数做位运算是很不方便的,建议只对无符号数做位运算,以减少出错的可能。

(二)掩码

如果要对一个整数中的某些位进行操作,怎样表示这些位在整数中的位置呢?可以用掩码

(Mask)来表示。比如掩码0x0000ff00表示对一个32位整数的8~15位进行操作,举例如下。

#include <stdio.h>

int main(void)
{
	unsigned int a, b, mask = 0x0000ff00;
	
	/*取出8~15位*/
	a = 0x12345678;
	b = (a & mask) >> 8;	
	printf("%x\n",a);
	printf("%x\n\n",b);
	
	/*将8~15位清0*/
	a = 0x12345678;
	b = a & ~mask;
	printf("%x\n",a);
	printf("%x\n\n",b);
	
	/*将8~15位置1*/
	a = 0x12345678;
	b = a | mask;
	printf("%x\n",a);
	printf("%x\n\n",b);
	
	return 0;
}

习题:

1、统计一个无符号整数的二进制表示中1的个数,函数原型是intcountbit(unsigned int x);

#include<stdio.h>
int countbit(unsignedint x)
{
    int count;
    for(count=0; x>0;count++)
    x&=x-1;	//把最后面的1变0
    return count;
}
int main(void)
{
    unsigned int a;
    printf("Pleaseinput an integer: ");
    scanf("%i",&a);
    printf("%d\n",countbit(a));
    return 0;
}

2、用位操作实现无符号整数的乘法运算,函数原型是unsignedint multiply(unsigned int x,

unsigned inty);。例如:(11011)2×(10010)2=((11011) 2 <<1)+((11011)2 <<4)

#include<stdio.h>
unsigned intmultiply(unsigned int x, unsigned int y)
{
    int ans = 0;
    while (x > 0)
    {
      if (x & 1)
    {
      ans += y;
    }
      y <<= 1;
      x >>= 1;
    }
      return ans;
    }

int main(void)
{
    unsigned int x = 123;
    unsigned int y = 456;
    printf("%d\n",multiply(x, y));
    return 0;
}

3、对一个32位无符号整数做循环右移,函数原型是unsignedint rotate_right(unsigned int

x);。所谓循环右移就是把低位移出去的部分再补到高位上去,例如rotate_right(0xdeadbeef,

16)的值应该是0xefdeadbe

#include <stdio.h>
unsigned introtate_right(unsigned int x, int n)
{
    int save;
    int i;
    for(i=0;i<n;i++)
{
    save=x&0x00000001;
    x=x>>1;
    save=save<<7;
    x+=save;
}
    return x;
}

int main(void)
{
    unsigned int x = 123;
    printf("%d\n",rotate_right(x,32));
    return 0;
}

4、交换两个变量的值,不得借助额外的存储空间,

#include<stdio.h>

void swap1(unsigned intx, unsigned int y)
{
    x = x ^ y;
    y = y ^ x;
    x = y ^ x;
    printf("Swap1:");
    printf("%d,%d\n", x, y);
}

void swap2(unsigned intx, unsigned int y)
{
    x = x + y;
    y = x - y;
    x = x - y;
    printf("Swap2:");
    printf("%d,%d\n", x, y);
}

int main(void)
{
    unsigned int a = 123;
    unsigned int b = 456;
    printf("Original:");
    printf("%d,%d\n", a, b);
    swap1(a,b);
    swap2(a,b);
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值