【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.mzph.cn/news/904488.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unicode和UTF - 8主要有以下区别

Unicode和UTF - 8主要有以下区别 概念范畴 Unicode:是字符集 。它为世界上几乎所有的字符(包括各国文字、标点符号、特殊符号等)分配了唯一的编号,这个编号也叫码位、码点,比如“中”字的Unicode码点是U+4E2D 。它规定了字符的抽象表示,只关注字符与数字编号的对应关系,…

企业数字化转型第二课:接受不完美(1/2)

一.引言 先看一组中国企业数字化转型相关的数据&#xff1a; 战略认知层面&#xff1a;92%中国企业将数字化纳入战略核心&#xff08;麦肯锡2023&#xff09;执行困境层面&#xff1a;63%企业转型首年遭遇重大挫折&#xff08;BCG 2024追踪&#xff09;价值释放周期&#xff1…

OSCP - Proving Grounds - Sumo

主要知识点 ShellShock漏洞dirtycow提权 具体步骤 执行nmap扫描,比较直观&#xff0c;22和80端口开放&#xff0c;但是80端口没有什么内容 Nmap scan report for 192.168.210.87 Host is up (0.44s latency). Not shown: 65533 closed tcp ports (reset) PORT STATE SERV…

pyqt写一个TCP(UDP)检测工具

先用电脑连接到目标WIFI&#xff0c;再运行以下代码。 import sys from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5.QtNetwork import *class NetTestTool(QWidget):def __init__(self):super().__init__()self.init_ui()self.tcp_socket QTcpSocket()…

趣味编程:梦幻万花筒

目录 1.效果展示 2.源码展示 3.代码逻辑详解 3.1 头文件与宏定义 3.2 HSV函数转RGB颜色函数 3.3 主函数 初始化部分 循环部分 线条绘制部分 刷新和延时部分 结束部分 4.小结 本篇博客主要介绍趣味编程用C语言实现万花筒小程序。 1.效果展示 2.源码展示 #define…

软件开发各阶段的自动化测试技术详解

引言 在当今快速迭代的软件开发环境中&#xff0c;自动化测试已成为保证软件质量、提高测试效率的重要手段。本文将深入探讨软件开发生命周期各个阶段的自动化测试技术&#xff0c;包括单元测试、代码级集成测试、Web Service测试和GUI测试的自动化实现方法。 单元测试的自动…

Elasticsearch:我们如何在全球范围内实现支付基础设施的现代化?

作者&#xff1a;来自 Elastic Kelly Manrique SWIFT 和 Elastic 如何应对基础设施复杂性、误报问题以及日益增长的合规要求。 金融服务公司在全球范围内管理实时支付方面面临前所未有的挑战。SWIFT&#xff08;Society for Worldwide Interbank Financial Telecommunication -…

day009-用户管理专题

文章目录 1. 创建包含时间的文件2. 与用户相关的文件3. 用户分类4. 与用户相关的命令4.1 添加用户4.2 删除用户4.3 查看用户4.4 修改用户密码 5. sudo6. 思维导图7. 老男孩思想-学习方法 1. 创建包含时间的文件 或$()是替换符号&#xff0c;可以将命令的结果作为字符串或变量的…

shell脚本实现远程重启多个服务器

直接deepseek帮写脚本 remoteReboot.sh #!/bin/bash # 配置文件路径&#xff08;格式&#xff1a;每行一个服务器地址&#xff09; SERVER_FILE"servers.list" # 读取服务器列表 mapfile -t SERVERS < "$SERVER_FILE" for server in "${SERVER…

如何利用 QuickAPI 生成 PostgreSQL 样本测试数据:全面解析与实用指南

目录 一、什么是 QuickAPI&#xff1f; 二、为什么需要生成样本测试数据&#xff1f; 三、如何在 QuickAPI 中生成 PostgreSQL 样本测试数据&#xff1f; 1. 登录 QuickAPI 平台 2. 选择 PostgreSQL 数据库和目标表 3. 配置样本数据生成规则 4. 导出或直接插入数据 四、…

