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

strerrorGetLastErrorerrno 是 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,需同时处理 errnoGetLastError

总结口诀:

标准库看 errno,WinAPI 查 GetLastError
strerrorerrno 变文字,
FormatMessageGetLastError 的码。

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

相关推荐
愚者游世6 分钟前
<algorithm> 中 remove、remove_if、remove_copy、remove_copy_if 详解
c++·学习·程序人生·职场和发展·visual studio
我头发还没掉光~17 分钟前
【C++写详细总结①】从for循环到算法初步
数据结构·c++·算法
雨落在了我的手上30 分钟前
C语言之数据结构初见篇(2):顺序表之通讯录的实现(续)
c语言·开发语言·数据结构
码不停蹄Zzz41 分钟前
对内存堆栈管理的简单理解[C语言]
c语言·开发语言
blackicexs42 分钟前
第八周第五天
数据结构·c++·算法
程序员学习随笔1 小时前
ext4 原理篇(三):日志子系统 Journal 深度剖析 —— 如何保障数据一致性?
linux·c++
OxyTheCrack1 小时前
【C++】一篇文章悲观锁与乐观锁与其思想在C++语言中的应用
linux·开发语言·数据库·c++·笔记
whycthe1 小时前
c++二叉树详解
数据结构·c++·算法
郝学胜-神的一滴1 小时前
循环队列深度剖析:从算法原理到C++实现全解析
开发语言·数据结构·c++·算法·leetcode
FirstFrost --sy1 小时前
仿mudou库one thread one loop式并发服务器实现
运维·服务器·开发语言·c++