嵌入式软件十大编程低级错误C语言-下篇

6.    资源泄漏问题


6.1.    定义(说明)


资源泄漏指的是程序在动态分配内存、打开文件、建立网络连接等操作后,未能正确释放或关闭这些资源,导致占用的资源无法被系统回收和重用的情况。资源泄漏包括内存泄漏、文件泄漏、网络连接泄漏等。内存泄漏已经单独列出,本节重点讲述文件泄漏和网络连接泄漏。
1)    文件泄漏:在打开文件后,未使用`fclose`函数关闭文件,或者在文件操作完成后未正确关闭文件,导致文件描述符未能释放,造成文件泄漏。
2)    网络连接泄漏:在建立网络连接后,未使用相应的函数关闭网络连接,导致网络连接资源无法被释放,造成网络连接泄漏。
资源泄漏会导致系统资源的浪费和耗尽,严重影响程序的性能和稳定性。为了避免资源泄漏,程序员应该注意在使用完资源后及时释放或关闭资源,确保资源得到正确释放。同时,使用静态代码分析工具、内存检测工具等可以帮助发现潜在的资源泄漏问题,提高程序的质量和可靠性。


6.2.    产生该问题的原因


产生文件泄漏的原因主要包括以下几点:
1)    忘记关闭文件:在程序中打开文件进行读写操作后,如果忘记使用`fclose`函数关闭文件,就会导致文件描述符未被释放,造成文件泄漏。
2)    异常退出:如果程序在打开文件后发生异常退出,比如程序崩溃或者被强制终止,可能导致文件未能正确关闭,造成文件泄漏。
3)    文件操作顺序错误:在文件操作中,如果打开文件后没有正确的顺序进行读写操作并关闭文件,可能导致文件未能正确关闭,造成文件泄漏。
4)    文件描述符泄漏:在程序中频繁打开文件但未正确关闭,或者在循环中多次打开文件但只关闭最后一次打开的文件,可能导致文件描述符泄漏,造成文件泄漏。
5)    大量文件句柄未关闭:在处理大量文件时,如果程序中存在大量文件句柄未被关闭,可能导致系统资源耗尽,造成文件泄漏。
为避免文件泄漏,程序员应该养成良好的文件操作习惯,确保在打开文件后及时使用`fclose`函数关闭文件。另外,使用异常处理机制,确保在异常情况下能够正确关闭文件,避免文件泄漏。


产生网络连接泄漏的原因主要包括以下几点:


1)    忘记关闭网络连接:在程序中建立网络连接后,如果忘记使用相应的函数关闭网络连接,就会导致网络连接资源未被释放,造成网络连接泄漏。
2)    异常退出:如果程序在建立网络连接后发生异常退出,比如程序崩溃或者被强制终止,可能导致网络连接未能正确关闭,造成网络连接泄漏。
3)    网络连接操作顺序错误:在网络连接操作中,如果建立连接后没有正确的顺序进行数据传输或关闭连接,可能导致网络连接未能正确关闭,造成网络连接泄漏。
4)    大量连接未关闭:在程序中频繁建立网络连接但未正确关闭,或者在循环中多次建立连接但只关闭最后一次建立的连接,可能导致网络连接资源泄漏。
为避免网络连接泄漏,程序员应该养成良好的网络连接管理习惯,确保在建立网络连接后及时使用相应的函数关闭连接。使用异常处理机制,确保在异常情况下能够正确关闭网络连接,避免网络连接泄漏。


6.3.    典型案例分析


【问题描述】设备网络断开重连时,存在小内存泄露
【问题定位】mqtt库中,释放mqtt句柄资源时,没有使用正确的接口关闭,导致ssl资源泄露
 
 


【纠正措施】 针对第三方接口,应当优先使用第三方提供的资源释放接口,尽量不要自己造接口

6.4.    应遵循的原则


要避免文件泄漏,程序员应该遵循以下原则:
1)    及时关闭文件:在程序中打开文件进行读写操作后,一定要确保在不再需要文件时及时使用`fclose`函数关闭文件,释放文件描述符。
2)    异常处理:在文件操作中,要使用异常处理机制来确保在发生异常情况下能够正确关闭文件,避免文件泄漏。
3)    检查返回值:在文件操作中,应该始终检查文件操作函数的返回值,确保文件打开、读写等操作是否成功,避免因操作失败而导致文件泄漏。
4)    合理管理文件句柄:在处理大量文件时,要合理管理文件句柄,确保每个文件都能正确关闭,避免文件描述符泄漏。
遵循以上原则可以帮助程序员有效地避免文件泄漏问题,确保文件资源得到正确释放,提高程序的可靠性和稳定性。


