前置知识: C

运算符与表达式

9 min中级

算术、关系、逻辑、位运算及运算符优先级详解。

前置知识

学习目标

  • 掌握「1. 运算符分类 (Operator Categories)」的核心机制、典型用法与常见陷阱
  • 掌握「2. 运算符优先级 (Precedence)」的核心机制、典型用法与常见陷阱
  • 掌握「3. 表达式 (Expressions)」的核心机制、典型用法与常见陷阱
  • 掌握「4. 运算符与表达式的最佳实践」的核心机制、典型用法与常见陷阱
  • 掌握「5. 常见问题与解决方案」的核心机制、典型用法与常见陷阱

1. 运算符分类 (Operator Categories)

1.1 算术运算符 (Arithmetic)

1.1.1 基本算术运算符

运算符描述示例 (a=10, b=3)
+加法a + b = 13
-减法a - b = 7
*乘法a * b = 30
/除法a / b = 3 (整数除法舍去小数)
%取模a % b = 1

1.1.2 自增自减运算符

运算符描述示例 (a=10)结果最终 a 值
a++后置自增a++1011
++a前置自增++a1111
a--后置自减a--109
--a前置自减--a99

1.1.3 算术运算符示例

 #include <stdio.h>
 int main() {
  int a = 10, b = 3;
  printf("a + b = %d\n", a + b); // 13
  printf("a - b = %d\n", a - b); // 7
  printf("a * b = %d\n", a * b); // 30
  printf("a / b = %d\n", a / b); // 3(整数除法)
  printf("a %% b = %d\n", a % b); // 1
  // 自增自减
  int c = 5;
  printf("c++ = %d\n", c++); // 5
  printf("c = %d\n", c); // 6
  printf("++c = %d\n", ++c); // 7
  printf("c = %d\n", c); // 7
  return 0;
 }

1.2 关系运算符 (Relational)

1.2.1 关系运算符列表

运算符描述示例 (a=10, b=3)
==等于a == b → 0 (假)
!=不等于a != b → 1 (真)
>大于a > b → 1 (真)
<小于a < b → 0 (假)
>=大于等于a >= b → 1 (真)
<=小于等于a <= b → 0 (假)

1.2.2 关系运算符示例

 #include <stdio.h>
 int main() {
  int a = 10, b = 3;
  printf("a == b: %d\n", a == b); // 0
  printf("a != b: %d\n", a != b); // 1
  printf("a > b: %d\n", a > b); // 1
  printf("a < b: %d\n", a < b); // 0
  printf("a >= b: %d\n", a >= b); // 1
  printf("a <= b: %d\n", a <= b); // 0
  return 0;
 }

1.3 逻辑运算符 (Logical)

1.3.1 逻辑运算符列表

| 运算符 | 描述 | 短路特性 | 示例 | | ------ | ------ | ------------------ | -------------------- | ------------------ | -------- | --- | -------- | | && | 逻辑与 | 左为假时,右不执行 | (a > 0) && (b > 0) | | | | | 逻辑或 | 左为真时,右不执行 | (a > 0) | | (b > 0) | | ! | 逻辑非 | 无 | !(a > 0) |

1.3.2 逻辑运算符示例

 #include <stdio.h>
 int main() {
  int a = 10, b = 0;
  // 逻辑与
  printf("(a > 0) && (b > 0): %d\n", (a > 0) && (b > 0)); // 0
  // 逻辑或
  printf("(a > 0) || (b > 0): %d\n", (a > 0) || (b > 0)); // 1
  // 逻辑非
  printf("!(a > 0): %d\n", !(a > 0)); // 0
  printf("!(b > 0): %d\n", !(b > 0)); // 1
  // 短路特性示例
  int x = 5, y = 5;
  printf("(x == 0) && (++y): %d\n", (x == 0) && (++y)); // 0,y 不变
  printf("y = %d\n", y); // 5
  printf("(x != 0) || (++y): %d\n", (x != 0) || (++y)); // 1,y 不变
  printf("y = %d\n", y); // 5
  return 0;
 }

