关于位运算必须记住的事


前言

本笔记含有笔者的一些课堂笔记,所以有很多英语的描述。
因此比较偏个人向。
但我相信如果你想成为优秀的程序员,英语的掌握是必不可少的(笑)


一、优先级

原表格网址:C语言运算符优先级(超详细)-CSDN博客

优先级运算符名称或含义使用形式结合方向说明
1[]数组下标数组名[常量表达式]左到右
()圆括号(表达式) / 函数名(形参表)
.成员选择(对象)对象.成员名
->成员选择(指针)对象指针->成员名
2-负号运算符-表达式右到左单目运算符
~按位取反运算符~表达式
++自增运算符++变量名 / 变量名++
自减运算符–变量名 / 变量名–
*取值运算符*指针变量
&取地址运算符&变量名
!逻辑非运算符!表达式
(类型)强制类型转换(数据类型)表达式
sizeof长度运算符sizeof(表达式)
3/表达式 / 表达式左到右双目运算符
*表达式 * 表达式
%余数(取模)整型表达式 % 整型表达式
4+表达式 + 表达式左到右双目运算符
-表达式 - 表达式
5<<左移变量 << 表达式左到右双目运算符
>>右移变量 >> 表达式
6>大于表达式 > 表达式左到右双目运算符
>=大于等于表达式 >= 表达式
<小于表达式 < 表达式
<=小于等于表达式 <= 表达式
7==等于表达式 == 表达式左到右双目运算符
!=不等于表达式 != 表达式
8&按位与表达式 & 表达式左到右双目运算符
9^按位异或表达式 ^ 表达式左到右双目运算符
10|按位或表达式表达式左到右
11&&逻辑与表达式 && 表达式左到右双目运算符
12||逻辑或表达式表达式
13?:条件运算符表达式1 ? 表达式2 : 表达式3右到左三目运算符
14=赋值运算符变量 = 表达式右到左
/=除后赋值变量 /= 表达式
*=乘后赋值变量 *= 表达式
%=取模后赋值变量 %= 表达式
+=加后赋值变量 += 表达式
-=减后赋值变量 -= 表达式
<<=左移后赋值变量 <<= 表达式
>>=右移后赋值变量 >>= 表达式
&=按位与后赋值变量 &= 表达式
^=按位异或后赋值变量 ^= 表达式
=按位或后赋值变量= 表达式
15,逗号运算符表达式, 表达式, …左到右

说明:
同一优先级的运算符,运算次序由结合方向所决定。
简单记就是:! > 算术运算符 > 关系运算符 > 逻辑运算符 > 赋值运算符

二、Attention

1. Cool Stuff with Xor

用异或(^)实现一个交换两个变量的函数:

int inplace_swap(int *x, int *y)
{
   *x = *x ^ *y;    /* #1 */
   *y = *x ^ *y;    /* #2 */
   *x = *x ^ *y;    /* #3 */
}

用表格表示出*x 和 *y的值:
异或运算

Warning:
对于以下这个程序:

void reverse_array(int a[], int cnt) {
	int first, last;
    for (first = 0, last = cnt-1; first <= last; first++, last--)
	    inplace_swap(&a[first], &a[last]);
}

如果first = last,则*x与*y的值相同,即x与y指向同一个地址
这样第一步中*x与*y都会被赋值为0(A^A = 0)
即最后a[mid]一定会被置0,这不符合交换的要求

2. Mask Operations

mask例子:0xFF
对x进行遮罩处理:

  1. The least significant byte of x, with all other bits set to 0
    • x & 0xFF
  2. All but the least significant byte of complemented, with the least significant byte left unchanged
    • x ^ ~0xFF
  3. The least significant byte set to all 1s, and all other bytes of x left unchanged.
    • x | 0xFF
  • x &1 = x
  • x & 0 = 0
  • x | 1 = 1
  • x | 0 = x
  • x ^ 0 = x
  • x ^ 1 = ~x

3. 位运算的一些作用

  • a && 5/a

    –If a is zero, the evaluation of 5/a is stopped
    –avoid division by zero

  • p && *p

    –Never cause the dereferencing of a null pointer

  • Using only bit-level and logical operations

    –Implement x == y
    –it returns 1 when x and y are equal, and 0 otherwise
    –!(x^y)

4.防止越界的移位运算

What happens ?

  • int lval = 0xFEDCBA98 << 32;
  • int aval = 0xFEDCBA98 >> 36;
  • unsigned uval = 0xFEDCBA98u >> 40;

在C和C++编程中,将整数左移或右移的位数超过该整数的大小(在这种情况下,对于一个int是32位)会导致未定义的行为,并且结果可能因编译器和平台而异。

  1. int lval = 0xFEDCBA98 << 32;

这里,你正在将一个int左移32位。这种行为是未定义的,因为对于一个32位的整数,左移32位或更多位是不明确定义的。

一些编译器可能会将此视为如果你已经将位移为0位,实际上将lval留为0xFEDCBA98

其他编译器可能会进行环绕,这在这种情况下实际上与将位移为0位相同。

  1. int aval = 0xFEDCBA98 >> 36;

与第一个案例类似,将int右移36位是未定义的行为。

一些编译器可能会将此视为如果你已经将位移为4位(36模32),结果为0xFFEDCBA9

其他编译器可能会产生不同的结果,甚至只给出垃圾值。

  1. unsigned uval = 0xFEDCBA98u >> 40;

在这里,你正在将无符号整数(unsigned)右移40位。

对于无符号整数,行为是明确定义的,结果实际上是将值向右移动40 % 32 = 8位。

因此,uval将是0x00FEDCBA

更多文章:
数据在内存中的对齐问题
计算机编译程序的原理
函数调用的汇编原理
汇编语句详解(持续更新)
C语言中的类型转换

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值