【C/C++】strerror、GetLastError 和 errno 的含义和区别?

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 的码。

合理区分使用场景,才能准确诊断程序错误。

相关推荐
老王爱玩车2 小时前
预处理详解
c语言·开发语言·学习
学逆向的3 小时前
C 控制台指令
c语言·开发语言·网络安全
沫璃染墨3 小时前
从零入门数据结构与算法(一):复杂度分析——从算法效率到时间与空间复杂度
c语言·开发语言·数据结构·笔记·算法·飞书
想做小南娘,发现自己是女生喵3 小时前
第三周学习文档 Linux C 多进程与多线程
linux·c语言·学习
Lzg_na3 小时前
僵尸进程、孤儿进程、守护进程:含义、区别与生命周期详解
c++
zhanghaha13144 小时前
C++ 的类是什么(新手保姆讲解,配合 Qt 对照理解)
c++
aqiu1111114 小时前
【C++算法打怪专栏】LeetCode 402. 移掉 K 位数字(单调栈 + 贪心)
c++·算法·leetcode
(Charon)4 小时前
【C++面试】线程并发与异步:区别、实现原理及使用场景
开发语言·c++
JMchen5 小时前
实战项目一——实现一个高性能图片滤镜App
android·c++·性能优化
听风若依5 小时前
函数栈帧的创建与销毁-重置版
c语言·学习