1.4 位运算符 (Bitwise)

1.4.1 位运算符列表

| 运算符 | 描述 | 示例 (a=6 (0110), b=3 (0011)) | | ------ | -------- | ----------------------------- | --- | ------------- | | & | 按位与 | a & b = 2 (0010) | | | | 按位或 | a | b = 7 (0111) | | ^ | 按位异或 | a ^ b = 5 (0101) | | ~ | 按位取反 | ~a = -7 (1001...1001) | | << | 左移 | a << 1 = 12 (1100) | | >> | 右移 | a >> 1 = 3 (0011) |

1.4.2 位运算符示例

 #include <stdio.h>
 void print_bits(int n, int bits) {
  for (int i = bits - 1; i >= 0; i--) {
  printf("%d", (n >> i) & 1);
  }
  printf("\n");
 }
 int main() {
  int a = 6; // 0110
  int b = 3; // 0011
  printf("a = %d: ", a);
  print_bits(a, 4);
  printf("b = %d: ", b);
  print_bits(b, 4);
  printf("a & b = %d: ", a & b);
  print_bits(a & b, 4);
  printf("a | b = %d: ", a | b);
  print_bits(a | b, 4);
  printf("a ^ b = %d: ", a ^ b);
  print_bits(a ^ b, 4);
  printf("~a = %d: ", ~a);
  print_bits(~a, 4);
  printf("a << 1 = %d: ", a << 1);
  print_bits(a << 1, 4);
  printf("a >> 1 = %d: ", a >> 1);
  print_bits(a >> 1, 4);
  return 0;
 }

1.4.3 位运算符的应用

 // 检查某一位是否为 1
 #define CHECK_BIT(x, pos) ((x) & (1 << (pos)))
 // 设置某一位为 1
 #define SET_BIT(x, pos) ((x) |= (1 << (pos)))
 // 清除某一位为 0
 #define CLEAR_BIT(x, pos) ((x) &= ~(1 << (pos)))
 // 切换某一位的值
 #define TOGGLE_BIT(x, pos) ((x) ^= (1 << (pos)))
 // 示例
 int main() {
  int x = 0; // 0000
  SET_BIT(x, 2); // 0100
  printf("x after setting bit 2: %d\n", x); // 4
  TOGGLE_BIT(x, 1); // 0110
  printf("x after toggling bit 1: %d\n", x); // 6
  if (CHECK_BIT(x, 2)) {
  printf("Bit 2 is set\n");
  }
  CLEAR_BIT(x, 2); // 0010
  printf("x after clearing bit 2: %d\n", x); // 2
  return 0;
 }

1.5 赋值运算符 (Assignment)

1.5.1 赋值运算符列表

| 运算符 | 描述 | 示例 | 等价于 | | ------ | -------------- | ------------ | ------------ | ---- | ------ | --- | | = | 简单赋值 | a = b | a = b | | += | 加后赋值 | a += b | a = a + b | | -= | 减后赋值 | a -= b | a = a - b | | *= | 乘后赋值 | a *= b | a = a * b | | /= | 除后赋值 | a /= b | a = a / b | | %= | 取模后赋值 | a %= b | a = a % b | | <<= | 左移后赋值 | a <<= b | a = a << b | | >>= | 右移后赋值 | a >>= b | a = a >> b | | &= | 按位与后赋值 | a &= b | a = a & b | | ^= | 按位异或后赋值 | a ^= b | a = a ^ b | | | = | 按位或后赋值 | a | = b | a = a | b |

1.5.2 赋值运算符示例

 #include <stdio.h>
 int main() {
  int a = 10, b = 3;
  printf("初始值: a = %d, b = %d\n", a, b);
  a += b; // a = a + b
  printf("a += b: %d\n", a); // 13
  a -= b; // a = a - b
  printf("a -= b: %d\n", a); // 10
  a *= b; // a = a * b
  printf("a *= b: %d\n", a); // 30
  a /= b; // a = a / b
  printf("a /= b: %d\n", a); // 10
  a %= b; // a = a % b
  printf("a %%= b: %d\n", a); // 1
  return 0;
 }

