C语言操作符深度解析与二进制运算原理
整型数据的内存存储机制
计算机内存以连续字节序列存放整型数据,其占用空间由具体类型决定。常见规格包括1字节、2字节、4字节及8字节,均采用二进制编码方案。以8位无符号数为例,可覆盖0至255的数值区间。
多字节整型遵循特定字节排列规则。小端模式(Little-Endian)将低位字节置于低地址端,高位字节置于高地址端。以32位数值0x12345678为例,其内存分布为:
地址 内容
0x1000 0x78
0x1001 0x56
0x1002 0x34
0x1003 0x12大端模式(Big-Endian)则相反,高位字节存入低地址。该模式在当代主流硬件中已较少采用。
原码、反码与补码
整型以补码形式驻留内存。三种编码均包含符号位与数值位,符号位0标识正数,1标识负数。
以32位系统为例:
数值 1: 00000000 00000000 00000000 00000001
数值 -1: 11111111 11111111 11111111 11111111补码转换规则:
- 正数:原码、反码、补码三者一致
- 负数:原码 → 符号位不变、数值位取反得反码 → 反码加一得补码
1. 算术操作符
基础运算符:+ - * / %
%仅适用于整型操作数,返回除法余数- 其余运算符可同时处理整型与浮点型
1.1 除法运算特性
整型除法:两侧均为整数时执行,结果截断小数部分。
浮点除法:任一侧为浮点数即触发,结果保留小数。
#include <stdio.h>
int main(void) {
int m = 7, n = 2;
double p = 7.0, q = 2.0;
printf("%d\n", m / n); /* 输出: 3 */
printf("%f\n", p / q); /* 输出: 3.500000 */
printf("%f\n", m / q); /* 输出: 3.500000 */
/* 除数为零将导致运行时异常 */
/* printf("%d", m / 0); */
return 0;
}2. 移位操作符
操作对象限定为整型,不可移动负数个位。
2.1 左移 <<
规则:左侧位舍弃,右侧补零。
/* 数值5的二进制 */
00000000 00000000 00000000 00000101
/* 左移2位后 */
00000000 00000000 00000000 00010100 /* 结果为20 */2.2 右移 >>
实现方式分为两类:
- 逻辑右移:右侧位舍弃,左侧补零
- 算术右移:右侧位舍弃,左侧填充原符号位
C标准未指定具体方式,主流编译器采用算术右移。
#include <stdio.h>
int main(void) {
signed int val = -8; /* 补码: 11111111...11111000 */
printf("%d\n", val >> 1); /* 算术右移得 -4 */
unsigned int uval = 8;
printf("%u\n", uval >> 1); /* 逻辑右移得 4 */
return 0;
}⚠️ 移位位数为负属于未定义行为。
3. 位操作符
针对二进制位逐位运算,操作数须为整型。
3.1 按位与 &
对应位全1则结果为1,否则为0。
#include <stdio.h>
int main(void) {
int x = 6; /* 0110 */
int y = 10; /* 1010 */
printf("%d\n", x & y); /* 0010 = 2 */
return 0;
}3.2 按位或 |
对应位存1则结果为1,双0方为0。
0110 (6)
| 1010 (10)
--------
1110 (14)3.3 按位异或 ^
对应位相异为1,相同为0。
#include <stdio.h>
int main(void) {
int a = 12; /* 1100 */
int b = 10; /* 1010 */
int res = a ^ b; /* 0110 = 6 */
printf("%d\n", res);
/* 经典应用:无临时变量交换 */
a = a ^ b;
b = a ^ b;
a = a ^ b;
return 0;
}4. 赋值操作符
基础形式:变量 = 表达式
支持连续赋值(可读性较差,建议拆分):
int k;
int j;
k = j = 15 + 3; /* 先计算18赋给j,再将j值赋给k */复合赋值符集合:
+= -= *= /= %=
<<= >>= &= |= ^=5. 单目操作符
5.1 逻辑非 !
真值转假,假值转真。常用于条件判断:
if (!(ptr == NULL)) { /* 指针有效 */ }5.2 sizeof 运算符
- 返回类型或对象的字节大小
- 编译期运算符,非函数调用
- 变量可省略括号:
sizeof var合法
#include <stdio.h>
int main(void) {
short s;
printf("%zu\n", sizeof(s)); /* 合法 */
printf("%zu\n", sizeof s); /* 合法,证明非函数 */
printf("%zu\n", sizeof(short)); /* 类型须加括号 */
return 0;
}5.3 自增自减
| 形式 | 行为 |
|---|---|
++i / --i | 先增减,后使用值 |
i++ / i-- | 先使用值,后增减 |
#include <stdio.h>
int main(void) {
int cnt = 5;
printf("%d\n", ++cnt); /* 6,先加后用 */
cnt = 5;
printf("%d\n", cnt++); /* 5,先用后加 */
printf("%d\n", cnt); /* 6 */
return 0;
}5.4 其他单目运算符
-负号+正号(通常省略)&取地址~按位取反(0变1,1变0)
6. 关系操作符
> >= < <= == !=注意区分赋值=与相等判断==。
7. 逻辑操作符
&&逻辑与:两侧表达式全真则真,存在短路求值特性||逻辑或:两侧表达式有真则真,存在短路求值特性
8. 条件操作符
语法:条件 ? 表达式A : 表达式B
条件为真取A值,为假取B值。
#include <stdio.h>
int main(void) {
int score = 78;
/* 替代简单if-else */
char grade = (score >= 60) ? 'P' : 'F';
printf("%c\n", grade);
/* 嵌套使用 */
int level = (score >= 90) ? 3 : (score >= 60) ? 2 : 1;
printf("%d\n", level);
return 0;
}9. 逗号表达式
形式:exp1, exp2, ..., expN
按序执行各子表达式,整体结果为最末表达式值。
#include <stdio.h>
int main(void) {
int m, n;
/* 先执行m=3,再执行n=m+2,整体值为5 */
int result = (m = 3, n = m + 2, n * 2);
printf("m=%d, n=%d, result=%d\n", m, n, result);
/* 输出: m=3, n=5, result=10 */
return 0;
}10. 特殊操作符
10.1 下标引用 []
数组声明与元素访问中的方括号含义不同:
int dataset[10]; /* 声明:定义含10元素的数组 */
dataset[7] = 100; /* 访问:引用第8个元素(下标从0起) */[]接受两个操作数:数组名与索引值,等价于*(dataset + 7)。
10.2 函数调用 ()
首操作数为函数标识符,后续为实参列表。参数数量可变,最少为零个。
#include <stdio.h>
void greet(void) { /* 无参函数 */
printf("Hello\n");
}
double compute(double base, int exp) {
double acc = 1.0;
while (exp-- > 0) acc *= base;
return acc;
}
int main(void) {
greet(); /* 零实参 */
printf("%f\n", compute(2.5, 3)); /* 两实参 */
return 0;
}