20 · 未定义行为与安全函数
什么是未定义行为(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 的习惯
- 所有
malloc、fopen、系统调用立刻检查返回值。 - 所有指针初始化,所有缓冲区操作带上界。
- 编译时开警告:
gcc -Wall -Wextra -Werror,把警告当错误。 - 调试期开
-fsanitize=address,undefined。
关联
09-指针 · 11-字符串 · 19-内存管理 · 17-类型转换与错误处理