C语言笔记第三十一课

一、上课内容

1. 位运算及应用
位运算 : 针对二进制 ( 数字本来 int,4 字节 , 下面假设为 1 字节 )
12:0000 1100
13:0000 1101
~12:1111 0011 按位取反
12&13:0000 1100 按位与 , 相同的位都为 1 才为 1
12 | 13:0000 1101 按位或 , 相同的位只要有 1 就为 1
12^13:0000 0001 按位异或 , 相同的位不一样就为 1
12<<1:000 11000 按位左移 , 右边补 0( 离符号太远 )
24=12*2 左移相当于乘法 ( 正数 ) 乘以 2 的位移次方
12>>1:00000 110 右移 , 左边补符号位 ( 如果是无符号数补 0, 有符号数符号位为 0 则补 0, 1 则补
1)
6=12/2 右移相当于除法 ( 正数 ), 除以 2 的位移次方
具体运算如下 : 下面的运算都是针对二进制
去掉最后一位 | (101101->10110) | x>>1
在最后加一个 0 | (101101->1011010) | x<<1
在最后加一个 1 | (101101->1011011) | (x<<1) | 1
把最后一位变成 1 | (101100->101101) | x|1
把最后一位变成 0 | (101101->101100) | (x>>1) <<1 x&~1
最后一位取反 | (101101->101100) | x^1
把右数第 k 位变成 1 | (101001->101101,k=3) | x|(1<<(k-1))
把右数第 k 位变成 0 | (101101->101001,k=3) | x&111011 -> x&~(1<<(k-1))
右数第 k 位取反 | (101001->101101,k=3) | x^100 -> x^(1<<(k-1))
1. 确定符号 2. 确定数字 3. 构造数字
得到 1 | 1 ( 这一位是 1)
得到 0 &0( 这一位是 0)
取反 ^1
取最右边的 1 | (1101101->1) | x&1 , x%2
取末三位 | (1101101->101) | x&111->x&((1<<3)-1) ,x&7
取末 k | (1101101->1101,k=4) | x&((1<<k)-1)
取右数第 k | (1101101->1,k=4) | (x>>(k-1))&1

编程1:所有的数字成对,只有一个数字只出现一次,找到它  

//1^3^5^3^1=1^1^3^3^5=5
//找打单的数字
int SingleNum(int* arr, int len)//O(n)
{
int tmp = 0;
for (int i = 0; i < len; i++)
tmp ^= arr[i];
return tmp;
}
int main()
{
int arr[] = {1,5,3,7,9,6,1,3,9,6,5};//所有的数字成对,只有一个数字只出现一次,找
到它
//算法1:1.排序;2.遍历 O(nlogn)
//算法2:从头到尾按位异或(按位异或相同的数字为0,5^5==0)
int n = SingleNum(arr,sizeof(arr)/sizeof(arr[0]));
printf("%d\n",n);
return 0;
}

 编程应用2:统计一个数字二进制1的个数.例如9转为二进制是0000 1001,那么二进制中1的个数为2.

//算法1:利用%2和/2
//统计十进制数字中1的个数例如1231212->3 得个位n%10 ,丢个位n/=10
//统计二进制中1的个数
int Bits(unsigned int n)//-1 ->11111111111111111111111111111111
{
int count = 0;//计数器
while (n != 0)
{
if (n % 2 == 1)
count++;
n /= 2;
}
return count;
}
int main()
{
printf("%d\n",Bits(12345678));
printf("%d\n", Bits(0x12345678));//1+1+2+1+2+2+3+1
printf("%d\n", Bits(-1));//32
return 0;
}
//算法2:利用位运算,统计最右边1的个数
int Bits2(unsigned int n)
{//n&1:得到二进制右数第1位; n>>=1:丢弃右数第1位
int count = 0;
while (n != 0)//0x10000000
{
count += n & 1;
n >>= 1;
}
return count;
}
int main()
{
printf("%d\n",Bits(12345678));
printf("%d\n", Bits(0x12345678));//1+1+2+1+2+2+3+1
printf("%d\n", Bits(-1));//32
return 0;
}
//算法3:利用x&(x-1)进行统计
int Bits(unsigned int n)
{
int count = 0;
while (n != 0)
{
count++;
n &= n - 1; //丢弃二进制最右边的1
}
return count;
}
int main()
{
printf("%d\n",Bits(12345678));
printf("%d\n", Bits(0x12345678));//1+1+2+1+2+2+3+1
printf("%d\n", Bits(-1));//32
return 0;
}
x&(x-1) 的作用 ? : 丢弃最右边的 1 , 如下面的举例 :
x :3 11
x-1 :2 10
结果 :2 10
x :4 100
x-1 :3 011
结果 :0 000
x :5 101
x-1 :4 100
结果 :4 100
x :6 110
x-1 :5 101
结果 :4 100

 

//其他算法
int func(unsigned i) //统计二进制1的个数
{
unsigned temp = i;
temp = (temp & 0x55555555) + ((temp & 0xaaaaaaaa) >> 1);
temp = (temp & 0x33333333) + ((temp & 0xcccccccc) >> 2);
temp = (temp & 0x0f0f0f0f) + ((temp & 0xf0f0f0f0) >> 4);
temp = (temp & 0xff00ff) + ((temp & 0xff00ff00) >> 8);
temp = (temp & 0xffff) + ((temp & 0xffff0000) >> 16);
return temp;
}
int main()
{
printf("%d\n", func(0x7f530829));//3+4+2+2+1+1+2=15
return 0;
}
2. 泛型编程
void *p: 通用指针 , 也称泛型指针 , 没有类型的指针 , 仅仅记录地址的开头 , 不能算术运算 ,p+1, 不能*p.malloc 的返回值就是 void *;
void *memcpy(
void *dest,
const void *src,
size_t count
);//内存拷贝函数
dest:目标地址
sec:源地址
count:拷贝的字节数

下面以交换数据为例. 

void Swap_char(char* p1, char* p2)
{
char tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void Swap_short(short* p1, short* p2)
{
short tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void Swap_int(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
int main()
{
char a = 'x';
char b = 'y';
short c = 10;
short d = 20;
int e = 500;
int f = 600;
printf("%c,%c\n", a, b);
Swap_char(&a,&b);
printf("%c,%c\n",a,b);
printf("%d,%d\n", c, d);
Swap_short(&c,&d);
printf("%d,%d\n",c,d);
printf("%d,%d\n", e, f);
Swap_int(&e,&f);
printf("%d,%d\n",e,f);
return 0;
}

从上面的代码能发现,交换的过程是一样的,不一样的只有数据类型.  

下面的函数能针对所有的数据类型进行数据交换 

//size:字节数
void Swap(void* vp1, void* vp2,int size)
{
void* tmp = malloc(size);
memcpy(tmp, vp1, size);
memcpy(vp1,vp2,size);
memcpy(vp2,tmp,size);
free(tmp);
}
int main()
{
char a = 'x';
char b = 'y';
short c = 10;
short d = 20;
int e = 500;
int f = 600;
printf("%c,%c\n", a, b);
Swap(&a,&b,sizeof(char));
printf("%c,%c\n",a,b);
printf("%d,%d\n", c, d);
Swap(&c,&d,sizeof(short));
printf("%d,%d\n",c,d);
printf("%d,%d\n", e, f);
Swap(&e,&f,sizeof(int));
printf("%d,%d\n",e,f);
return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值