C语言操作符详解:位运算、单目运算与表达式求值

C语言操作符详解

1. 操作符分类

C语言常见操作符包括:

  • 算术:+ - * / %;
  • 移位:<< >>;
  • 位运算:& | ^ ~;
  • 赋值:= += -= *= /= %= <<= >>=;
  • 比较:> >= < <= == !=;
  • 逻辑:&& || !;
  • 条件:条件 ? 值1 : 值2;
  • 访问与调用:[] () . ->;
  • 其他:sizeof、类型转换、逗号操作符。

2. 进制与转换

C语言整数常量:

1
2
3
int decimal = 15;  // 十进制
int octal = 017; // 八进制,以0开头
int hex = 0x0F; // 十六进制,以0x开头

二进制转十进制:把每一位乘以对应的 2 的幂后相加。二进制转八进制按3位分组,转十六进制按4位分组。

3. 原码、反码与补码

整数在计算机中以二进制存储。对有符号整数:

  • 正数的原码、反码、补码相同;
  • 负数反码:原码符号位不变,其余位取反;
  • 负数补码:反码加1。

运算统一使用补码,可以让加法器同时处理正数与负数。位运算也直接作用于补码表示。

4. 移位操作符

1
2
3
unsigned int x = 5;     // 00000101
unsigned int a = x << 1; // 00001010,即10
unsigned int b = x >> 1; // 00000010,即2

左移时低位补0。无符号数右移时高位补0;负有符号数右移的具体方式与实现有关。移位位数为负数或不小于类型位数会产生未定义行为。

5. 位操作符

操作符 含义 常见用途
& 按位与 清零、读取标志位
` ` 按位或
^ 按位异或 翻转、比较位差异
~ 按位取反 所有位翻转
1
2
3
4
unsigned int flags = 0;
flags |= 1u << 2; // 设置第2位
flags &= ~(1u << 2); // 清除第2位
int enabled = (flags & (1u << 2)) != 0;

异或的性质:x ^ x == 0,x ^ 0 == x。

6. 单目操作符

1
2
3
4
5
6
7
!value      // 逻辑非
~value // 按位取反
-value // 取负
&value // 取地址
*pointer // 解引用
sizeof x // 求类型或对象大小
(double)x // 强制类型转换

sizeof 的结果类型是 size_t。对数组对象使用 sizeof(arr) 得到整个数组字节数;但数组作为函数参数时已经退化为指针。

7. 前置与后置自增

1
2
3
int a = 5;
int x = ++a; // a先变为6,x得到6
int y = a++; // y得到6,a再变为7

不要在一个表达式中多次修改同一变量,例如 i = i++ + ++i;,这种代码可能包含未定义行为。

8. 逗号、下标与成员访问

逗号表达式从左到右计算,整体结果是最后一个表达式的值:

1
int value = (1, 2, 3); // value为3

结构体对象用 . 访问成员,结构体指针用 ->:

1
2
3
4
struct Student { char name[20]; int age; };
struct Student s = {"Li", 20};
struct Student *ps = &s;
printf("%d %d\n", s.age, ps->age);

9. 优先级、结合性和求值顺序

优先级决定表达式如何分组,结合性解决同一优先级的组合方向,但它们不等于操作数的实际求值顺序。

1
int result = a + b * c; // 等价于 a + (b * c)

复杂表达式应主动加括号,不要依赖记忆。&& 和 || 具有短路特性:结果确定后,右侧表达式不会执行。

10. 总结

操作符学习的难点是底层补码、隐式类型转换及表达式求值规则。工程中应优先保证清晰,避免把多个副作用塞进一个表达式。