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

每日一个C语言知识:C 错误处理

C 语言错误处理

C语言中的错误处理主要依赖于返回值、全局变量和标准库提供的错误处理机制。


1. 基本的错误处理机制

1.1 返回值检查

这是最常用的错误处理方式,函数通过返回值来指示成功或失败。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 示例:文件操作错误处理
void file_operation_example() {FILE *file = fopen("nonexistent.txt", "r");if (file == NULL) {printf("错误:无法打开文件\n");return;}// 文件操作...fclose(file);
}// 示例:内存分配错误处理
void memory_allocation_example() {int *array = (int*)malloc(1000000000 * sizeof(int)); // 可能失败if (array == NULL) {printf("错误:内存分配失败\n");return;}// 使用数组...free(array);
}// 示例:字符串操作错误处理
void string_operation_example() {char source[] = "Hello, World!";char destination[10];// 安全的字符串复制if (strncpy(destination, source, sizeof(destination) - 1) == NULL) {printf("错误:字符串复制失败\n");return;}destination[sizeof(destination) - 1] = '\0'; // 确保终止printf("复制结果: %s\n", destination);
}

2. 标准错误处理机制

2.1 errno 全局变量

C标准库提供了 errno 全局变量来记录错误代码。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>   // 包含errno的定义
#include <string.h>  // 包含strerror()int main() {FILE *file;// 尝试打开不存在的文件file = fopen("nonexistent_file.txt", "r");if (file == NULL) {printf("打开文件失败!\n");printf("错误代码: %d\n", errno);printf("错误描述: %s\n", strerror(errno));}// 尝试除以零errno = 0; // 重置errnoint result = 1 / 0;if (errno != 0) {printf("数学运算错误!\n");printf("错误代码: %d\n", errno);printf("错误描述: %s\n", strerror(errno));}return 0;
}

2.2 perror() 函数

perror() 自动将 errno 转换为可读的错误信息。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>int main() {FILE *file;// 使用perror输出错误信息file = fopen("/invalid/path/file.txt", "r");if (file == NULL) {perror("打开文件失败");// 输出: 打开文件失败: No such file or directory}// 内存分配错误int *ptr = malloc(1000000000000ULL);if (ptr == NULL) {perror("内存分配失败");// 输出: 内存分配失败: Cannot allocate memory} else {free(ptr);}return 0;
}

3. 常见的错误代码

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <math.h>void demonstrate_common_errors() {printf("常见错误代码及其含义:\n");printf("======================\n");// 模拟各种错误errno = EPERM;      printf("EPERM (%d): %s\n", EPERM, strerror(EPERM));errno = ENOENT;     printf("ENOENT (%d): %s\n", ENOENT, strerror(ENOENT));errno = EINTR;      printf("EINTR (%d): %s\n", EINTR, strerror(EINTR));errno = EIO;        printf("EIO (%d): %s\n", EIO, strerror(EIO));errno = EBADF;      printf("EBADF (%d): %s\n", EBADF, strerror(EBADF));errno = EAGAIN;     printf("EAGAIN (%d): %s\n", EAGAIN, strerror(EAGAIN));errno = ENOMEM;     printf("ENOMEM (%d): %s\n", ENOMEM, strerror(ENOMEM));errno = EACCES;     printf("EACCES (%d): %s\n", EACCES, strerror(EACCES));errno = EFAULT;     printf("EFAULT (%d): %s\n", EFAULT, strerror(EFAULT));errno = EINVAL;     printf("EINVAL (%d): %s\n", EINVAL, strerror(EINVAL));
}int main() {demonstrate_common_errors();return 0;
}

4. 自定义错误处理系统

4.1 定义错误码和错误处理函数

