C语言运算符应用
一、常用位运算和使用场景总结
1. 常用位运算操作行为
我们知道,物理层面的最小单位是bit,而软件层面是Byte。C语言提供了访问bit的工具——位运算符号。
&
与,有如下特性,所以我们可以用它来清除某一个位,即拿0去和一些位做与
操作,可以让它们都变成0。只要说清除
就是置0。- 1 & A = A
- 0 & A = 0
|
或,有如下特性,所以我们可以用它来设置某一个位,即拿1去和一些位做或
操作,可以让它们都变成1。只要说设置
就是置1。- 1 | A = 1
- 0 | A = A
>>
右移,补位与数据类型有关。<<
左移,末位补0。~
取反,~0 = 1
,~1 = 0
2. 设置某区域的值
有一个32bit的内存空间,假设标记为a int a;
-
对这段空间的第5bit设置为1。(从0开始的第5bit)
a | 0x1<<5
,a 或 0x1左移5位,这样可以避免写很长的数去或
。 -
对这段空间的第5bit清除为0。
清除是&0
,又要保持其他位不变,所以我们需要构造一个第5位为0,其他位为1的数。所以需要先构造第五位为1,其他为0的数,再取反。
a & ~(0x1 << 5)
-
对这段空间的5678位设置1011.
由于第5678位的具体的值未知,所以我们要先清除这几位的值(0x0位移没有意义!),再进行设置。
a & (~0xf << 5)
//注意先取反还是先左移
a | (0xb << 5)
3. 异或的灵活应用
^
同0异1
-
交换两个数,不引入中间变量
int a1 = 20, a2 = 10; a1 = a1 ^ a2; a2 = a1 ^ a2; // 此时的a2 = 原来的 a1 ^ a2 ^ a2 == a1 a1 = a1 ^ a2; // 此时的a1仍为原来的 a1 ^ a2, 而此时的a2已经为a1,现在的赋值就相当于什么都没变时的 a1 ^ a2 ^ a1 == a2。完成了交换
-
对称加密
dlkfajsdf ^ key1得到密文
密文 ^ key1得到原始信息 -
给你一堆数据,两两成对出现,只有1个数据是单个的,让我们从这堆数据中,找出这个单个的数据是什么?
一起做一次异或a1^a2^a3......
4. 统计一个int空间中1的个数
有一个特性:a & a-1
每做一次,a中1的个数会减少一个。
所以我们只需要不断地运行a & a-1
,直到a为0,统计运算的次数,这就是a中的1的个数
二、其他运算符梳理
1. 运算符
+ - * /
: 存在越界、容量够不够的问题
%
: 求余数,得到的是一个范围的数,一个循环的数。循环队列会用到。
2.逻辑运算符
逻辑与位的要区分开
- 逻辑:真和假。0(NULL等价于0)为假,其他都为真。
- 位状态:1和0——高电平和低电平
~a
和!a
~a
是逐位取反,值可大可小!a
是逻辑取反,真假转换
3.自增、自减
C语言提供的运算符,都不会改变变量本身的值,都需要和等号进行复合
a++
与++a
a++
是先把a暂存起来,放到一个临时位置,然后a本身的值加一。整个表达式的返回值是依靠这个临时位置的值的。++a
是a自身的内容加1,改变了a里面原来的值。表达式返回现在的a。
所以显然,++a
的效率更高,虽然现代编译器能优化,但是我们尽量在能用++a
的地方用它。
三、不同容量内存的赋值逻辑
char a = 0xabcd;
一个char的空间只有1字节,也就是8位,而0xabcd有16位,所以是“小的容器装大的内容”,会发生截取。
而如果是大的容器装小的,则会补填符号位。
sizeof(0xabcd)
的值不为2,因为0xabcd是数字常量,默认是int大小。
printf("%x")
中,%x
的意思是,把int这么大的内存的值,当作16进制显示在屏幕上。另外还有%hx
,是short这么大的;%hhx
是char这么大的。
四、指针const的结合,typedef
typedef
是对数据类型进行别名的
char *str_t; // 定义了一个叫做str_t的地址类型的变量,指向了一个char空间的地址
typedef char *str_t; // 定义了一个叫做str_t的地址类型的别名,指向了一个char空间的地址
const str_t s1 = s; //相当于char *const s1 = s; const是修饰这个*的,所以它指向的地址不能变,而地址中的内容可以变
const char *s2 = s; //const修饰的是指向的内容,也就是说,s2指向的东西可以变,而指向的东西的内容不能变