要避免网络连接泄漏,程序员应该遵循以下原则:


1)    明确关闭连接:在程序中建立网络连接后,一定要确保在不再需要连接时及时关闭连接,释放连接资源。对于TCP连接,可以使用`close`或者对应的网络库提供的关闭连接函数来关闭连接。
2)    使用连接池:对于需要频繁建立和关闭连接的场景,可以考虑使用连接池管理连接。连接池可以维护一定数量的连接,在需要时从连接池中获取连接,在不需要时放回连接池,避免频繁建立和关闭连接带来的性能损耗和资源泄漏。
3)    异常处理:在建立和使用网络连接的过程中,要使用异常处理机制来处理异常情况,确保在发生异常时能够正确关闭连接,避免连接泄漏。
4)    检查返回值:在建立和关闭连接的过程中,应该始终检查网络库函数的返回值,确保连接操作是否成功,避免因操作失败而导致连接泄漏。
5)    合理管理连接句柄:在处理大量连接时,要合理管理连接句柄,确保每个连接都能正确关闭,避免连接资源泄漏。
6)    使用现代网络库:现代网络库如libcurl、Boost.Asio等提供了更安全、更方便的网络连接管理方式,可以自动管理连接资源,减少手动管理连接资源的出错可能性。
遵循以上原则可以帮助程序员有效地避免网络连接泄漏问题,确保连接资源得到正确释放,提高程序的可靠性和稳定性。


7.    使用未初始化的变量问题


7.1.    定义(说明)


未初始化的变量是指在声明或定义变量时没有显式地给变量赋初值。未初始化的变量在内存中的值是不确定的,可能是随机值,也可能是编译器给定的默认值。


7.2.    产生该问题的原因


产生未初始化变量的原因可能包括:
1)    程序员忘记给变量赋初值;
2)    变量的初值在后续的逻辑中才能确定;
3)    为了提高程序的性能,避免不必要的初始化操作。


7.3.    典型案例分析


【问题描述】【重启】无网关款V53设备,烧写并绑定后,首次拔插电源,重启后出现内存不足,APP进程被杀死问题。
【问题定位】未初始化变量,导致变量为随机数
 
【纠正措施】 所有的变量在声明的时候必须进行初始化

7.4.    应遵循的原则


在使用未初始化变量时,应该遵循以下原则:
1)    尽量避免使用未初始化变量:未初始化变量的值是不确定的,可能导致程序出现难以预测的行为,甚至引发程序崩溃。因此,应该尽量避免使用未初始化变量,确保所有变量都经过初始化后再使用。
2)    明确知道未初始化变量的值:如果确实需要使用未初始化变量,程序员应该明确了解未初始化变量的值可能是随机的,并在使用时对其进行必要的检查和处理,以确保程序的正确性。
3)    使用静态分析工具:静态分析工具可以帮助检测未初始化变量的使用情况,帮助程序员及时发现潜在的问题并进行修复。
4)    规范化变量的初始化:在声明或定义变量时,尽量在同一行给变量赋初值,避免在后续代码中忘记初始化变量。
遵循以上原则可以帮助程序员避免未初始化变量可能带来的问题,提高程序的稳定性和可靠性。


8.    忘记检查返回值的问题


8.1.    定义(说明)


忘记检查返回值是指在调用函数或操作返回值时,程序员没有对返回值进行检查或处理。这种情况可能导致程序无法正确处理函数调用的结果,从而引发程序错误或异常。


8.2.    产生该问题的原因


产生忘记检查返回值问题的原因可能包括:
1)    程序员忽视了函数返回值的重要性,认为函数调用一定会成功,从而忽略了对返回值的检查;
2)    程序员对函数返回值的含义或可能的返回结果不够了解,无法正确处理返回值;
3)    编码时的疏忽或粗心导致忘记检查返回值。


8.3.    典型案例分析


【问题描述】接口测试触发异常报警类型,管理机会重启
【问题定位】测试下发平台未定义的告警类型,程序上未对异常类型进行保护,导致程序异常
  修改前:
 
  修改后:
 
【纠正措施】指针增加判空处理
【举一反三】 
引用别人的接口返回值 必须考虑其值可能的情况 -1 NULL(0)等异常情况

8.4.    应遵循的原则