1.6 其他运算符

1.6.1 sizeof 运算符

 #include <stdio.h>
 int main() {
  printf("Size of int: %zu bytes\n", sizeof(int));
  printf("Size of char: %zu bytes\n", sizeof(char));
  printf("Size of double: %zu bytes\n", sizeof(double));
  printf("Size of int*: %zu bytes\n", sizeof(int*));
  int arr[10];
  printf("Size of arr: %zu bytes\n", sizeof(arr));
  printf("Number of elements: %zu\n", sizeof(arr) / sizeof(arr[0]));
  return 0;
 }

1.6.2 取地址和解引用运算符

 #include <stdio.h>
 int main() {
  int a = 10;
  int *p = &a; // 取地址
  printf("a = %d\n", a);
  printf("&a = %p\n", &a);
  printf("p = %p\n", p);
  printf("*p = %d\n", *p); // 解引用
  *p = 20; // 通过指针修改值
  printf("After modification: a = %d\n", a);
  return 0;
 }

1.6.3 条件运算符(三目运算符)

 #include <stdio.h>
 int main() {
  int a = 10, b = 3;
  // 找出最大值
  int max = (a > b) ? a : b;
  printf("Max: %d\n", max); // 10
  // 找出最小值
  int min = (a < b) ? a : b;
  printf("Min: %d\n", min); // 3
  // 条件赋值
  int result = (a % 2 == 0) ? 1 : 0;
  printf("Is a even? %d\n", result); // 1
  return 0;
 }

1.6.4 逗号运算符

 #include <stdio.h>
 int main() {
  int a, b, c;
  // 逗号运算符从左到右执行,返回最后一个表达式的值
  c = (a = 5, b = 10, a + b);
  printf("a = %d, b = %d, c = %d\n", a, b, c); // 5, 10, 15
  // 在 for 循环中使用
  for (int i = 0, j = 10; i < j; i++, j--) {
  printf("i = %d, j = %d\n", i, j);
  }
  return 0;
 }

2. 运算符优先级 (Precedence)

2.1 优先级表(从高到低)

优先级运算符结合性
1() [] -> .从左到右
2! ~ ++ -- * & (type) sizeof从右到左
3* / %从左到右
4+ -从左到右
5<< >>从左到右
6< <= > >=从左到右
7== !=从左到右
8&(按位与)从左到右
9^(按位异或)从左到右
10|(按位或)从左到右
11&&从左到右
12||从左到右
13? :从右到左
14= += -= *= /= %= <<= >>= &= ^= |=从右到左
15,从左到右

2.2 优先级示例

 #include <stdio.h>
 int main() {
  int a = 10, b = 3, c = 5, d = 2;
  // 优先级示例
  int result1 = a + b * c; // 先乘后加: 10 + 15 = 25
  printf("a + b * c = %d\n", result1);
  int result2 = (a + b) * c; // 先加后乘: 13 * 5 = 65
  printf("(a + b) * c = %d\n", result2);
  int result3 = a || b && c; // 先与后或: 10 || 1 = 1
  printf("a || b && c = %d\n", result3);
  int result4 = a > b ? c : d; // 条件运算符: 10 > 3 为真,结果 5
  printf("a > b ? c : d = %d\n", result4);
  return 0;
 }

2.3 结合性示例

 #include <stdio.h>
 int main() {
  // 从左到右结合
  int a = 10 - 3 + 5; // (10 - 3) + 5 = 12
  printf("10 - 3 + 5 = %d\n", a);
  // 从右到左结合(赋值运算符)
  int b, c;
  b = c = 5; // b = (c = 5)
  printf("b = %d, c = %d\n", b, c);
  // 从右到左结合(单目运算符)
  int d = 5;
  int e = -++d; // -(++d) = -6
  printf("-++d = %d\n", e);
  return 0;
 }

