6.4 错误处理与安全函数

本篇学习返回值检查、`errno`、`perror`、`strerror` 和常见安全替代方案,并能写出更稳妥的 C 程序。

错误处理与安全函数

概念说明

C 语言默认没有像很多高级语言那样统一的异常机制。 大多数标准库函数会通过返回值告诉你“成功还是失败”,必要时再借助 errno 补充更具体的错误原因。

所以,C 里的错误处理重点不是“出错以后再说”,而是每次调用都及时检查结果。 文件操作、内存申请、字符串处理、格式化输入输出,都是最应该养成这个习惯的地方。

这里所谓的“安全函数”,更准确地说,是“边界意识更强的写法”。 有时它表现为使用 snprintffgetsstrtol 这类更容易做长度和错误检查的接口;有时也包括某些平台提供的 _s 系列函数,但后者并不是所有环境都统一支持。

语法/规则

  1. 调用可能失败的函数后,要先检查返回值,再决定是否继续执行。
  2. errno 只有在函数已经明确失败时才有意义,不应在成功路径里随意读取它。
  3. 如果错误信息后面还要继续使用,最好先把 errno 保存到局部变量中,再调用 strerrorperror
  4. 错误信息应优先输出到 stderr,不要和正常结果混在 stdout 中。
  5. 程序结束时可用 EXIT_SUCCESSEXIT_FAILURE 表达退出状态,比直接写数字更清晰。
  6. 读取字符串时,fgets 往往比无长度控制的 gets%s 更稳妥。
  7. 格式化拼接字符串时,snprintf 通常比 sprintf 更适合做边界控制。
  8. 解析数字时,strtolstrtod 一般比 atoiatof 更容易检查错误。
  9. _s 系列函数属于可选或平台相关能力,写可移植代码前应先确认支持情况。

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    FILE *fp = fopen("not-exist.txt", "r");
    if (fp == NULL) {
        int err = errno;
        fprintf(stderr, "errno = %d\n", err);
        perror("fopen");
        fprintf(stderr, "message = %s\n", strerror(err));
        return EXIT_FAILURE;
    }

    fclose(fp);
    return EXIT_SUCCESS;
}

输出结果:

1
2
3
errno = 2
fopen: No such file or directory
message = No such file or directory

不同平台下的错误号和错误文本可能略有差异,但整体用法是一致的。

常见错误

  1. 读取 errno 之前不先确认函数是否失败,导致把旧错误当成新错误。
  2. 直接把错误输出到 stdout,让正常输出和错误输出混在一起。
  3. 使用 sprintfstrcpystrcat 这类接口时完全不考虑目标缓冲区大小。
  4. 直接用 atoi 解析用户输入,却没有检测转换失败或溢出的情况。
  5. 以为 _s 后缀函数在所有平台都可直接使用,结果换编译器后无法通过编译。
本文禁止转载
使用 Hugo 构建
主题 StackJimmy 设计 由 Hobin 魔改
最近构建时间:2026-04-17 19:07:48 CST
载入天数...载入时分秒...
发表了 1 篇文章 · 发表了 152 篇笔记 · 总计 18 万 0 千字