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

[技术讨论] 【C语言实战经验6】什么是防御式编程?请看代码

为什么会有防御式编程呢?其实防御式编程的概念来源于防御式驾驶思维,因为你不知道也无法确定司机下一步要做什么,那怎么样才能保证司机在做出危险动作的时候自己不会受到伤害呢?这个时候就需要自己承担自我保护责任,就算是他人过错;所以,防御式编程的主要思想就是不能因外部的错误数据而破坏程序的正常运行。

另外,防御式编程其实也算一种软件开发的方法,通过预判和处理潜在错误来防止程序崩溃或者产生不可预知的行为,从而达到提高代码安全性和健壮性的目的。

那到底都有哪些防御式编程的代码操作呢?我们一起来看看吧!

1、函数入参验证

在定义和编写函数时,会经常接触到函数形参,有时候函数形参或者形参之间会有值范围和一些特殊性存在,有些实参值是不能作为形参输入的,一旦输入错误数据,那可能就会出现问题。

比如自定义了一个有符号数的除法运算函数,形参1为被除数dividend,形参2为除数divisor,形参3为商quotient(输出结果)。最简单的代码实现就是如下:

复制

  1. void divide(int dividend, int divisor, int *quotient)
  2. {
  3. *quotient = dividend / divisor;
  4. }

那如何增加防御式代码呢?

大家都知道,除数不能为0,要对除数执行非零判断,如下所示:

复制

  1. int divide(int dividend, int divisor, int *quotient)
  2. {
  3. // 检查除数是否为0
  4. if (0 == divisor)
  5. {
  6. return -1; // 错误代码
  7. }
  8. *quotient = dividend / divisor;
  9. return 0; // 成功
  10. }

2、指针安全检查

自定义函数时,函数入参类型用指针类型是非常常见的操作,但是相比其他基本数据类型而言,指针又是最容易出问题的,包括空指针和野指针在内往往都是导致程序崩溃的罪魁祸首。

因此,函数入参使用指针类型时,一定要先进行指针的安全性检查。

如下代码,先检查指针变量dest和src是否为NULL(空),如果为NULL,则直接返回,不执行拷贝操作。

复制

  1. void copy(char *dest, const char *src, size_t dest_size)
  2. {
  3. // 检查指针是否为NULL
  4. if (NULL == dest || NULL == src)
  5. {
  6. return;
  7. }
  8. // 确保目标缓冲区足够大
  9. size_t src_len = strlen(src);
  10. if (src_len >= dest_size)
  11. {
  12. src_len = dest_size - 1;
  13. }
  14. // 安全拷贝
  15. strncpy(dest, src, src_len);
  16. dest[src_len] = '\0'; // 确保字符串终止
  17. }

3、边界检查

有时候我们定义的函数的入参是有特殊的实际意义的,比如阈值范围、数组长度和协议命令码等,如果实际输入的数值不在合理范围内,即超出了边界,那就要执行异常返回处理。

比如一个根据ADC数据计算电量的函数,ADC的正常范围是100~2000,那我们就可以按如下方式增加防御式编程,以避免输出异常的电量数据:

复制

  1. #define ADC_VALUE_MIN (100)
  2. #define ADC_VALUE_MAX (2000)
  3. int cal_volatge(unsigned int adc_data)
  4. {
  5. //判断ADC数据是否在合理范围内
  6. if((adc_data < ADC_VALUE_MIN) || (adc_data > ADC_VALUE_MAX))
  7. {
  8. return -1;
  9. }
  10. //计算电压
  11. return 0;
  12. }

4、断言处理

断言(assertion)是一种在程序中的一阶逻辑(如一个结果为真或假的逻辑判断式),目的是为了表示与验证软件开发者预期的结果。

断言处理在STM32的固件库里其实是非常常见的,会对一些入参进行检查:

当程序执行到断言的位置时,对应的断言应该为真。若断言不为真时,程序会中止执行,并给出错误信息。