3. 表达式 (Expressions)

3.1 表达式类型

  • 算术表达式: 由算术运算符组成,结果为数值
  • 关系表达式: 由关系运算符组成,结果为 0 或 1
  • 逻辑表达式: 由逻辑运算符组成,结果为 0 或 1
  • 位表达式: 由位运算符组成,结果为数值
  • 赋值表达式: 由赋值运算符组成,结果为赋值后的值
  • 条件表达式: 由三目运算符组成,结果为两个表达式之一的值
  • 逗号表达式: 由逗号运算符组成,结果为最后一个表达式的值

3.2 表达式示例

 #include <stdio.h>
 int main() {
  int a = 10, b = 3;
  // 算术表达式
  int arith_expr = a + b * 2;
  printf("Arithmetic expression: %d\n", arith_expr);
  // 关系表达式
  int rel_expr = a > b;
  printf("Relational expression: %d\n", rel_expr);
  // 逻辑表达式
  int log_expr = (a > 0) && (b < 5);
  printf("Logical expression: %d\n", log_expr);
  // 位表达式
  int bit_expr = a & b;
  printf("Bitwise expression: %d\n", bit_expr);
  // 赋值表达式
  int assign_expr = a = b + 5;
  printf("Assignment expression: %d, a = %d\n", assign_expr, a);
  // 条件表达式
  int cond_expr = (a > b) ? a : b;
  printf("Conditional expression: %d\n", cond_expr);
  // 逗号表达式
  int comma_expr = (a = 10, b = 20, a + b);
  printf("Comma expression: %d\n", comma_expr);
  return 0;
 }

3.3 表达式中的类型转换

3.3.1 隐式类型转换

 #include <stdio.h>
 int main() {
  int a = 10;
  float b = 3.14;
  // int 转换为 float
  float result1 = a + b;
  printf("a + b = %f\n", result1); // 13.140000
  // float 转换为 int(截断小数)
  int result2 = a + (int)b;
  printf("a + (int)b = %d\n", result2); // 13
  return 0;
 }

3.3.2 显式类型转换

 #include <stdio.h>
 int main() {
  double pi = 3.14159;
  int radius = 5;
  // 显式类型转换
  int area = (int)(pi * radius * radius);
  printf("Area: %d\n", area); // 78
  // 指针类型转换
  int x = 100;
  void *ptr = &x;
  int *int_ptr = (int *)ptr;
  printf("*int_ptr = %d\n", *int_ptr); // 100
  return 0;
 }

3.4 表达式中的副作用

3.4.1 副作用示例

 #include <stdio.h>
 int main() {
  int a = 5;
  // 未定义行为:多次修改同一个变量
  // int result = a++ + ++a; // 不要这样写!
  // 正确的写法
  int b = a++;
  int c = ++a;
  int result = b + c;
  printf("b = %d, c = %d, result = %d\n", b, c, result); // 5, 7, 12
  return 0;
 }

4. 运算符与表达式的最佳实践

4.1 代码风格建议

  • 括号使用: 对于复杂表达式,使用括号明确优先级
  • 命名规范: 使用有意义的变量名
  • 表达式简洁性: 避免过于复杂的表达式
  • 注释: 对于复杂的位运算或逻辑表达式,添加注释

4.2 性能优化建议

  • 位运算: 对于位移操作,使用位运算符代替乘法和除法
  • 短路求值: 利用逻辑运算符的短路特性优化条件判断
  • 常量表达式: 尽可能使用常量表达式,便于编译器优化

4.3 常见错误避免

  • 优先级错误: 始终使用括号明确优先级
  • 类型转换错误: 注意隐式类型转换可能导致的精度丢失
  • 副作用错误: 避免在表达式中多次修改同一个变量
  • 逻辑错误: 注意逻辑运算符的短路特性

