<你想看的我这里都有😎 >
前言
在开始学习之前,先向各位推荐c和c++指令手册的官方网站:C语言|C++中文网 (c-cpp.com)
其次关于C语言的错误处理一共分为三个部分:错误号处理方式、断言处理方式、信号处理方式,非局部跳转处理方式我们将这四部分分为四篇文章来写......
错误号处理方式
errno.h头文件
文件说明:是一个标准C库头文件
概念:标准库中的一些函数通过向<errno.h>中声明的整形全局变量errno存储一个错误码(1、2、3等数字)来表示有错误发生,大部分使用errno变量的函数集中在<math.h>中,但也有一些在标准库的其他部分,并定义了一组与错误码相关的宏(EACCES
、EINVAL
、ENOMEM
等):
//由于是宏定义,所以将EACCES等宏存储至errno中时就相当于将这些宏所对应的错误码(数字)存放在errno中 #define EACCES 数字
功能:检查调用标准库中函数的操作是否成功
简述运行过程:
1、当我们要调用一个库函数,该库函数不论是正常运行还是非正常运行都会在最后给errno变量赋值一个宏(实际上是一个错误码),若赋值结果不为0则表示函数调用过程中出错
注意事项:
1、在函数调用前将errno置为零非常重要,虽然程序刚开始时errno的值默认为0,但有可能在随后的函数调用中已经被改动了,而库函数不会将errno清零,这就需要我们人为修改了:
//一系列操作 .... //人为置零 errno = 0; //再次函数调用 y = sqrt(x); //判断调用是否成功 if(errno != 0) { fprintf(stderr,"sqrt error; program terminated.\n") //以标准错误流stderr输出信息 exit (EXIT_FAILURE); //程序退出 }
2、当发生错误时,向errno中存储的值通常是EDOM和ERANGE,它们代表调用数学函数时可能发生的两种错误(这也是为什么说大部分使用errno变量的函数集中在<math.h>中),C99中还增加了EILSEQ宏,它主要是将一些特定头文件(尤其是<wchar.h>头文件)中的库函数在发生编码错误时将EILSEQ的值存储到errno中:
EDOM |
函数参数超出范围,例如:sqrt(-1) |
EILSEQ (C99) |
不合法的字符顺序,例如:wcstombs(str, L"\xffff", 2) |
ERANGE |
函数结果超出范围,例如:strtol("0xfffffffff",NULL,0) |
3、一些函数可能会同时导致多种错误,可以用errno分别于它们比较然后确定到底发生了什么错误
简单案例:
函数操作失败:打印errno的值为2,利用strerror函数接收errno的值并打印出具体的问题
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> int main() { FILE* file = fopen("nonexistent.txt", "r"); if (file == NULL) { printf("Error opening file. Errno: %d\n", errno); // 或者使用 strerror 函数打印对应的错误描述信息 printf("Error message: %s\n", strerror(errno)); } return 0; }
函数操作成功:打印errno的值为0,利用strerror函数接收errno的值并打印出不存在问题
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> int main() { FILE* file = fopen("nonexistent.txt", "r"); if (file != NULL) { printf("Error opening file. Errno: %d\n", errno); // 或者使用 strerror 函数打印对应的错误描述信息 printf("Error message: %s\n", strerror(errno)); } return 0; }
error.h头文件
一般情况下,在VS中使用该头文件需要下载GUN C库
文件说明:是一个 GNU C 库(glibc)中的头文件
概念:该头文件声明了扩展错误处理函数和相关宏,其中最常见且重要的是erorr和error_at_line函数,这些函数允许以类似于格式化字符串输出 (printf
) 的方式生成自定义格式或包含更多详细信息(如源代码位置等)的出错消息
功能:生成自定义格式或包含更多详细信息的出错消息
error.h头文件中包含了两个函数:
//error()函数 extern void error (int __status, int __errnum, const char *__format, ...) __attribute__ ((__format__ (__printf__, 3, 4))); //error_at_line()函数 extern void error_at_line (int __status, int __errnum, const char *__fname, unsigned int __lineno, const char *__format, ...) __attribute__ ((__format__ (__printf__, 5, 6)));
参数解释:
- _status:状态码,代表整体程序/调用者关心是否成功执行,零表示成功,非零表示不成功,
状态码可以自定义,也可以是约定好的标准状态码,一些常见的标准错误码包括:
!不推荐自定义!
- 1:一般性未知错误
- 2:误用 shell 命令时引起不支持选项等
- 126:命令不可执行(例如权限问题)
- 127:没有找到命令
- 128 + N(N为信号编号):由于收到信号而导致进程终止
- _errnum:错误码,代表具体某次操作所产生/涉及到/引发了什么样类型和内容方面问题(一般都用errno的值表示)
- _format:格式化字符串和可变数量的参数,用于打印出错消息
- _fname:包含出错位置信息(如源代码文件名)作为字符串常量(系统自动添加)
- _lineno:包含出错位置信息(如源代码行号)作为无符号整数值(系统自动添加)
关于的”__attribute__“解释:
内容说明:是一种 GNU C 扩展属性
作用:告诉编译器函数的参数列表和格式字符串之间的关系,以便进行编译时的格式检查
参数解释:
__format__
是一个特殊属性名称,后面跟着两个整数值:第一个整数表示从第几个参数开始是格式字符串(通常是形如printf()
或scanf()
的函数中的第一个可变参数),第二个整数表示从该位置开始应用类型检查:
void my_printf(const char *fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
上述代码定义了一个名为
my_printf
的函数,并使用了该属性。其中"fmt"
参数是作为格式字符串使用(即模拟类似于标准库函数printf()
的行为)。数字 "1" 表示"fmt"
参数在参数列表中位于索引位置1 (从0开始计算),而数字 "2" 表示类型检查应该从索引位置2 开始应用。即打印时先打印fmt后面的内容再打印fmt的内容。通过将此属性添加到函数声明或定义上,编译器可以对调用该函数时传递给它们的实际参数与预期的格式进行比较,并发出警告或错误消息以帮助捕获潜在的格式错误。
关于“属性”的解释:
在 C 语言中,属性是用于向编译器提供额外信息或指示的机制。它们以特殊的语法形式出现,并通过将属性应用于声明或定义来修改其行为或注释代码。
实例一:
//使用<error.h>头文件中的error函数 #define _GNU_SOURCE // 需要包含此宏定义以启用 glibc 扩展功能 #include <stdio.h> #include <stdlib.h> #include <errno.h> void process_data(int value) { if (value < 0) { error(EXIT_FAILURE, errno, "Invalid value: %d", value); } //error(-1,建议使用errno,“如何显示提示信息可以自定义比如:File error:”,%d对应的值) // 处理数据... } int main() { int data = -1; process_data(data); return 0; }
//输出结果 Invalid value: %d: Invalid argument
其实如果想要将输出结果格式化的话可以这样描述:
<自定义错误消息>: <变量值>: <与错误码相关的具体描述信息>
至于状态码的值,一般来说都会取非零值
实例二:
//使用<error.h>头文件中的error_at_line函数 //使用error_at_line()实现在即显示错误原因的同时又显示行号 #define _GNU_SOURCE // 需要包含此宏定义以启用 glibc 扩展功能 #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <error.h> int main() { FILE *file = fopen("nonexistent.txt", "r"); if (file == NULL) { error_at_line(errno, errno, __FILE__, __LINE__, "Failed to open file"); } // 其他操作... fclose(file); return 0; }
//输出结果 ./example: Failed to open file: No such file or directory (Error 2) at example.c:11
其实如果想要将输出结果格式化的话可以这样描述:
<当前程序的名称> :<自定义错误信息> : <错误码对应信息> (错误码) at <当前程序名> : 问题行号
error.h与errno.h的区别
<errno.h>
提供了全局变量和宏来处理基本级别(底层)上发生的各种系统和库函数调用产生额外信息<error.h>
则提供了更高级别、灵活性更强的错误处理函数和宏,用于生成自定义格式或包含更多详细信息的出错消息。需要注意的是,<error.h>
是 GNU C 特定库(glibc)提供的功能,在其他平台或编译器环境中可能不存在或有不同实现。因此,在使用时应谨慎考虑可移植性和目标平台兼容性<error.h>
头文件提供了一些高级别且更灵活的错误处理函数,如error
和error_at_line
这些函数可以用于生成自定义格式或包含更多详细信息的出错消息。然而,在实际开发中,通常会使用<errno.h>
头文件中定义的errno
全局变量和相关宏来报告和处理错误。这是因为标准库函数在执行失败时会将适当的错误码设置到errno
变量,并且许多标准库函数都依赖于该机制。使用errno
的好处是它提供了一种统一、标准化的方式来表示和传递错误信息。通过查阅<errno.h>
中定义的宏,可以获得与特定错误代码对应的文本描述,并根据需要进行相应处理。虽然自定义状态码或其他高级别方法可能具有更大灵活性和扩展性,但直接使用errno
是最常见、最简单并与现有代码兼容度较高的方式
补充内容:
1~34号错误码在Linux中的/usr/include/asm-generic/errno-base.h目录下,使用cat指令查看:
#define EPERM 1 /*不允许操作*/ #define EPERM 1 /* Operation not permitted */ #define ENOENT 2 /*没有这样的文件或目录*/ #define ENOENT 2 /* No such file or directory */ #define ESRCH 3 /* 没有这样的进程 */ #define ESRCH 3 /* No such process */ #define EINTR 4 /*中断的系统调用*/ #define EINTR 4 /* Interrupted system call */ #define EIO 5 /* I/O错误*/ #define EIO 5 /* I/O error */ #define ENXIO 6 /*没有这样的设备或地址*/ #define ENXIO 6 /* No such device or address */ #define E2BIG 7 /* Arg list太长*/ #define E2BIG 7 /* Arg list too long */ #define ENOEXEC 8 /*执行格式错误*/ #define ENOEXEC 8 /* Exec format error */ #define EBADF 9 /*错误的文件号*/ #define EBADF 9 /* Bad file number */ #define ECHILD 10 /*没有子进程*/ #define ECHILD 10 /* No child processes */ #define EAGAIN 11 /*重试*/ #define EAGAIN 11 /* Try again */ #define ENOMEM 12 /*内存不足*/ #define ENOMEM 12 /* Out of memory */ #define EACCES 13 /*权限被拒绝*/ #define EACCES 13 /* Permission denied */ #define EFAULT 14 /*错误地址*/ #define EFAULT 14 /* Bad address */ #define ENOTBLK 15 /*需要的块设备*/ #define ENOTBLK 15 /* Block device required */ #define EBUSY 16 /*设备或资源忙*/ #define EBUSY 16 /* Device or resource busy */ #define EEXIST 17 /*文件存在*/ #define EEXIST 17 /* File exists */ #define EXDEV 18 /*跨设备链接*/ #define EXDEV 18 /* Cross-device link */ #define ENODEV 19 /*没有这样的设备*/ #define ENODEV 19 /* No such device */ #define ENOTDIR 20 /*不是一个目录*/ #define ENOTDIR 20 /* Not a directory */ #define EISDIR 21 /*是一个目录*/ #define EISDIR 21 /* Is a directory */ #define EINVAL 22 /*无效参数*/ #define EINVAL 22 /* Invalid argument */ #define ENFILE 23 /*文件表溢出*/ #define ENFILE 23 /* File table overflow */ #define EMFILE 24 /*打开的文件太多*/ #define EMFILE 24 /* Too many open files */ #define ENOTTY 25 /*不是打字机*/ #define ENOTTY 25 /* Not a typewriter */ #define ETXTBSY 26 /*文本文件繁忙*/ #define ETXTBSY 26 /* Text file busy */ #define EFBIG 27 /*文件太大*/ #define EFBIG 27 /* File too large */ #define ENOSPC 28 /*设备上没有空格*/ #define ENOSPC 28 /* No space left on device */ #define ESPIPE 29 /*非法查找*/ #define ESPIPE 29 /* Illegal seek */ #define EROFS 30 /*只读文件系统*/ #define EROFS 30 /* Read-only file system */ #define EMLINK 31 /*链接太多*/ #define EMLINK 31 /* Too many links */ #define EPIPE 32 /*破裂的管道*/ #define EPIPE 32 /* Broken pipe */ #define EDOM 33 /*数学参数的域的func */ #define EDOM 33 /* Math argument out of domain of func */ #define ERANGE 34 /*数学结果不可表示*/ #define ERANGE 34 /* Math result not representable */
35~134号错误码在Linxu中的/usr/include/asm-generic/errno.h目录下,使用cat指令查看:
#define EDEADLK 35 /*将发生资源死锁*/ #define EDEADLK 35 /* Resource deadlock would occur */ #define ENAMETOOLONG 36 /*文件名太长*/ #define ENAMETOOLONG 36 /* File name too long */ #define ENOLCK 37 /*没有可用的记录锁*/ #define ENOLCK 37 /* No record locks available */ #define ENOSYS 38 /*函数未实现*/ #define ENOSYS 38 /* Function not implemented */ #define ENOTEMPTY 39 /*目录不为空*/ #define ENOTEMPTY 39 /* Directory not empty */ #define ELOOP 40 /*遇到太多符号链接*/ #define ELOOP 40 /* Too many symbolic links encountered */ #define EWOULDBLOCK EAGAIN /*操作将阻塞*/ #define EWOULDBLOCK EAGAIN /* Operation would block */ #define ENOMSG 42 /*没有指定类型的消息*/ #define ENOMSG 42 /* No message of desired type */ #define EIDRM 43 /*标识符删除*/ #define EIDRM 43 /* Identifier removed */ #define ECHRNG 44 /*通道号超出范围*/ #define ECHRNG 44 /* Channel number out of range */ #define EL2NSYNC 45 /* 2级未同步 */ #define EL2NSYNC 45 /* Level 2 not synchronized */ #define EL3HLT 46 /* 3级停止 */ #define EL3HLT 46 /* Level 3 halted */ #define EL3RST 47 /* 3级复位*/ #define EL3RST 47 /* Level 3 reset */ #define ELNRNG 48 /*链路数超出范围*/ #define ELNRNG 48 /* Link number out of range */ #define EUNATCH 49 /*协议驱动程序未连接*/ #define EUNATCH 49 /* Protocol driver not attached */ #define ENOCSI 50 /*没有CSI结构可用*/ #define ENOCSI 50 /* No CSI structure available */ #define EL2HLT 51 /*二级暂停*/ #define EL2HLT 51 /* Level 2 halted */ #define EBADE 52 /*无效的交换*/ #define EBADE 52 /* Invalid exchange */ #define EBADR 53 /*无效请求描述符*/ #define EBADR 53 /* Invalid request descriptor */ #define EXFULL 54 /* 完整交换 */ #define EXFULL 54 /* Exchange full */ #define ENOANO 55 /*无阳极*/ #define ENOANO 55 /* No anode */ #define EBADRQC 56 /*无效请求码*/ #define EBADRQC 56 /* Invalid request code */ #define EBADSLT 57 /*无效槽位*/ #define EBADSLT 57 /* Invalid slot */ #define EDEADLK #define EDEADLOCK EDEADLK #define EBFONT 59 /*错误的字体文件格式*/ #define EBFONT 59 /* Bad font file format */ #define ENOSTR 60 /*设备不是流*/ #define ENOSTR 60 /* Device not a stream */ #define ENODATA 61 /*没有可用的数据*/ #define ENODATA 61 /* No data available */ #define ETIME 62 /* 计时器过期 */ #define ETIME 62 /* Timer expired */ #defien ENOSR 63 /*流出流资源*/ #define ENOSR 63 /* Out of streams resources */ #define ENONET 64 /*机器不在网络上*/ #define ENONET 64 /* Machine is not on the network */ #define ENOPKG 65 /*未安装的包*/ #define ENOPKG 65 /* Package not installed */ #define EREMOTE 66 /*对象是远程*/ #define EREMOTE 66 /* Object is remote */ #define ENOLINK 67 /*链接已被切断*/ #define ENOLINK 67 /* Link has been severed */ #define EADV 68 /*发布错误*/ #define EADV 68 /* Advertise error */ #define ESRMNT 69 /* Srmount错误*/ #define ESRMNT 69 /* Srmount error */ #define ECOMM 70 /*发送通信错误*/ #define ECOMM 70 /* Communication error on send */ #define EPROTO 71 /*协议错误*/ #define EPROTO 71 /* Protocol error */ #define EMULTIHOP 72 /*尝试多跳*/ #define EMULTIHOP 72 /* Multihop attempted */ #define EDOTDOT 73 /* RFS特定错误*/ #define EDOTDOT 73 /* RFS specific error */ #define EBADMSG 74 /*不是数据消息*/ #define EBADMSG 74 /* Not a data message */ #define EOVERFLOW 75 /*对于定义的数据类型,值太大*/ #define EOVERFLOW 75 /* Value too large for defined data type */ #define ENOTUNIQ 76 /*名称在网络上不是唯一的*/ #define ENOTUNIQ 76 /* Name not unique on network */ #define EBADFD 77 /*坏状态的文件描述符*/ #define EBADFD 77 /* File descriptor in bad state */ #define EREMCHG 78 /*远程地址更改*/ #define EREMCHG 78 /* Remote address changed */ #define ELIBACC 79 /*不能访问所需的共享库*/ #define ELIBACC 79 /* Can not access a needed shared library */ #define ELIBBAD 80 /*访问一个损坏的共享库*/ #define ELIBBAD 80 /* Accessing a corrupted shared library */ #define ELIBSCN 81 /* .lib section in a.out已损坏*/ #define ELIBSCN 81 /* .lib section in a.out corrupted */ #define ELIBMAX 82 /*试图链接过多的共享库*/ #define ELIBMAX 82 /* Attempting to link in too many shared libraries */ #define ELIBEXEC 83 /* 不能直接执行共享库*/ #define ELIBEXEC 83 /* Cannot exec a shared library directly */ #define EILSEQ 84 /*非法字节序列*/ #define EILSEQ 84 /* Illegal byte sequence */ #define ERESTART 85 /*中断的系统调用应该重启*/ #define ERESTART 85 /* Interrupted system call should be restarted */ #define ESTRPIPE 86 /*流管道错误*/ #define ESTRPIPE 86 /* Streams pipe error */ #define EUSERS 87 /*用户过多*/ #define EUSERS 87 /* Too many users */ #define ENOTSOCK 88 /*非套接字的套接字操作*/ #define ENOTSOCK 88 /* Socket operation on non-socket */ #define EDESTADDRREQ 89 /*需要的目的地址*/ #define EDESTADDRREQ 89 /* Destination address required */ #define EMSGSIZE 90 /*消息太长*/ #define EMSGSIZE 90 /* Message too long */ #define EPROTOTYPE 91 /*协议类型错误的套接字*/ #define EPROTOTYPE 91 /* Protocol wrong type for socket */ #define ENOPROTOOPT 92 /*协议不可用*/ #define ENOPROTOOPT 92 /* Protocol not available */ #define EPROTONOSUPPORT 93 /*不支持协议*/ #define EPROTONOSUPPORT 93 /* Protocol not supported */ #define ESOCKTNOSUPPORT 94 /* Socket类型不支持*/ #define ESOCKTNOSUPPORT 94 /* Socket type not supported */ #define EOPNOTSUPP 95 /*不支持传输端点上的操作*/ #define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ #define EPFNOSUPPORT 96 /*不支持协议族*/ #define EPFNOSUPPORT 96 /* Protocol family not supported */ #define EAFNOSUPPORT 97 /*协议不支持的地址族 #define EAFNOSUPPORT 97 /* Address family not supported by protocol */ #define EADDRINUSE 98 /*地址已被使用*/ #define EADDRINUSE 98 /* Address already in use */ #define EADDRNOTAVAIL 99 /*无法分配请求的地址*/ #define EADDRNOTAVAIL 99 /* Cannot assign requested address */ #define ENETDOWN 100 /*网络关闭*/ #define ENETDOWN 100 /* Network is down */ #define ENETUNREACH 101 /*网络不可达*/ #define ENETUNREACH 101 /* Network is unreachable */ #define ENETRESET 102 /*由于复位导致网络连接断开*/ #define ENETRESET 102 /* Network dropped connection because of reset */ #define ECONNABORTED 103 /*软件导致的连接中止*/ #define ECONNABORTED 103 /* Software caused connection abort */ #define ECONNRESET 104 /*连接复位*/ #define ECONNRESET 104 /* Connection reset by peer */ #define ENOBUFS 105 /*没有可用的缓冲区空间*/ #define ENOBUFS 105 /* No buffer space available */ #define EISCONN 106 /*传输端点已经连接*/ #define EISCONN 106 /* Transport endpoint is already connected */ #define ENOTCONN 107 /*传输端点未连接*/ #define ENOTCONN 107 /* Transport endpoint is not connected */ #define eshudown108 /*在传输端点关闭后不能发送*/ #define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ #define ETOOMANYREFS 109 /*引用太多:无法拼接*/ #define ETOOMANYREFS 109 /* Too many references: cannot splice */ #define ETIMEDOUT 110 /*连接超时*/ #define ETIMEDOUT 110 /* Connection timed out */ #define ECONNREFUSED 111 /*拒绝连接*/ #define ECONNREFUSED 111 /* Connection refused */ #define EHOSTDOWN 112 /*主机已关闭*/ #define EHOSTDOWN 112 /* Host is down */ #define EHOSTUNREACH 113 /*没有到主机的路由*/ #define EHOSTUNREACH 113 /* No route to host */ #define EALREADY 114 /*操作已经在进行中*/ #define EALREADY 114 /* Operation already in progress */ #define EINPROGRESS 115 /*操作正在进行中*/ #define EINPROGRESS 115 /* Operation now in progress */ #define ESTALE 116 /*过期的NFS文件句柄*/ #define ESTALE 116 /* Stale NFS file handle */ #define EUCLEAN 117 /*结构需要清洗*/ #define EUCLEAN 117 /* Structure needs cleaning */ #define ENOTNAM 118 /*不是XENIX命名类型文件*/ #define ENOTNAM 118 /* Not a XENIX named type file */ #define ENAVAIL 119 /*没有XENIX信号量可用*/ #define ENAVAIL 119 /* No XENIX semaphores available */ #define EISNAM 120 /*是一个命名类型文件*/ #define EISNAM 120 /* Is a named type file */ #define EREMOTEIO 121 /*远程I/O错误*/ #define EREMOTEIO 121 /* Remote I/O error */ #define EDQUOT / /*配额已超过*/ #define EDQUOT 122 /* Quota exceeded */ #define ENOMEDIUM 123 /*没有找到介质*/ #define ENOMEDIUM 123 /* No medium found */ #define EMEDIUMTYPE 124 /*错误的介质类型*/ #define EMEDIUMTYPE 124 /* Wrong medium type */ #define ECANCELED 125 /* 操作取消 */ #define ECANCELED 125 /* Operation Canceled */ #define ENOKEY 126 /* 所需密钥不可用 */ #define ENOKEY 126 /* Required key not available */ #define EKEYEXPIRED 127 /* 密钥已过期 */ #define EKEYEXPIRED 127 /* Key has expired */ #define EKEYREVOKED 128 /* 密钥已被撤销 */ #define EKEYREVOKED 128 /* Key has been revoked */ #define EKEYREJECTED 129 /* 服务拒绝了密钥 */ #define EKEYREJECTED 129 /* Key was rejected by service */ /* 对于健壮的互斥锁 */ /* for robust mutexes */ #define EOWNERDEAD 130 /* 主人死后 */ #define EOWNERDEAD 130 /* Owner died */ #define ENOTRECOVERABLE 131 /* 不可恢复状态 */ #define ENOTRECOVERABLE 131 /* State not recoverable */ #define ERFKILL 132 /* 由于射频杀伤,无法操作 */ #define ERFKILL 132 /* Operation not possible due to RF-kill */ #define EHWPOISON 133 /* 内存页有硬件错误 */ #define EHWPOISON 133 /* Memory page has hardware error */
~over~