C标准库里有一个assert.h头文件,该头文件常用于防御式编程,其中提供了一个assert宏,它只带一个参数,通过布尔表达式的方式描述一些非预期错误,包括空指针、输入输出参数值不在合理范围及数组越界等。

比如下面的代码通过断言判断指针是否为NULL:

复制

  1. void set_int_array_value(IntArray *arr, size_t index, int value)
  2. {
  3. //使用断言检查内部不变式
  4. assert(arr != NULL);
  5. // 运行时检查
  6. if (index >= arr->size)
  7. {
  8. return;
  9. }
  10. arr->data[index] = value;
  11. }

以上通过几个实际的常用的代码demo介绍了防御式编程的基本内容,其实防御式编程还涉及其他内容,简单总结如下:

验证所有输入:包括用户输入、文件内容和函数参数等;

检查返回值:特别是内存分配、文件操作等可能失败的调用;

使用断言:验证程序内部不变式;

初始化变量:特别是指针和敏感数据;

边界检查:数组访问、循环条件等;

资源管理:确保分配的资源最终被释放,原则就是谁申请谁释放;

错误处理:提供有意义的错误信息并处理错误;

代码审查:代码走查,代码走读,多人检查代码中的潜在问题;

静态分析:使用工具检测潜在问题,包括语法错误等。

通过以上这些防御式编程的操作和实践,将会显著提高代码的安全性和健壮性,有望彻底告别软件死机问题。

。
---------------------
作者:dffzh
链接:https://bbs.21ic.com/forum.php?mod=viewthread&tid=3458784
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

http://www.cnnetsun.cn/news/191499.html

相关文章:

  • OpenMV中HOG特征提取全面讲解
  • 8个AI论文生成平台测评,降重与写作功能深度解析
  • 8个AI论文改写工具评测,降重与写作功能全面分析
  • Elasticsearch基本用法项目应用:分页与高亮处理
  • 基于proteus的4位数码管动态扫描实战案例
  • 全面讲解ESP32开发核心外设:GPIO控制基础教学
  • PaperzzAI PPT:别再熬夜做PPT了,让AI给你“一键生成高光时刻”——不是模板搬运工,是你的视觉导演+内容编剧
  • 图解说明Vitis使用教程:适合初学者的界面功能解析
  • 具身智能重构体验!CES Asia 2026:消费电子从“工具”变身“主动伙伴”
  • STM32-时钟树编程
  • Packet Tracer使用教程:OSPF基础配置图解说明
  • 批量部署USB转串口驱动的企业级Windows策略应用
  • 赋能成长型企业:SAP Business One与奥维奥的数字化共赢之道
  • 一文说清同步整流buck电路图及其工作原理
  • Packet Tracer下载步骤详解:适合初学者的系统学习
  • 2025年AI论文写作平台精选,集成LaTeX支持与智能格式检查
  • Hotkey Detective终极指南:3步解决Windows热键冲突难题
  • 【Mol Plant综述精读】植物中的染色质重塑:复合物组成、机制多样性及生物学功能
  • 基于GA-HIDMSPSO算法优化BP神经网络+NSGAII多目标优化算法工艺参数优化、工程设计优化(四目标优化案例)
  • 系统学习erase前必须知道的存储基础知识
  • 通俗解释定制ROM在2025机顶盒刷机中的作用机制
  • 【数据分析】基于逆向方法的新型神经网络的实现,以估计云杉音木薄板的材料特性附Matlab代码
  • 微信小程序二维码生成实战指南:3步实现个性化营销码
  • 终极指南:如何使用Keyboard Chatter Blocker解决机械键盘连击问题
  • Performance-Fish性能优化指南:让《环世界》告别卡顿的5大秘诀
  • GKD订阅管理难题:如何用简单方法解决复杂问题
  • Windows热键失灵怎么办?这款侦探工具帮你快速定位问题
  • RePKG神器:Wallpaper Engine壁纸资源完美提取指南
  • 微信小程序二维码生成终极指南:weapp-qrcode快速上手与实战技巧
  • Calibre-Douban插件:电子书元数据智能管理完整指南