4.4 最佳实践示例

 #include <stdio.h>
 // 位运算优化:判断奇偶
 #define IS_EVEN(x) ((x) & 1 == 0)
 // 位运算优化:乘以 2 的幂
 #define MULTIPLY_BY_POWER_OF_TWO(x, n) ((x) << (n))
 // 逻辑运算符短路优化
 int is_valid(int *ptr, int size) {
  return ptr != NULL && size > 0; // 如果 ptr 为 NULL,size > 0 不会执行
 }
 int main() {
  // 使用括号明确优先级
  int a = 10, b = 3, c = 5;
  int result = (a + b) * c; // 明确先加后乘
  // 位运算优化
  int x = 5;
  printf("x is even? %d\n", IS_EVEN(x)); // 0
  printf("x * 8 = %d\n", MULTIPLY_BY_POWER_OF_TWO(x, 3)); // 40
  // 逻辑短路优化
  int *ptr = NULL;
  int size = 10;
  if (is_valid(ptr, size)) {
  printf("Valid pointer and size\n");
  } else {
  printf("Invalid pointer or size\n"); // 执行这里
  }
  return 0;
 }

5. 常见问题与解决方案

5.1 整数除法问题

问题: 整数除法会截断小数部分 解决方案: 使用浮点数类型或显式类型转换

 // 错误示例
 int a = 10, b = 3;
 float result = a / b; // 结果为 3.0,不是 3.333...
 // 正确示例
 float result = (float)a / b; // 结果为 3.333...

5.2 优先级混淆

问题: 运算符优先级不明确导致错误 解决方案: 使用括号明确优先级

 // 错误示例
 int a = 10, b = 3, c = 5;
 int result = a + b * c; // 可能不是预期的 (a + b) * c
 // 正确示例
 int result = (a + b) * c; // 明确先加后乘

5.3 逻辑运算符短路

问题: 依赖逻辑运算符的短路特性可能导致意外行为 解决方案: 确保短路部分的代码不包含重要的副作用

 // 问题:如果 ptr 为 NULL,func() 不会执行
 if (ptr != NULL && func()) {
  // ...
 }
 // 解决方案:如果 func() 需要执行,分开写
 if (ptr != NULL) {
  if (func()) {
  // ...
  }
 }

5.4 位运算符号扩展

问题: 有符号数右移时会进行符号扩展 解决方案: 使用无符号类型或掩码

 // 符号扩展示例
 int a = -1; // 二进制全 1
 int b = a >> 1; // 结果仍为 -1,因为符号扩展
 // 无符号类型示例
 unsigned int c = -1; // 二进制全 1
 unsigned int d = c >> 1; // 结果为 0x7FFFFFFF

5.5 自增自减运算符的副作用

问题: 在表达式中使用自增自减运算符可能导致未定义行为 解决方案: 避免在复杂表达式中使用自增自减运算符

 // 未定义行为
 int a = 5;
 int result = a++ + ++a; // 不要这样写!
 // 正确写法
 int a = 5;
 int b = a++;
 int c = ++a;
 int result = b + c;

6. 代码优化技巧

6.1 算术运算优化

  • 使用位运算: 位移操作比乘法除法更快
  • 常量折叠: 编译器会优化常量表达式
  • 避免冗余计算: 缓存计算结果

6.2 逻辑运算优化

  • 短路求值: 利用逻辑运算符的短路特性
  • 条件判断顺序: 将最可能为真的条件放在前面
  • 位掩码: 使用位掩码替代多个条件判断

6.3 表达式优化示例

 // 优化前
 for (int i = 0; i < 1000; i++) {
  int result = a * 8 + b * 4;
  // ...
 }
 // 优化后
 for (int i = 0; i < 1000; i++) {
  int result = (a << 3) + (b << 2); // 位运算更快
  // ...
 }
 // 优化前
 if (x > 0 && y > 0 && z > 0) {
  // ...
 }
 // 优化后(假设 x > 0 的概率最高)
 if (x > 0 && y > 0 && z > 0) {
  // 保持不变,因为短路特性会自动优化
 }
 // 优化前
 if (flag == 1) {
  // case 1
 }
  // case 2
 }
  // case 4
 }
 // 优化后(使用位掩码)
 #define FLAG_1 1
 #define FLAG_2 2
 #define FLAG_4 4
 if (flag & FLAG_1) {
  // case 1
 }
 if (flag & FLAG_2) {
  // case 2
 }
 if (flag & FLAG_4) {
  // case 4
 }

