Lesson-15.md
December 14, 2012 · View on GitHub
Lesson 15 Count bit 1 in a number 统计一个数二进制表示中1的个数
#include <stdio.h>
int count_bit_1(int n)
{
unsigned int i;
int counter = 0;
for(i = 0; i < sizeof(int) * 8; i++)
{
if((n >> i) & 0x01)
counter++;
}
return counter;
}
int main(int argc, char *argv[])
{
int num;
printf("please input a number:");
scanf("%d", &num);
printf("number in hex is 0x%x\n", n);
printf("%d bit '1' in %d\n", count_bit_1(num), num);
return 0;
}
第2种算法--消去最左侧的1
#include <stdio.h>
int count_bit_1(int n)
{
int counter;
for (counter = 0; n; counter++)
n &= n-1;
return counter;
}
int main(int argc, char *argv[])
{
int num;
printf("please input a number:");
scanf("%d", &num);
printf("number in hex is 0x%x\n", num);
printf("%d bit '1' in %d\n", count_bit_1(num), num);
return 0;
}
第3种算法--二进制数内的加法
#include <stdio.h>
//types and constants used in the functions below
typedef unsigned int uint32; //assume this gives 32-bits
const uint32 m1 = 0x55555555; //binary: 0101...
const uint32 m2 = 0x33333333; //binary: 00110011..
const uint32 m4 = 0x0f0f0f0f; //binary: 4 zeros, 4 ones ...
const uint32 m8 = 0x00ff00ff; //binary: 8 zeros, 8 ones ...
const uint32 m16 = 0x0000ffff; //binary: 16 zeros, 16 ones ...
const uint32 h01 = 0x01010101; //the sum of 256 to the power of 0,1,2,3...
//This is a naive implementation, shown for comparison,
//and to help in understanding the better functions.
//It uses 24 arithmetic operations (shift, add, and).
int count_bit_1(unsigned int x)
{
x = (x & m1 ) + ((x >> 1) & m1 ); //put count of each 2 bits into those 2 bits
x = (x & m2 ) + ((x >> 2) & m2 ); //put count of each 4 bits into those 4 bits
x = (x & m4 ) + ((x >> 4) & m4 ); //put count of each 8 bits into those 8 bits
x = (x & m8 ) + ((x >> 8) & m8 ); //put count of each 16 bits into those 16 bits
x = (x & m16) + ((x >> 16) & m16); //put count of each 32 bits into those 32 bits
return x;
}
int main(int argc, char *argv[])
{
int num;
printf("please input a number:");
scanf("%d", &num);
printf("number in hex is 0x%x\n", num);
printf("%d bit '1' in %d\n", count_bit_1(num), num);
return 0;
}
知识点
- 位操作 &, |, ~, ^
- 设置位 set bit
a |= 1<<4; - 清除位 clear bit
a &= ~(1<<4); - 测试位 test bit
if (a & (1<<31)) - 设置位域 set bit-field
a &= ~(0x7<<28); a |= 0x5<<28; - 获取位域 get bit-field
if (((a>>28) & 0x7) == 0x5)
- 设置位 set bit
- 常见操作
- 不要把 &, | 混淆为 &&, ||
1 & 1 == 1 && 1 1 & 2 == 0 但是 1 && 2 == ture 同理类似的 5 & 10 == 0 - 取反操作用来构造数
0xFFFFFFFF == ~0x0 0xFFFFFFE0 == ~0x1F - 运算符&,^,| 的优先级比<,>关系运算符和判等运算符 == 要低
举例:
int status = 0;
if (status & 0x4000 == 0) // 条件成立,还是不成立?
- 不要把 &, | 混淆为 &&, ||
- 优化算法效率 http://hi.baidu.com/bobchennan/item/169952dc515045e4795daa10
- n & n-1 妙用
- 二进制数内的加法
课堂讨论
- 与第1个算法相比,第2个算法有什么优点?
- 第3个算法的时间复杂度是多少? 它背后的思想是什么?
课后练习
-
请写出可以进行位操作的 set_bit, get_bit 接口
- set_bit(int num, int pos, int v);
- get_bit(int num, int pos);
-
用位运算实现字符的大小写转换 (两种方法:异或,测试后修改)
- 要求:输入大写的字符转为小写,输入小写的字符转为大写;
-
用位运算实现对一个无符号整型的二进制打印,八进制打印,十六进制打印
- 要求:
int print_bin(int a);
int print_oct(int a);
int print_hex(int a);
a = 31
二进制打印 0000 0000 ... 0001 1111
八进制打印 0 0 0 ... 0 3 7
十六进制打印 00 00 00 1F - 要求:
Bit-Field 位域操作
#include <stdio.h>
struct flag
{
unsigned int is_keyword : 1;
unsigned int is_extern : 1;
unsigned int is_static : 1;
unsigned int is_mid : 4;
unsigned int is_high : 1;
unsigned int is_highest : 30;
} flags;
int main( int argc, char * argv[] )
{
printf( "hello, Cruel World! \n" );
flags.is_keyword = 1;
flags.is_extern = 1;
flags.is_high = 1;
flags.is_mid = 2;
printf( "flags = 0x%x \n", flags );
printf( "sizeof flags = 0x%x \n",sizeof(flags) );
return 0;
}
扩展应用
/* include/linux/tcp.h */
struct tcphdr {
__be16 source;
__be16 dest;
__be32 seq;
__be32 ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u16 res1:4,
doff:4,
fin:1,
syn:1,
rst:1,
psh:1,
ack:1,
urg:1,
ece:1,
cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u16 doff:4,
res1:4,
cwr:1,
ece:1,
urg:1,
ack:1,
psh:1,
rst:1,
syn:1,
fin:1;
#else
#error "Adjust your <asm/byteorder.h> defines"
#endif
__be16 window;
__sum16 check;
__be16 urg_ptr;
};
知识点
- 位域 bit-field
- 大端和小端 Big-Endian/Little-Endian
课堂讨论
- 位域和之前学过的结构体,有何异同之处?
- 位域在哪些场合下可以适用?
- 码流结构 stream
- 传输协议结构 protocal
- 特殊功能寄存器设置 SFR
课后练习
- 用位域操作实现随机生成无重复的10个数字,要求不允许使用数组
- 提示:随机数用 rand() 函数,用一个整型数的bit0-bit9来记录已经产生的数字
名人名言
- Donald Ervin Knuth
- Premature optimization is the root of all evil. (过早优化是万恶之源)