C 语言没有 C++、Java 等语言中的异常机制;库函数与系统调用通常通过返回值表示成败,并在失败时设置全局变量 errno。结合条件判断与 perror、strerror 等函数,可编写可维护的错误处理逻辑。文件相关 API 的用法见文件处理一节。
errno
errno 定义于 errno.h,许多标准库函数在失败时写入相应错误码。常见取值示例:
| errno | 含义 |
|---|---|
1 | Operation not permitted(操作不允许) |
2 | No such file or directory(文件不存在) |
5 | I/O error |
12 | Out of memory(内存不足) |
13 | Permission denied(权限不足) |
打开不存在文件时的基本检查:
#include <stdio.h>
#include <errno.h>
int main(void) {
FILE *fp = fopen("missing.txt", "r");
if (fp == NULL) {
printf("Error Code: %d\n", errno);
perror("Error");
}
return 0;
}输出:
Error Code: 2
Error: No such file or directory常用错误处理手段
1. 检查返回值
对返回指针的函数判断是否为 NULL;对返回整型的函数判断是否表示错误(如 -1):
FILE* fp;
fp = fopen("test.txt", "r");
if (fp == NULL) {
printf("File opening error\n");
} else {
printf("File open successfully\n");
}2. perror()
perror 将自定义前缀与 errno 对应的描述输出到标准错误流:
#include <errno.h>
#include <stdio.h>
int main(void) {
FILE* fp = fopen("test.txt", "r");
printf("Value of errno: %d\n", errno);
perror("Message from perror");
return 0;
}3. strerror()
strerror 返回指向错误描述字符串的指针,便于自行格式化输出:
#include <errno.h>
#include <stdio.h>
#include <string.h>
int main(void) {
FILE* fp = fopen("test.txt", "r");
printf("Value of errno: %d\n", errno);
printf("The error message is : %s\n", strerror(errno));
return 0;
}4. ferror()
在写操作后检查流是否出错:
#include <stdio.h>
int main(void) {
FILE *fptr = fopen("test.txt", "w");
if (fptr == NULL) return 1;
fprintf(fptr, "Hello, online-compiler!");
if (ferror(fptr) == 0)
printf("Data written successfully.\n");
fclose(fptr);
return 0;
}5. feof()
读文件时判断是否已到达文件尾:
#include <stdio.h>
int main(void) {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL)
return 0;
do {
char c = fgetc(fp);
if (feof(fp))
break;
printf("%c", c);
} while (1);
fclose(fp);
return 0;
}6. clearerr()
到达文件尾或发生错误后,流上可能保留 EOF 或错误标志;clearerr 可清除这些标志以便继续操作:
#include <stdio.h>
int main(void) {
FILE *fptr = fopen("test.txt", "w+");
fprintf(fptr, "Hello, online-compiler!");
while (fgetc(fptr) != EOF);
if (feof(fptr)) {
printf("EOF encounter\n");
}
clearerr(fptr);
if (!feof(fptr)) {
printf("Reset the EOF successfully\n");
}
fclose(fptr);
return 0;
}进程退出状态:exit()
exit 向环境返回状态码;EXIT_SUCCESS 与 EXIT_FAILURE 为可移植的宏:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
FILE* fp = fopen("test.txt", "rb");
if (fp == NULL) {
printf("Value of errno: %d\n", errno);
printf("Error opening the file: %s\n", strerror(errno));
perror("Error printed by perror");
exit(EXIT_FAILURE);
printf("I will not be printed\n");
} else {
fclose(fp);
exit(EXIT_SUCCESS);
}
return 0;
}调用 exit 后,同一函数内后续语句不会执行。
应用层前置检查
除零等逻辑错误可在调用库函数之前用条件判断拦截:
#include <stdio.h>
int main(void) {
int num1 = 10, num2 = 0;
if (num2 == 0)
printf("Error: Division by zero is not allowed\n");
else
printf("Result: %d", num1 / num2);
return 0;
}小结
健壮程序应检查关键函数返回值,在失败时读取 errno 或流错误标志,并选择记录日志、清理资源或终止进程。多步资源获取失败时,可结合goto 统一清理避免重复代码。
本节归纳了 errno 与常见 I/O 错误检测函数。下一节说明变参函数与 stdarg.h 的用法。