算术运算符

加法写法:加法运算 <expr> + <expr>

// 计算两数之和
int a = 10, b = 3;
int sum = a + b;

减法写法:减法运算 <expr> - <expr>

// 计算两数之差
int a = 10, b = 3;
int diff = a - b;

乘法写法:乘法运算 <expr> * <expr>

// 计算两数之积
int a = 10, b = 3;
int product = a * b;

除法写法:除法运算 <expr> / <expr>

// 整数除法(舍去小数)
int a = 10, b = 3;
int quotient = a / b;

取模写法:取模运算 <expr> % <expr>

// 计算余数
int a = 10, b = 3;
int remainder = a % b;

后置写法:后置自增 <var>++

// 返回原值后自增
int c = 5;
int result = c++;

前置写法:前置自增 ++<var>

// 先自增后返回新值
int c = 5;
int result = ++c;

后置写法:后置自减 <var>--

// 返回原值后自减
int c = 5;
int result = c--;

前置写法:前置自减 --<var>

// 先自减后返回新值
int c = 5;
int result = --c;

关系运算符

等于写法:等于比较 <expr> == <expr>

// 判断两数是否相等
int a = 10, b = 3;
int result = (a == b);

不等于写法:不等于比较 <expr> != <expr>

// 判断两数是否不等
int a = 10, b = 3;
int result = (a != b);

大于写法:大于比较 <expr> > <expr>

// 判断 a 是否大于 b
int a = 10, b = 3;
int result = (a > b);

小于写法:小于比较 <expr> < <expr>

// 判断 a 是否小于 b
int a = 10, b = 3;
int result = (a < b);

逻辑运算符

逻辑与写法:逻辑与运算 <expr> && <expr>

// 短路逻辑与,左为假时右不执行
int a = 10, b = 0;
int result = (a > 0) && (b > 0);

逻辑或写法:逻辑或运算 <expr> || <expr>

// 短路逻辑或,左为真时右不执行
int a = 10, b = 0;
int result = (a > 0) || (b > 0);

逻辑非写法:逻辑非运算 !<expr>

// 逻辑取反
int a = 10;
int result = !(a > 0);

位运算符

按位与写法:按位与运算 <expr> & <expr>

// 按位与
int a = 6, b = 3;
int result = a & b;

按位或写法:按位或运算 <expr> | <expr>

// 按位或
int a = 6, b = 3;
int result = a | b;

按位异或写法:按位异或运算 <expr> ^ <expr>

// 按位异或
int a = 6, b = 3;
int result = a ^ b;

按位取反写法:按位取反运算 ~<expr>

// 按位取反
int a = 6;
int result = ~a;

左移写法:左移运算 <expr> << <n>

// 左移 1 位
int a = 6;
int result = a << 1;

右移写法:右移运算 <expr> >> <n>

// 右移 1 位
int a = 6;
int result = a >> 1;

位操作宏写法:检查某一位 #define <NAME>(x, pos) ((x) & (1U << (pos)))

// 检查指定位是否为 1
#define CHECK_BIT(x, pos) ((x) & (1U << (pos)))

位操作宏写法:设置某一位 #define <NAME>(x, pos) ((x) |= (1U << (pos)))

// 设置指定位为 1
#define SET_BIT(x, pos) ((x) |= (1U << (pos)))

位操作宏写法:清除某一位 #define <NAME>(x, pos) ((x) &= ~(1U << (pos)))

// 清除指定位为 0
#define CLEAR_BIT(x, pos) ((x) &= ~(1U << (pos)))

赋值运算符

