当前位置: 首页 > 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.jsqmd.com/news/125545/

相关文章:

  • 基于SpringBoot的青年大学习记录管理系统的设计与实现
  • 【保姆级教程】2025最新 WordPress 建站全流程,从零到一实现网站上线(建议收藏)
  • 上位机是什么意思?图文并茂的新手教程
  • 事后诸葛亮会议
  • Paperzz 毕业论文 AI 功能:把 “论文熬大夜” 变成 “四步出框架” 的毕业捷径
  • 无法通过 scp 上传文件至路由器解决方法
  • 堆排序--自学笔记
  • 8个AI论文生成平台测评,降重与写作功能深度解析
  • Java毕设项目:基于springboot的旧物回收商城系统的设计与实现(源码+文档,讲解、调试运行,定制等)
  • 基于SpringBoot+Spark+vue的在线广告推荐系统
  • 有限动画状态机FSM
  • 【国产 OS 顶流实战】KylinOS V10 等保 2.0 三级合规 + MES 系统国产化迁移全案
  • GEO优化公司优质推荐:这六家企业技术扎实,长期效果经得起考验 - 品牌企业推荐师(官方)
  • 8款AI论文生成工具横向评测,降重与写作能力全面对比
  • Vue3_计算属性
  • KylinOS V10 等保 2.0 三级合规 + MES 系统国产化迁移全案
  • Java基于springboot+vue的社区残障人士服务平台系统
  • 2025客户管理系统选型指南:14 款国内外CRM厂商产品能力深度对比
  • Python中的数据结构(容器)之列表(list)
  • openmv与stm32硬件连接图解:一文说清引脚对接
  • PaperzzAI毕业论文写作:不是“代写”,是你的学术“外挂大脑”——让毕业季从“肝论文”变成“赢人生”
  • 树莓派5首次使用:操作指南与避坑建议
  • ESP32中RMT外设替代PWM:WS2812B时序控制新思路
  • 手把手教你完成四层板PCB绘制与电源分割操作
  • 2025 AI 视频生成大横评:Sora、可灵、Luma、Runway 谁才是真正的“电影级”导演?
  • Paperzz AI PPT:把 “做 PPT 的苦”,变成 “选模板的爽”
  • STM32工程中Keil生成Bin文件超详细版说明
  • I2C读写时序基础:一文说清起始与停止条件
  • 工业热成像数据增强不足 后来才知道加高斯噪声模拟设备老化
  • CC2530运行ZStack时的中断处理机制解析