对于返回值检查,应该遵循以下原则:
1)    检查所有函数调用的返回值:在调用函数或操作时,应该始终检查返回值,确保程序能够正确处理函数调用的结果。如果返回值表示成功或失败状态,应该根据返回值来决定后续的操作。
2)    处理错误情况:当函数调用返回错误值时,程序员应该及时处理错误情况,可以通过返回错误码、打印错误信息、记录日志等方式来处理错误,避免错误被忽略导致程序异常。
3)    使用断言或异常处理机制:在关键函数调用处,可以使用断言或异常处理机制来确保返回值的有效性,及时发现并处理错误情况。
4)    编写清晰的文档:对于函数的返回值含义和可能的返回结果,应该编写清晰的文档说明,让程序员了解函数调用的返回值意义,帮助正确处理返回值。
遵循以上原则可以帮助程序员避免忘记检查返回值问题,提高程序的可靠性和稳定性。

9.    编码安全性及不恰当的错误处理


9.1.    定义(说明)


编码安全性问题是指程序员在编写代码时未考虑到潜在的安全漏洞或未正确处理输入数据,导致程序容易受到恶意攻击或数据泄露的问题。这种问题可能会导致程序受到缓冲区溢出、整数溢出、格式化字符串漏洞等安全漏洞的攻击。
不恰当的错误处理问题是指程序员在编写代码时未正确处理可能出现的错误情况,导致程序无法正确处理异常情况或错误,可能导致程序崩溃或数据丢失等问题。


9.2.    产生该问题的原因


产生编码安全性问题的原因可能包括:
1)    缓冲区溢出:未正确处理输入数据的长度或未对输入数据进行边界检查,导致缓冲区溢出漏洞。
2)    整数溢出:未正确处理整数运算的溢出情况,导致整数溢出漏洞。
3)    格式化字符串漏洞:在使用格式化字符串函数时,未正确控制格式化输出的参数,导致格式化字符串漏洞。
4)    不安全的函数:使用不安全的函数或库函数,如`gets()`、`strcpy()`等,容易引发安全问题。
5)    未验证输入:未对输入数据进行验证和过滤,可能导致恶意输入数据引发安全漏洞。

产生不恰当的错误处理问题的原因可能包括:
1)    忽略错误码:程序员在调用系统函数或库函数时,忽略了返回的错误码,未对返回值进行检查和处理。
2)    错误处理逻辑不完善:程序员在编写错误处理逻辑时,没有考虑到所有可能的异常情况,导致程序无法正确应对错误。
3)    错误处理方式不当:程序员在遇到错误时,采取了不恰当的处理方式,可能导致程序状态混乱或数据丢失。
4)    错误处理流程混乱:程序员在代码中错误处理流程混乱,导致难以追踪和调试错误情况。


9.3.    典型案例分析


【问题描述】【重连WiFi】路由器断上电500次,D2灯带无法连接上WiFi
【问题定位】申请内存未及时释放,造成内存泄露,一段时间后导致设备堆内存不足重启
 
【纠正措施】内存资源的申请和释放最好成对出现,并且对于所有的异常分支都必须考虑资源释放问题。

9.4.    应遵循的原则


为了编码安全性问题,在编码过程中应该遵循以下原则:
1)    输入验证:对所有输入数据进行验证和过滤,确保输入数据符合预期格式和范围。
2)    边界检查:对所有涉及缓冲区操作的代码,要确保正确处理输入数据的长度,避免发生缓冲区溢出。
3)    使用安全函数:尽量使用安全的函数或库函数,如`fgets()`替代`gets()`,`strncpy()`替代`strcpy()`等。
4)    整数溢出检查:在进行整数运算时,要注意检查是否会发生整数溢出,避免安全问题。
5)    安全编程规范:遵循安全编程规范,如避免硬编码密码、避免使用未经验证的输入数据等。
遵循以上原则可以帮助程序员提高C语言代码的安全性,减少程序受到恶意攻击的可能性,保护数据的安全性和完整性。

为了避免产生不恰当的错误处理问题,在编码过程中应该遵循以下原则:


1)    错误码检查:对所有可能返回错误码的系统函数或库函数,要对返回值进行检查,确保程序能够正确处理异常情况。
2)    错误处理逻辑完善:编写错误处理逻辑时,要考虑到所有可能的异常情况,制定完善的错误处理方案。
3)    合理处理错误:在遇到错误时,要采取合理的错误处理方式,如记录错误日志、恢复程序状态、提示用户等。
4)    统一错误处理流程:在代码中采用统一的错误处理流程,避免混乱和重复的错误处理代码。
遵循以上原则可以帮助程序员提高C语言代码的健壮性和可靠性,减少程序出错的可能性,确保程序能够正确处理各种异常情况。


