前言
本笔记含有笔者的一些课堂笔记,所以有很多英语的描述。
因此比较偏个人向。
但我相信如果你想成为优秀的程序员,英语的掌握是必不可少的(笑)
一、优先级
原表格网址: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进行遮罩处理:
- The least significant byte of x, with all other bits set to 0
- x & 0xFF
- All but the least significant byte of complemented, with the least significant byte left unchanged
- x ^ ~0xFF
- 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位)会导致未定义的行为,并且结果可能因编译器和平台而异。
int lval = 0xFEDCBA98 << 32;
这里,你正在将一个int左移32位。这种行为是未定义的,因为对于一个32位的整数,左移32位或更多位是不明确定义的。
一些编译器可能会将此视为如果你已经将位移为0位,实际上将lval
留为0xFEDCBA98
。
其他编译器可能会进行环绕,这在这种情况下实际上与将位移为0位相同。
int aval = 0xFEDCBA98 >> 36;
与第一个案例类似,将int右移36位是未定义的行为。
一些编译器可能会将此视为如果你已经将位移为4位(36模32),结果为0xFFEDCBA9
。
其他编译器可能会产生不同的结果,甚至只给出垃圾值。
unsigned uval = 0xFEDCBA98u >> 40;
在这里,你正在将无符号整数(unsigned)右移40位。
对于无符号整数,行为是明确定义的,结果实际上是将值向右移动40 % 32 = 8
位。
因此,uval
将是0x00FEDCBA
。
更多文章:
数据在内存中的对齐问题
计算机编译程序的原理
函数调用的汇编原理
汇编语句详解(持续更新)
C语言中的类型转换