黑马点评day04(分布式锁-setnx)

4、分布式锁 4.1 、基本原理和实现方式对比 分布式锁&#xff1a;满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁&#xff0c;只要大家使用的是同一把锁&#xff0c;那么我们就能锁住线程&#xff0c;不让线程并行&#x…

‌人工智能在农作物病虫害识别中的应用前景分析

近年来&#xff0c;全球气候变化加剧、农业种植规模化发展&#xff0c;农作物病虫害对粮食安全的威胁日益凸显。据统计&#xff0c;全球每年因病虫害造成的农作物损失约占总产量的20%-40%&#xff0c;而传统依赖人工经验的防治方式效率低、成本高&#xff0c;难以满足现代农业需…

C++ 完美转发

C 完美转发逐步详解 1. 问题背景与核心目标 在 C 模板编程中&#xff0c;若直接将参数传递给其他函数&#xff0c;参数的 值类别&#xff08;左值/右值&#xff09;和 类型信息&#xff08;如 const&#xff09;可能会丢失。例如&#xff1a; template<typename T> voi…

Midjourney 绘画 + AI 配音:组合玩法打造爆款短视频!

一、引言:AI 重构短视频创作范式 在某短视频工作室的深夜剪辑室里,资深编导正在为一条古风剧情视频发愁:预算有限无法实拍敦煌场景,人工绘制分镜耗时 3 天,配音演员档期排到一周后。而使用 Midjourney 生成敦煌壁画风格的场景图仅需 15 分钟,AI 配音工具实时生成多角色台…

AI基础知识(02):机器学习的任务类型、学习方式、工作流程

03 机器学习(Machine Learning)的任务类型与学习方式 广义的机器学习主要是一个研究如何让计算机通过数据学习规律,并利用这些规律进行预测和决策的过程。这里的Machine并非物理意义上的机器,可以理解为计算机软硬件组织;Learning可以理解为一个系统或平台经历了某些过程…

数据结构、刷leetcode返航版--二分5/7

1.排序 快排&#xff1a; 第一章 基础算法&#xff08;一&#xff09; - AcWing 如何调整范围 经典二分 递归结束条件&#xff1b;条件满足时&#xff0c;进行处理&#xff1b;递归左边&#xff0c;递归右边 分界点划分可以是l,r,(lr)/2,但是如果是选l&#xff0c;比如是1…

LeetCode 267:回文排列 II —— Swift 解法全解析

文章目录 摘要描述题解答案题解代码分析统计字符频率判断是否可能构成回文构建半边字符数组回溯生成半边排列 示例测试及结果时间复杂度空间复杂度实际使用场景&#xff1a;回文排列在真实项目里能干啥&#xff1f;文本处理、数据清洗类系统游戏开发&#xff1a;名字合法性验证…

JumpServer批量添加资产

环境说明&#xff1a;我的环境是H3C网络设备环境 一、在linux系统环境下通过Python脚本获取交换机信息&#xff0c;IP地址和设备名称一一对应&#xff0c;脚本如下&#xff1a; cat get_device-sysname.py import re from netmiko import ConnectHandler from concurrent.fut…

理解字、半字与字节 | 从 CPU 架构到编程实践的数据类型解析

注&#xff1a;本文为 “字、半字、字节” 相关文章合辑。 略作重排&#xff0c;未全校。 如有内容异常&#xff0c;请看原文。 理解计算机体系结构中的字、半字与字节 在计算机科学中&#xff0c;理解“字 (Word)”、“半字 (Half-Word)”和“字节 (Byte)”等基本数据单元的…

数据库实验10 函数存储

数据库实验10 一、实验目的 掌握函数和存储过程的定义方法&#xff0c;包括标量函数、表值函数、存储过程的语法结构。理解函数和存储过程的作用及原理&#xff0c;区分标量函数与表值函数的应用场景&#xff0c;掌握存储过程的参数传递、逻辑控制和错误处理机制。能够熟练运…