当前位置: 首页 > backend >正文

【C/C++】errno/strerror 和 GetLastError()/FormatMessage 的区别

strerrorerrno 详解

printf("Error: %s\n", strerror(errno));

这行代码用于在 C 语言中输出系统错误信息,但它与 Windows 的 GetLastError() 有重要区别。下面我将详细解释每个部分及其工作原理。

1. 组件解析

errno

  • 定义errno 是一个全局整型变量(在 C11 标准中改为线程局部存储),定义在 <errno.h> 头文件中
  • 作用:存储标准 C 库函数执行失败时的错误代码
  • 特点
    • 由标准 C 库函数设置(如文件操作、内存分配等)
    • 不是 Windows API 错误(Windows API 使用 GetLastError()
    • 在多线程环境中,现代编译器通常将其实现为线程安全的

strerror

  • 原型char *strerror(int errnum);
  • 作用:将错误代码转换为可读的错误描述字符串
  • 参数:接收一个错误码(通常是 errno 的值)
  • 返回值:指向静态分配的字符串的指针,描述错误信息

2. 典型使用场景

#include <stdio.h>
#include <errno.h>
#include <string.h>int main() {FILE *fp = fopen("nonexistent.txt", "r");if (fp == NULL) {printf("Error: %s\n", strerror(errno));// 或使用 perror 更简单的方式:perror("Failed to open file");}return 0;
}

3. 与 Windows API GetLastError() 的区别

特性errno/strerrorGetLastError()/FormatMessage
来源标准 C 库函数设置Windows API 函数设置
头文件<errno.h>, <string.h><windows.h>
错误码范围标准 C 定义的错误码Windows 系统错误码
线程安全现代实现中是线程安全的本身就是线程特定的
典型错误示例ENOENT(2): 文件不存在ERROR_FILE_NOT_FOUND(2): 系统找不到文件
获取字符串的函数strerror()FormatMessage()

4. 常见问题

问题1:为什么我的 Windows API 错误用 strerror 显示不正确?

  • 原因:Windows API 错误应该使用 GetLastError() + FormatMessage()
  • 示例
    // 错误方式(不适用于Windows API错误)
    CloseHandle(invalid_handle);
    printf("%s\n", strerror(errno)); // 不会显示正确的Windows错误// 正确方式
    CloseHandle(invalid_handle);
    printf("Error: %d\n", GetLastError()); // 使用FormatMessage更好
    

问题2:errno 的值何时会被覆盖?

  • 规则
    1. 成功的库函数调用不会修改 errno(标准规定)
    2. 某些实现可能在成功调用时将 errno 设为 0
    3. 应该立即在函数调用失败后检查 errno

问题3:strerror 的返回值有效期是多久?

  • 答案
    • 返回指向静态字符串的指针,不需要释放
    • 但后续调用可能会覆盖该内容(非线程安全实现中)
    • 在 C11 中可以使用 strerror_s 更安全的版本

5. 替代方案

(1) perror() - 更简单的打印方式

fopen("nonexistent.txt", "r");
if (errno) {perror("File open failed"); // 输出: "File open failed: No such file or directory"
}

(2) strerror_r (线程安全版本, POSIX)

char buf[256];
strerror_r(errno, buf, sizeof(buf));
printf("Error: %s\n", buf);

(3) C11 的 strerror_s

char buf[256];
strerror_s(buf, sizeof(buf), errno);
printf("Error: %s\n", buf);

6. 典型 errno 错误码示例

错误码描述
EPERM1操作不允许
ENOENT2文件或目录不存在
EIO5输入/输出错误
EBADF9错误的文件描述符
EAGAIN11资源暂时不可用,请重试
EACCESS13权限不足
EFAULT14错误的地址
EBUSY16设备或资源忙
EEXIST17文件已存在
EINVAL22无效参数
ENOSPC28设备无剩余空间

7. 最佳实践建议

  1. 在 Windows 编程中

    • 对于 Win32 API 错误,始终使用 GetLastError() + FormatMessage()
    • 对于标准 C 库函数,可以使用 errno + strerror()
  2. 跨平台编程

    #ifdef _WIN32
    // Windows 错误处理
    #else
    // POSIX 错误处理
    #endif
    
  3. 错误处理模板

    #include <stdio.h>
    #include <errno.h>
    #include <string.h>void print_error(const char *context) {#ifdef _WIN32DWORD error = GetLastError();LPSTR buffer = NULL;FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,NULL, error, 0, (LPSTR)&buffer, 0, NULL);fprintf(stderr, "%s: %s (code %d)\n", context, buffer, error);LocalFree(buffer);#elsefprintf(stderr, "%s: %s (code %d)\n", context, strerror(errno), errno);#endif
    }
    

理解这些区别对于正确诊断和报告系统错误至关重要,特别是在混合使用标准库函数和平台特定API时。

http://www.xdnf.cn/news/4442.html

相关文章:

  • 模拟设计中如何减小失配
  • 4.系统定时器基本定时器
  • 操作系统——第四章(文件的物理结构以及与逻辑结构的对比)
  • Redis相关命令详解与原理
  • 【Agent】使用 Python 结合 OpenAI 的 API 实现一个支持 Function Call 的程序,修改本机的 txt 文件
  • 如何检查 Watchtower 是否正常工作及更新未生效的排查方法【日常排错】
  • 探寻程序开发的个人密码
  • excel 批量导出图片并指定命名
  • Excel点击单元格内容消失
  • 龙虎榜——20250507
  • LVGL -meter的应用
  • phpstudy升级新版apache
  • 如何在金仓数据库KingbaseES中新建一个数据库?新建一个表?给表添加一个字段?
  • 【PostgreSQL数据分析实战:从数据清洗到可视化全流程】8.1 基础图表绘制(折线图/柱状图/散点图)
  • 把本地的文件拷贝到wsl的文件夹下或者 wsl读取本地的文件
  • 使用node.js创建一个简单的服务器
  • WD5040L、 7V 至 37V 的宽输入电压,输出电压范围为 3.3V 至 36V,6A输出、保护功能强,电源管理利器!
  • Redis的缓存穿透、缓存击穿和缓存雪崩
  • ai说什么是注解,并以angular ts为例
  • Go——项目实战
  • 【强化学习】强化学习算法 - 多臂老虎机问题
  • 精益数据分析(47/126):深挖UGC商业模式的关键要点与指标
  • 多模态大语言模型arxiv论文略读(六十二)
  • uniapp自定义底部导航栏h5有效果小程序无效的解决方案
  • 鞅与停时 - 一种特别的概率论问题
  • 讲解什么是快充诱骗协议芯片及它的工作原理和应用场景
  • 构建生命大模型,开拓教育新境界——启智书院举办十二周年庆典暨教育新生态跨界共拓峰会
  • 【存储管理—动态不等长存储资源分配算法】
  • 可执行文件格式(ELF格式)以及进程地址空间第二讲【Linux操作系统】
  • 【django.db.utils.OperationalError: unable to open database file】