20 · 未定义行为与安全函数

2026.09.27 · 🟡 中

什么是未定义行为(UB)

标准没有规定结果的行为 —— 编译器可以做任何事:崩溃、静默出错、或看起来正常。 这是 C 最危险的地方:「能跑」不等于「正确」。

常见 UB 清单

UB 例子
数组越界 int a[5]; a[10] = 1;
解引用野指针 / NULL int *p; *p = 1;
悬空指针 free 后再用
有符号整数溢出 INT_MAX + 1
读未初始化变量 int x; printf("%d", x);
同一表达式修改同一变量 i = i++ + ++i;
除以 0 int a = 1 / 0;
字符串缺 '\0' strlen 读到越界
修改字符串字面量 char *p="hi"; p[0]='H';

不安全的经典函数 → 安全替代

❌ 不安全 ✅ 替代
gets(buf) fgets(buf, size, stdin)
strcpy(dst, src) strncpy / snprintf / strcpy_s
strcat(dst, src) strncat / strcat_s
scanf("%s", buf) scanf("%19s", buf) 限宽 或 fgets
sprintf(buf, ...) snprintf(buf, size, ...)
memcpy 重叠 memmove

核心原则:凡是往缓冲区写数据的操作,都要把缓冲区大小传进去,并且检查够不够。

C11 的安全函数(Annex K)

fopen_s / strcpy_s / strcat_s / gets_s 等,带 _s 后缀,强制传缓冲区大小。 ⚠️ 并非所有编译器都支持,跨平台时慎用。

编写健壮 C 的习惯

  1. 所有 malloc、fopen、系统调用立刻检查返回值。
  2. 所有指针初始化,所有缓冲区操作带上界。
  3. 编译时开警告:gcc -Wall -Wextra -Werror,把警告当错误。
  4. 调试期开 -fsanitize=address,undefined。

关联

09-指针 · 11-字符串 · 19-内存管理 · 17-类型转换与错误处理