基本写法:简单赋值 <var> = <expr>;

// 简单赋值
int a = 10;

复合写法:加赋值 <var> += <expr>;

// 等价于 a = a + b
int a = 10, b = 3;
a += b;

复合写法:减赋值 <var> -= <expr>;

// 等价于 a = a - b
int a = 10, b = 3;
a -= b;

复合写法:乘赋值 <var> *= <expr>;

// 等价于 a = a * b
int a = 10, b = 3;
a *= b;

复合写法:除赋值 <var> /= <expr>;

// 等价于 a = a / b
int a = 10, b = 3;
a /= b;

其他运算符

sizeof 写法:获取大小 sizeof(<type|var>)

// 获取 int 类型字节数
size_t size = sizeof(int);

取地址写法:获取变量地址 &<var>

// 获取变量地址
int a = 10;
int *p = &a;

解引用写法:通过指针访问值 *<ptr>

// 解引用指针获取值
int a = 10;
int *p = &a;
int val = *p;

三目写法:条件运算符 <condition> ? <expr1> : <expr2>

// 找出最大值
int a = 10, b = 3;
int max = (a > b) ? a : b;

逗号写法:逗号运算符 <expr1>, <expr2>, ..., <exprN>

// 从左到右执行,返回最后一个表达式的值
int c = (a = 5, b = 10, a + b);

表达式类型转换

隐式写法:自动类型转换 <type> <var> = <other_type_var>;

// int 转换为 float
int a = 10;
float result = a + 3.14f;

显式写法:强制类型转换 (<target_type>)<expression>

// 显式转换 double 为 int
double pi = 3.14159;
int area = (int)(pi * 5 * 5);

文件打开与关闭

基本写法:打开文件 FILE *<fp> = fopen("<filename>", "<mode>");

#include <stdio.h>
// 以只读方式打开文件
FILE *fp = fopen("data.txt", "r");

基本写法:关闭文件 fclose(<fp>);

// 关闭文件
fclose(fp);

错误检查写法:检查文件是否打开成功 if (<fp> == NULL) { ... }

// 检查文件是否成功打开
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
    perror("Failed to open file");
    return 1;
}

文件打开模式

只读写法:以只读方式打开 fopen("<filename>", "r")

// 只读模式打开文本文件
FILE *fp = fopen("data.txt", "r");

只写写法:以只写方式打开 fopen("<filename>", "w")

// 只写模式打开文件(覆盖)
FILE *fp = fopen("output.txt", "w");

追加写法:以追加方式打开 fopen("<filename>", "a")

// 追加模式打开文件
FILE *fp = fopen("log.txt", "a");

读写写法:以读写方式打开 fopen("<filename>", "r+")

// 读写模式打开文件
FILE *fp = fopen("data.txt", "r+");

二进制写法:以二进制方式打开 fopen("<filename>", "rb")

// 二进制只读模式打开
FILE *fp = fopen("data.bin", "rb");

字符读写

基本写法:读取单个字符 int <ch> = fgetc(<fp>);

// 从文件读取单个字符
int ch = fgetc(fp);

基本写法:写入单个字符 fputc(<ch>, <fp>);

// 向文件写入单个字符
fputc('A', fp);

EOF 写法:检测文件结束 while ((<ch> = fgetc(<fp>)) != EOF) { ... }

// 循环读取直到文件结束
int ch;
while ((ch = fgetc(fp)) != EOF) {
    putchar(ch);
}

字符串读写

基本写法:读取字符串 char *<result> = fgets(<buffer>, <size>, <fp>);

// 从文件读取一行字符串
char buffer[100];
fgets(buffer, sizeof(buffer), fp);

基本写法:写入字符串 fputs("<string>", <fp>);

// 向文件写入字符串
fputs("Hello World", fp);

格式化读写

基本写法:格式化读取 fscanf(<fp>, "<format>", &<var>);

// 从文件按格式读取
int age;
fscanf(fp, "%d", &age);

基本写法:格式化写入 fprintf(<fp>, "<format>", <values>);