10.    互斥锁使用不当造成的异常


10.1.    定义(说明)


在C语言中,互斥锁使用不当可能导致多线程并发访问共享资源时发生竞态条件,造成数据不一致或程序崩溃等问题。


10.2.    产生该问题的原因


产生互斥锁使用不当问题的原因可能包括:
1)    未正确加锁:在访问共享资源时,未正确使用互斥锁进行加锁操作,导致多个线程同时访问共享资源。
2)    加锁顺序错误:在多个地方对共享资源加锁时,加锁的顺序不一致,可能导致死锁或数据不一致问题。
3)    加锁范围不正确:在对共享资源进行操作时,加锁的范围不正确,可能导致部分操作未受保护而引发问题。
4)    未正确释放锁:在访问共享资源后,未正确释放互斥锁,导致其他线程无法访问共享资源。


10.3.    典型案例分析


【问题描述】【云模式】分机接路由器,主机云呼叫分机,分机振铃中路由器重启,分机点击挂断卡死
【问题定位】云对讲中断网点击接听按键,开启tutk音频通道,先获取互斥锁g_audio_back_channel_mutex,然后avServStartEx会阻塞30秒,造成lphone阻塞了30秒未重置线程标志位,软件看门狗重启。
修改前:
 
修改后:
 
【纠正措施】加锁时必须考虑加锁的范围,以及涉及的接口是否可能是阻塞的,做好预防处理

10.4.    应遵循的原则


在使用互斥锁时,应该遵循以下原则:
1)    加锁和解锁一一对应:在访问共享资源时,必须确保每次加锁都有对应的解锁操作,避免出现死锁或资源泄漏。
2)    加锁范围正确:在对共享资源进行操作时,加锁的范围应该尽可能小,避免影响程序性能并减少竞态条件的可能性。
3)    加锁顺序一致:在多个地方对共享资源加锁时,应该保持一致的加锁顺序,避免死锁的发生。
4)    避免递归锁:避免在同一个线程中多次对同一个互斥锁进行加锁操作,可能导致死锁。
遵循以上原则可以帮助程序员正确使用互斥锁,保护共享资源的访问安全,避免多线程并发访问时出现竞态条件导致的问题。


11.    编码逻辑错误


11.1.    定义(说明)


编码逻辑错误指的是程序员在编写代码时所引入的逻辑错误或逻辑漏洞,导致程序运行时出现错误或异常行为。这种问题可能会导致程序功能不符合预期、产生错误输出或导致程序崩溃等情况。


11.2.    产生该问题的原因


产生编码逻辑错误的原因可能包括:
1)    理解错误:程序员对问题的理解不准确或不完整,导致编写的代码与实际需求不符。
2)    逻辑漏洞:在编写代码时,程序员未考虑到所有可能的情况或未正确处理边界条件,导致程序逻辑出现漏洞。
3)    粗心疏忽:在编写代码时,由于粗心或疏忽导致逻辑错误的引入,比如错用变量、错误的条件判断等。
4)    复杂逻辑:复杂的逻辑结构或嵌套条件语句可能会增加出错的可能性,特别是在多个条件交织的情况下。


11.3.    典型案例分析


【问题描述】【系统】【APP应用阻塞】设备使用过程出现APP应用阻塞现象
【问题定位】发送接口未加锁,导致发送给网关的socket出现粘包
 
【纠正措施】编写函数时,应当考虑接口的可重入性和并发性,针对存在全局资源的变量,应该加锁保护资源。


11.4.    应遵循的原则


在遇到编码逻辑错误的问题时,应该遵循以下原则:
1)    仔细分析问题:在编写代码之前,应该仔细分析问题需求,确保对问题的理解准确,避免因为误解导致逻辑错误。
2)    边界条件考虑:在编写代码时,要考虑所有可能的边界条件和特殊情况,确保程序能够正确处理各种情况。
3)    单元测试:编写完代码后,进行单元测试来验证代码的逻辑正确性,尽早发现和修复逻辑错误。
4)    代码审查:进行代码审查时,让其他人检查代码的逻辑是否正确,发现潜在的逻辑错误。
5)    简化复杂逻辑:尽量避免编写复杂的嵌套条件语句,可以考虑重构代码,将复杂逻辑拆分为多个简单逻辑块,减少出错的可能性。
遵循以上原则可以帮助程序员避免编码逻辑错误,提高代码的质量和可靠性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值