C 错误处理
C 语言 - 错误处理
Section titled “C 语言 - 错误处理”与其他一些语言(例如 C++、Java)不同,C 语言没有内置的异常处理机制。相反,错误处理主要依赖于检查函数的返回值,有时还会检查一个全局错误编号变量 errno。
许多标准库函数,特别是那些处理系统资源(如文件 I/O)或内存分配的函数,在发生错误时会返回一个特殊值(通常是 NULL、-1 或文档中针对特定函数说明的其他值)来指示错误。当错误发生时,这些函数通常会设置全局整数变量 errno(定义在 <errno.h> 中)为一个特定的错误代码,以提供更多详细信息。
重要规则:务必首先检查函数的返回值,看看是否发生了错误。只有在返回值指示错误发生时,才应该检查 errno 来理解错误发生的原因。有些函数即使成功也可能返回一个值,该值可能被误认为是错误指示符(例如 -1),因此检查返回值是至关重要的。
使用 errno 时,包含 <errno.h> 是良好的编程习惯。请注意,在现代多线程环境中,errno 是线程本地的,这意味着每个线程都有自己的 errno。
使用 perror() 和 strerror()
Section titled “使用 perror() 和 strerror()”C 语言提供了两个标准函数来获取与 errno 值对应的可读性强的错误消息:
- 函数
perror(const char *s)(来自<stdio.h>)打印您提供的自定义消息 (s),后跟一个冒号、一个空格以及当前errno值的文本表示。输出会发送到标准错误流 (stderr)。 - 函数
strerror(int errnum)(来自<string.h>)返回一个指向字符串的指针,该字符串包含传递的错误代码errnum的文本表示。您通常会将errno的当前值传递给它。
示例:模拟文件打开错误:
#include <stdio.h>#include <errno.h>#include <string.h>#include <stdlib.h> // Required for EXIT_FAILURE
// Note: No need to declare 'extern int errno;' explicitly when including <errno.h>// 注意:包含 <errno.h> 后无需显式声明 'extern int errno;' 了
int main(void) { // Use standard main signature FILE *pf; int errnum;
// Attempt to open a non-existent file for reading // 尝试打开一个不存在的文件进行读取 pf = fopen("nonexistent_file.txt", "rb");
// ALWAYS check the return value first! // 总是先检查返回值! if (pf == NULL) { errnum = errno; // Store errno immediately, as other calls might change it // 立即存储 errno 的值,因为其他调用可能会改变它
fprintf(stderr, "Error opening file.\n"); // Informative message // 错误提示消息
// Method 1: Using errno directly (less user-friendly) // 方法 1:直接使用 errno(用户友好性较低) fprintf(stderr, "Value of errno: %d\n", errnum);
// Method 2: Using perror (recommended for simple messages) // 方法 2:使用 perror(推荐用于简单消息) perror("Error reported by perror");
// Method 3: Using strerror (useful for custom formatting) // 方法 3:使用 strerror(适用于自定义格式) fprintf(stderr, "Error opening file: %s\n", strerror(errnum));
return EXIT_FAILURE; // Indicate failure using standard macro // 使用标准宏指示失败 } else { printf("File opened successfully! Closing it.\n"); // 文件打开成功!正在关闭它。 fclose(pf); }
return EXIT_SUCCESS; // Indicate success using standard macro // 使用标准宏指示成功}编译并执行时,假设 nonexistent_file.txt 不存在,输出将类似于(具体的 errno 值可能因操作系统而略有不同):
Error opening file.Value of errno: 2Error reported by perror: No such file or directoryError opening file: No such file or directory处理除以零错误
Section titled “处理除以零错误”在 C 语言中,整数除以零会导致未定义行为。程序可能崩溃或产生意想不到的结果。浮点数除以零由 IEEE 754 标准定义(结果为无穷大或 NaN),但这通常仍被视为错误情况。
您必须在执行除法之前明确检查除数是否为零:
#include <stdio.h>#include <stdlib.h> // For exit() and EXIT_FAILURE// 用于 exit() 和 EXIT_FAILURE
int main(void) { int dividend = 20; int divisor = 0; int quotient;
// Check before dividing // 在除法前检查 if (divisor == 0) { fprintf(stderr, "Error: Division by zero is not allowed.\n"); // 错误:不允许除以零。 return EXIT_FAILURE; // Exit indicating failure // 退出并指示失败 }
// This part will only execute if divisor is non-zero // 只有在除数非零时才会执行这部分代码 quotient = dividend / divisor; printf("Value of quotient: %d\n", quotient); // Use printf for normal output // 使用 printf 输出正常结果
return EXIT_SUCCESS; // Exit indicating success // 退出并指示成功}编译并执行时,将产生以下输出:
Error: Division by zero is not allowed.使用 assert 进行调试检查
Section titled “使用 assert 进行调试检查”<assert.h> 头文件提供了 assert 宏。它用于在开发过程中检查应该始终为真的条件。如果 assert() 中的条件为假,程序会向 stderr 打印一条错误消息(包含文件名、行号和条件),然后终止。
assert 主要是一个调试工具。通过在包含 <assert.h> 之前定义 NDEBUG 宏(通常通过编译器标志如 -DNDEBUG 完成),可以在发布版本中禁用断言,从而移除任何性能开销。
#include <stdio.h>#include <assert.h>#include <stdlib.h>
// Disable assertions by uncommenting the next line or using -DNDEBUG// 通过取消注释下一行或使用 -DNDEBUG 来禁用断言// #define NDEBUG
int main(void) { int divisor = 0;
// This assertion will fail // 这个断言将失败 assert(divisor != 0);
// This line will not be reached if assertions are enabled // 如果断言被启用,这行代码将不会被执行 printf("Continuing execution (only if NDEBUG is defined).\n"); // 继续执行(仅当 NDEBUG 定义时)
return EXIT_SUCCESS;}在启用断言(默认设置)的情况下运行此代码,可能会产生如下输出:
assertion_example: main.c:12: main: Assertion `divisor != 0' failed.程序退出状态
Section titled “程序退出状态”当程序完成时,它会向操作系统返回一个退出状态。此状态指示程序是成功完成还是遇到了错误。<stdlib.h> 中定义了用于此目的的标准宏:
EXIT_SUCCESS:通常定义为0。表示成功执行。EXIT_FAILURE:通常定义为一个非零值(通常为1)。表示程序由于错误而终止。
在 main 函数的 return 语句或 exit() 调用中,使用这些宏比直接使用 0 或 -1 等“魔法数字”更能使您的程序更具可移植性和可读性。
使用标准退出码的示例:
#include <stdio.h>#include <stdlib.h> // Required for EXIT_SUCCESS, EXIT_FAILURE// EXIT_SUCCESS, EXIT_FAILURE 需要包含此头文件
int main(void) { int dividend = 20; int divisor = 5; int quotient;
if (divisor == 0) { fprintf(stderr, "Error: Division by zero!\n"); // 错误:除以零! return EXIT_FAILURE; // Use standard macro for failure // 使用标准宏指示失败 }
quotient = dividend / divisor; printf("Value of quotient: %d\n", quotient); // 商的值:%d
return EXIT_SUCCESS; // Use standard macro for success // 使用标准宏指示成功}编译并执行时(divisor = 5),将产生以下输出:
Value of quotient: 4