#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 自定义错误码
typedef enum {SUCCESS = 0,ERROR_NULL_POINTER,ERROR_INVALID_INPUT,ERROR_OUT_OF_MEMORY,ERROR_FILE_IO,ERROR_NETWORK,ERROR_CALCULATION
} ErrorCode;// 错误信息映射
const char* error_messages[] = {"操作成功","空指针错误","无效输入","内存不足","文件I/O错误","网络错误","计算错误"
};// 错误处理函数
void handle_error(ErrorCode code, const char* context) {if (code != SUCCESS) {fprintf(stderr, "错误 [%d]: %s - %s\n", code, error_messages[code], context);// 根据错误严重程度决定是否退出if (code == ERROR_OUT_OF_MEMORY || code == ERROR_NULL_POINTER) {fprintf(stderr, "严重错误,程序退出\n");exit(EXIT_FAILURE);}}
}// 示例函数:安全的除法
ErrorCode safe_divide(double a, double b, double *result) {if (result == NULL) {return ERROR_NULL_POINTER;}if (b == 0.0) {return ERROR_CALCULATION;}*result = a / b;return SUCCESS;
}// 示例函数:安全的字符串复制
ErrorCode safe_string_copy(char *dest, const char *src, size_t dest_size) {if (dest == NULL || src == NULL) {return ERROR_NULL_POINTER;}if (dest_size == 0) {return ERROR_INVALID_INPUT;}strncpy(dest, src, dest_size - 1);dest[dest_size - 1] = '\0'; // 确保字符串终止return SUCCESS;
}int main() {double result;ErrorCode err;// 测试安全除法err = safe_divide(10.0, 2.0, &result);handle_error(err, "除法运算");if (err == SUCCESS) {printf("10.0 / 2.0 = %.2f\n", result);}err = safe_divide(10.0, 0.0, &result);handle_error(err, "除以零");// 测试安全字符串复制char buffer[10];err = safe_string_copy(buffer, "Hello, World!", sizeof(buffer));handle_error(err, "字符串复制");if (err == SUCCESS) {printf("复制结果: %s\n", buffer);}return 0;
}

4.2 带错误上下文的结构体

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>// 错误信息结构体
typedef struct {int code;char message[256];char file[128];int line;time_t timestamp;
} ErrorInfo;// 全局错误信息
ErrorInfo last_error = {0};// 设置错误信息
void set_error(int code, const char* message, const char* file, int line) {last_error.code = code;strncpy(last_error.message, message, sizeof(last_error.message) - 1);strncpy(last_error.file, file, sizeof(last_error.file) - 1);last_error.line = line;last_error.timestamp = time(NULL);
}// 获取错误信息
void print_last_error() {if (last_error.code != 0) {char time_str[64];strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", localtime(&last_error.timestamp));fprintf(stderr, "[%s] 错误 %d: %s\n", time_str, last_error.code, last_error.message);fprintf(stderr, "位置: %s:%d\n", last_error.file, last_error.line);}
}// 清除错误信息
void clear_error() {last_error.code = 0;last_error.message[0] = '\0';last_error.file[0] = '\0';last_error.line = 0;
}// 带错误检查的内存分配
void* checked_malloc(size_t size, const char* file, int line) {void *ptr = malloc(size);if (ptr == NULL) {set_error(1, "内存分配失败", file, line);}return ptr;
}// 带错误检查的文件打开
FILE* checked_fopen(const char* filename, const char* mode, const char* file, int line) {FILE *f = fopen(filename, mode);if (f == NULL) {char message[256];snprintf(message, sizeof(message), "无法打开文件: %s", filename);set_error(2, message, file, line);}return f;
}// 宏简化错误检查调用
#define CHECKED_MALLOC(size) checked_malloc(size, __FILE__, __LINE__)
#define CHECKED_FOPEN(filename, mode) checked_fopen(filename, mode, __FILE__, __LINE__)int main() {clear_error(); // 清除之前的错误// 使用带错误检查的函数int *data = (int*)CHECKED_MALLOC(1000000000000ULL * sizeof(int));if (data == NULL) {print_last_error();} else {free(data);}FILE *file = CHECKED_FOPEN("nonexistent.txt", "r");if (file == NULL) {print_last_error();} else {fclose(file);}return 0;
}

5. 资源管理和错误处理

5.1 使用 goto 进行错误清理

#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef struct {char *name;int *scores;FILE *log_file;
} Student;int initialize_student(Student *student, const char *name, int score_count) {int status = -1;// 初始化指针为NULL,便于清理student->name = NULL;student->scores = NULL;student->log_file = NULL;// 分配姓名student->name = malloc(strlen(name) + 1);if (student->name == NULL) {perror("分配姓名内存失败");goto cleanup;}strcpy(student->name, name);// 分配分数数组student->scores = malloc(score_count * sizeof(int));if (student->scores == NULL) {perror("分配分数数组失败");goto cleanup;}// 打开日志文件student->log_file = fopen("student.log", "w");if (student->log_file == NULL) {perror("打开日志文件失败");goto cleanup;}// 初始化成功status = 0;printf("学生 %s 初始化成功\n", name);cleanup:if (status != 0) {// 清理已分配的资源free(student->name);free(student->scores);if (student->log_file != NULL) {fclose(student->log_file);}printf("学生初始化失败\n");}return status;
}void cleanup_student(Student *student) {free(student->name);free(student->scores);if (student->log_file != NULL) {fclose(student->log_file);}printf("学生资源已清理\n");
}int main() {Student student;if (initialize_student(&student, "张三", 5) == 0) {// 使用学生对象...printf("正在使用学生: %s\n", student.name);// 清理资源cleanup_student(&student);}return 0;
}

5.2 使用 setjmplongjmp 进行错误恢复

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <string.h>jmp_buf error_recovery;typedef enum {ERR_NONE,ERR_MEMORY,ERR_FILE,ERR_NETWORK
} ErrorType;// 模拟可能失败的函数
int risky_operation(int scenario) {switch (scenario) {case 0:return 0; // 成功case 1:printf("内存分配失败\n");longjmp(error_recovery, ERR_MEMORY);case 2:printf("文件操作失败\n");longjmp(error_recovery, ERR_FILE);case 3:printf("网络连接失败\n");longjmp(error_recovery, ERR_NETWORK);default:return 0;}
}void handle_error(ErrorType error) {switch (error) {case ERR_MEMORY:printf("处理内存错误: 尝试减少内存使用\n");break;case ERR_FILE:printf("处理文件错误: 检查文件权限和路径\n");break;case ERR_NETWORK:printf("处理网络错误: 检查网络连接\n");break;default:printf("未知错误\n");}
}int main() {ErrorType error;// 设置错误恢复点if ((error = (ErrorType)setjmp(error_recovery)) == ERR_NONE) {printf("开始执行风险操作...\n");// 模拟不同的场景for (int i = 0; i < 4; i++) {printf("\n场景 %d:\n", i);risky_operation(i);printf("场景 %d 执行成功\n", i);}printf("\n所有操作完成!\n");} else {printf("\n捕获到错误,进行错误恢复...\n");handle_error(error);printf("错误恢复完成,继续执行...\n");}return 0;
}

6. 信号处理

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
#include <unistd.h>jmp_buf segmentation_fault_recovery;// 段错误信号处理函数
void segmentation_fault_handler(int sig) {printf("\n捕获到段错误信号 (%d)\n", sig);printf("进行错误恢复...\n");// 跳转到恢复点longjmp(segmentation_fault_recovery, 1);
}// 浮点错误处理函数
void floating_point_handler(int sig) {printf("\n捕获到浮点错误信号 (%d)\n", sig);printf("处理浮点异常...\n");// 恢复默认处理并重新发送信号signal(sig, SIG_DFL);raise(sig);
}int main() {// 设置信号处理函数signal(SIGSEGV, segmentation_fault_handler); // 段错误signal(SIGFPE, floating_point_handler);      // 浮点异常printf("信号处理示例\n");printf("============\n");// 段错误恢复示例if (setjmp(segmentation_fault_recovery) == 0) {printf("1. 段错误恢复测试:\n");int *bad_pointer = NULL;// 这会导致段错误,但会被我们的处理函数捕获// *bad_pointer = 42; // 取消注释来测试printf("段错误测试通过\n");} else {printf("从段错误中恢复成功\n");}// 浮点错误示例printf("\n2. 浮点错误测试:\n");// 这会导致浮点异常// int result = 1 / 0; // 取消注释来测试printf("浮点错误测试通过\n");printf("\n程序正常结束\n");return 0;
}

7. 断言(Assertions)

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>// 使用断言进行调试
double calculate_discriminant(double a, double b, double c) {// 前置条件检查assert(!isnan(a) && !isnan(b) && !isnan(c)); // 确保不是NaNassert(isfinite(a) && isfinite(b) && isfinite(c)); // 确保是有限数double discriminant = b * b - 4 * a * c;// 后置条件检查assert(!isnan(discriminant)); // 结果不应为NaNassert(isfinite(discriminant)); // 结果应为有限数return discriminant;
}// 自定义断言宏
#define CUSTOM_ASSERT(condition, message) \do { \if (!(condition)) { \fprintf(stderr, "断言失败: %s\n", message); \fprintf(stderr, "文件: %s, 行号: %d\n", __FILE__, __LINE__); \abort(); \} \} while(0)void test_custom_assert() {int *ptr = malloc(sizeof(int));CUSTOM_ASSERT(ptr != NULL, "内存分配失败");CUSTOM_ASSERT(*ptr == 0, "新分配的内存应该被初始化为0"); // 这不一定成立!free(ptr);
}int main() {printf("断言示例\n");printf("========\n");// 标准断言测试printf("1. 标准断言测试:\n");double disc = calculate_discriminant(1.0, -3.0, 2.0);printf("判别式: %.2f\n", disc);// 这会触发断言失败// double bad_disc = calculate_discriminant(0.0, 0.0, 0.0); // 取消注释测试// 自定义断言测试printf("\n2. 自定义断言测试:\n");test_custom_assert();printf("\n所有测试通过!\n");return 0;
}

8. 完整的错误处理框架示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>// 错误级别
typedef enum {LOG_DEBUG,LOG_INFO,LOG_WARNING,LOG_ERROR,LOG_CRITICAL
} LogLevel;// 错误处理配置
typedef struct {LogLevel min_level;FILE *log_file;int exit_on_critical;
} ErrorConfig;// 全局错误处理配置
ErrorConfig error_config = {.min_level = LOG_WARNING,.log_file = NULL,.exit_on_critical = 1
};// 初始化错误处理系统
int error_system_init(const char *log_filename, LogLevel min_level) {if (log_filename != NULL) {error_config.log_file = fopen(log_filename, "a");if (error_config.log_file == NULL) {perror("无法打开日志文件");return -1;}} else {error_config.log_file = stderr;}error_config.min_level = min_level;return 0;
}// 清理错误处理系统
void error_system_cleanup() {if (error_config.log_file != NULL && error_config.log_file != stderr) {fclose(error_config.log_file);error_config.log_file = NULL;}
}// 记录错误
void log_message(LogLevel level, const char *file, int line, const char *format, ...) {if (level < error_config.min_level) {return;}// 获取当前时间time_t now = time(NULL);char time_str[64];strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", localtime(&now));// 级别名称const char *level_names[] = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"};// 格式化消息va_list args;va_start(args, format);fprintf(error_config.log_file, "[%s] %s: %s:%d: ", time_str, level_names[level], file, line);vfprintf(error_config.log_file, format, args);fprintf(error_config.log_file, "\n");fflush(error_config.log_file);va_end(args);// 关键错误处理if (level == LOG_CRITICAL && error_config.exit_on_critical) {fprintf(error_config.log_file, "关键错误,程序退出\n");error_system_cleanup();exit(EXIT_FAILURE);}
}// 日志宏
#define LOG_DEBUG(...)    log_message(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_INFO(...)     log_message(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_WARNING(...)  log_message(LOG_WARNING, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_ERROR(...)    log_message(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_CRITICAL(...) log_message(LOG_CRITICAL, __FILE__, __LINE__, __VA_ARGS__)// 示例应用程序
int main() {// 初始化错误处理系统if (error_system_init("app.log", LOG_DEBUG) != 0) {fprintf(stderr, "错误处理系统初始化失败\n");return EXIT_FAILURE;}LOG_INFO("应用程序启动");// 模拟各种级别的日志LOG_DEBUG("这是调试信息");LOG_INFO("应用程序初始化完成");LOG_WARNING("内存使用率较高");LOG_ERROR("文件读取失败");// 模拟关键错误(会退出程序)// LOG_CRITICAL("数据库连接失败"); // 取消注释测试LOG_INFO("应用程序正常退出");// 清理error_system_cleanup();return EXIT_SUCCESS;
}

总结

C语言错误处理的主要方法:

方法适用场景优点缺点
返回值检查函数调用、资源分配简单直观错误信息有限
errno全局变量系统调用、库函数标准化的错误代码线程不安全
perror()快速错误报告自动格式化错误信息灵活性差
自定义错误码应用程序特定错误可读性好,类型安全需要额外定义
setjmp/longjmp错误恢复、嵌套调用非局部跳转复杂,可能泄漏资源
信号处理系统级错误、异常处理异步事件平台依赖性强
断言调试、开发阶段自动检查假设发布版本中通常被禁用
http://www.dtcms.com/a/549073.html

相关文章:

  • 基础建设的网站有哪些内容网页制作基础教程费
  • 建站工具哪个好用深圳网站建设定制平台
  • 2048游戏笔记3 游戏开始与结束 cocos3.8.7
  • 【AI WorkFow】n8n 源码分析-核心结构体设计思考(六)
  • 网站诊断内容工程建设教育培训
  • Opencv(四):自适应二值化
  • GitHub 热榜项目 - 日榜(2025-10-30)
  • 网络:4.应用层自定义协议与序列化
  • Word崩溃打不开?实测三款Word文档修复工具!
  • 哪个网站做签约插画师好网站域名过期后续费多长时间生效
  • 《R for Data Science (2e)》免费中文翻译 (第11章) --- Communication(2)
  • 扩展阅读:CSV格式的目标检测(Object Detection)标注文件示例
  • 行政单位门户网站建设规定久久建筑网20g三维图集下载
  • Kotlin Multiplatform Mobile(KMM):实现 iOS 与 Android 共享业务逻辑
  • 利用Selenium和PhantomJS提升网页内容抓取与分析的效率
  • QML学习笔记(四十七)QML与C++交互:上下文对象
  • 农业物联网实践:基于 ESP8266 与土壤传感器的智能灌溉系统开发与部署
  • 【Windows 10 企业版 LSTC】下安装【英特尔® 显卡控制中心】
  • Linux常用操作命令详解
  • 十堰专业网站建设公司网站建设预算
  • 深圳网站设计+建设首选网站开发iis怎么配置
  • Angular【起步】
  • Unity ComputeShader入门指南
  • 铜鼻子冷压端子视觉检测机 尺寸外观瑕疵自动化检测设备
  • 强化学习(RL)简介及其在大语言模型中的应用
  • 沈阳自主建站模板网站代理维护
  • 东莞做展示网站的公司济南网络科技公司排名
  • 云栖实录 | 阿里云发布Elasticsearch Serverless 2.0,重塑AI搜索时代基础设施
  • 解决 InfiniteScroll 滚动 BUG
  • Python实现随机选播视频的示例代码