strerror、GetLastError 和 errno 是 C/C++ 编程中用于错误检测与报告的三个重要机制,但它们来自不同的标准或平台,用途和使用方式有明显区别。下面详细解释它们的含义、来源、用法及差异:
1. errno(错误号变量)
含义:
errno是一个 全局整型变量 (在多线程环境中通常是线程局部存储),用于记录 C 标准库函数执行过程中发生的错误代码。- 它定义在
<errno.h>(C)或<cerrno>(C++)中。
特点:
- 初始值为 0(表示无错误)。
- 当某些标准库函数(如
fopen,malloc,strtol等)失败时,会将特定的错误码(如ENOENT,ENOMEM)写入errno。 - 不会自动清零 :成功调用不保证
errno被重置为 0,因此应在出错后立即检查。 - 错误码是 POSIX/ANSI C 标准定义的,具有跨平台性(但具体值可能因系统而异)。
示例:
c
#include <stdio.h>
#include <errno.h>
#include <string.h>
FILE* fp = fopen("nonexistent.txt", "r");
if (!fp) {
printf("Error code: %d\n", errno);
printf("Error message: %s\n", strerror(errno));
}
2. strerror
含义:
- 函数
char* strerror(int errnum)将errno类型的错误码转换为 人类可读的错误字符串。 - 定义在
<string.h>(C)或<cstring>(C++)中。
作用:
- 将
errno的数值(如 2)转为字符串(如"No such file or directory")。 - 通常与
errno配合使用。
示例:
c
printf("Error: %s\n", strerror(errno));
⚠️ 注意:
strerror不是线程安全的(返回静态缓冲区指针)。可使用strerror_r(POSIX)或strerror_s(C11/MSVC)替代以获得线程安全版本。
3. GetLastError(Windows 特有)
含义:
- Windows API 提供的函数,用于获取 最近一次调用 Windows API 函数 失败时的错误代码。
- 定义在
<windows.h>中。 - 返回类型为
DWORD(32 位无符号整数)。
特点:
- 仅适用于 Windows 平台。
- 只对 Windows API 函数 (如
CreateFile,RegOpenKey,WSAStartup等)有效,不适用于 C 标准库函数。 - 每个线程有自己的"最后错误"值(线程局部)。
- 成功调用某些 API 可能会重置错误码为 0,但并非所有函数都如此,因此应立即在失败后调用
GetLastError。
获取错误信息:
需配合 FormatMessage 将错误码转为字符串:
c
#include <windows.h>
#include <stdio.h>
HANDLE hFile = CreateFile("nonexistent.txt", ...);
if (hFile == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
LPSTR msg;
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&msg, 0, NULL
);
printf("Windows error: %lu - %s", err, msg);
LocalFree(msg);
}
三者的核心区别总结:
| 项目 | errno |
strerror |
GetLastError |
|---|---|---|---|
| 来源 | C 标准库 / POSIX | C 标准库 | Windows API |
| 平台 | 跨平台 | 跨平台 | 仅 Windows |
| 适用函数 | C 标准库函数(如 fopen, malloc) |
配合 errno 使用 |
Windows API 函数(如 CreateFile) |
| 类型 | 全局变量(int) | 函数(转错误码为字符串) | 函数(返回 DWORD 错误码) |
| 线程安全 | 通常是线程局部(现代实现) | 通常不安全 (用 strerror_r/s 替代) |
线程局部(安全) |
| 典型错误码 | ENOENT (=2), EINVAL (=22) |
--- | ERROR_FILE_NOT_FOUND (=2), ERROR_ACCESS_DENIED (=5) |
常见误区:
- ❌ 用
GetLastError()检查fopen()失败原因 → 错误! 应用errno。 - ❌ 用
errno检查CreateFile()失败原因 → 错误! 应用GetLastError()。 - ✅ 正确做法:根据调用的函数类型选择对应的错误机制。
补充:C++ 中的异常 vs 这些机制
- C++ 标准库(如
std::fstream)通常不设置errno,而是通过状态标志(如fail())或抛出异常(如果启用了异常)。 - Windows C++ 程序若混合使用 CRT 和 WinAPI,需同时处理
errno和GetLastError。
总结口诀:
标准库看
errno,WinAPI 查GetLastError;
strerror把errno变文字,
FormatMessage解GetLastError的码。
合理区分使用场景,才能准确诊断程序错误。