// 向文件按格式写入
fprintf(fp, "Name: %s, Age: %d\n", "John", 30);

块读写

基本写法:读取数据块 size_t <count> = fread(<buffer>, <size>, <count>, <fp>);

// 从文件读取数据块
int data[10];
fread(data, sizeof(int), 10, fp);

基本写法:写入数据块 size_t <count> = fwrite(<buffer>, <size>, <count>, <fp>);

// 向文件写入数据块
int data[5] = {1, 2, 3, 4, 5};
fwrite(data, sizeof(int), 5, fp);

文件定位

基本写法:获取当前位置 long <pos> = ftell(<fp>);

// 获取当前文件位置
long pos = ftell(fp);

基本写法:设置文件位置 fseek(<fp>, <offset>, <origin>);

// 从文件开头偏移 10 字节
fseek(fp, 10, SEEK_SET);

基本写法:回到文件开头 rewind(<fp>);

// 将文件指针重置到开头
rewind(fp);

末尾写法:定位到文件末尾 fseek(<fp>, 0, SEEK_END);

// 定位到文件末尾
fseek(fp, 0, SEEK_END);

fgetpos 写法:获取文件位置 fgetpos(<fp>, &<pos>);

// 获取文件位置
fpos_t pos;
fgetpos(fp, &pos);

fsetpos 写法:设置文件位置 fsetpos(<fp>, &<pos>);

// 设置文件位置
fpos_t pos;
fsetpos(fp, &pos);

文件状态检查

基本写法:检查文件结束 feof(<fp>)

// 检查是否到达文件末尾
if (feof(fp)) {
    printf("End of file\n");
}

基本写法:检查文件错误 ferror(<fp>)

// 检查文件读写错误
if (ferror(fp)) {
    printf("File error\n");
}

基本写法:清除文件错误标志 clearerr(<fp>);

// 清除文件错误标志
clearerr(fp);

标准流

基本写法:使用标准输入 stdin

// 从标准输入读取
char buffer[100];
fgets(buffer, sizeof(buffer), stdin);

基本写法:使用标准输出 stdout

// 向标准输出写入
fputs("Hello\n", stdout);

基本写法:使用标准错误 stderr

// 向标准错误输出
fprintf(stderr, "Error message\n");

文件删除与重命名

基本写法:删除文件 remove("<filename>");

// 删除文件
remove("temp.txt");

基本写法:重命名文件 rename("<old_name>", "<new_name>");

// 重命名文件
rename("old.txt", "new.txt");

临时文件

基本写法:创建临时文件 FILE *<fp> = tmpfile();

// 创建临时文件(关闭后自动删除)
FILE *tmp = tmpfile();

基本写法:生成临时文件名 char *<name> = tmpnam(<buffer>);

// 生成临时文件名
char name[L_tmpnam];
tmpnam(name);

文件缓冲

基本写法:设置缓冲区 setvbuf(<fp>, <buffer>, <mode>, <size>);

// 设置全缓冲
char buffer[1024];
setvbuf(fp, buffer, _IOFBF, sizeof(buffer));

基本写法:刷新缓冲区 fflush(<fp>);

// 刷新文件缓冲区
fflush(fp);

获取文件大小

基本写法:通过 fseek 和 ftell 获取文件大小 fseek(<fp>, 0, SEEK_END); long <size> = ftell(<fp>);

// 获取文件大小
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
rewind(fp);

二进制文件读写

结构体写法:写入结构体到二进制文件 fwrite(&<struct_var>, sizeof(<StructType>), 1, <fp>);

// 将结构体写入二进制文件
typedef struct { int id; char name[50]; } Record;
Record r = {1, "John"};
fwrite(&r, sizeof(Record), 1, fp);

结构体读取写法:从二进制文件读取结构体 fread(&<struct_var>, sizeof(<StructType>), 1, <fp>);

// 从二进制文件读取结构体
Record r;
fread(&r, sizeof(Record), 1, fp);