Linux c编程一站式学习笔记(06), 位移操作
2014-01-03 05:06
447 查看
本文源于本人<<Linux c 一站式学习>>总结笔记
(一) 位运算
注意,&、|、^运算符都是要做UsualArithmetic
Conversion的(其中有一步是Integer
Promotion),~运算符也要做IntegerPromotion,所以在C语言中其实并不存在8位整数的位运
算,操作数在做位运算之前都至少被提升为int型了,上面用8位整数举例只是为了书写方便。比
如:
gdb调试结果为:
计算过程是这样的:常量0xfc是int型的,赋给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位进行操作,举例如下。
习题:
1、统计一个无符号整数的二进制表示中1的个数,函数原型是intcountbit(unsigned
int x);
2、用位操作实现无符号整数的乘法运算,函数原型是unsignedint multiply(unsigned int
x,
unsigned inty);。例如:(11011)2×(10010)2=((11011) 2 <<1)+((11011)2 <<4)。
3、对一个32位无符号整数做循环右移,函数原型是unsignedint
rotate_right(unsigned int
x);。所谓循环右移就是把低位移出去的部分再补到高位上去,例如rotate_right(0xdeadbeef,
16)的值应该是0xefdeadbe。
4、交换两个变量的值,不得借助额外的存储空间,
(一) 位运算
注意,&、|、^运算符都是要做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调试结果为:
计算过程是这样的:常量0xfc是int型的,赋给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; }
相关文章推荐
- Linux c编程一站式学习笔记(08), 几个运算符
- Linux C编程一站式学习 笔记
- Linux c编程一站式学习笔记(09),段错误
- Linux c编程一站式学习笔记(03), 变量注意点
- Linux c编程一站式学习笔记(01), switch语句
- Linux c编程一站式学习笔记(04), debug
- Linux c编程一站式学习笔记(02), Return语句
- Linux c编程一站式学习笔记(05), 两个程序对比Continue和Break
- Linux C编程一站式学习笔记(1)
- Linux c编程一站式学习笔记(07), Side Effect,Short-circuit与SequencePoint小结
- 数据库操作的桥梁-JDBC学习笔记
- SAS学习笔记之《SAS编程与数据挖掘商业案例》(3)变量操作、观测值操作、SAS数据集管理
- Linux学习笔记2-文件读写操作
- Python Selenium 学习笔记(三)键盘和鼠标操作
- opencv学习笔记 一 图像读取与操作
- Linux学习笔记_常用操作_1
- [ASP.NET学习笔记之一]ASP.NET中文件操作
- VC操作Excel(学习笔记)
- JavaScript学习笔记之下拉选择框的操作
- Hadoop学习笔记——入门指令操作