电子产业一站式赋能平台

PCB联盟网

搜索
查看: 27|回复: 0
收起左侧

嵌入式开发中C++内存泄漏的场景与解决办法

[复制链接]

963

主题

963

帖子

8028

积分

高级会员

Rank: 5Rank: 5

积分
8028
发表于 昨天 08:04 | 显示全部楼层 |阅读模式

vlr0xxq4nbg64014261741.gif

vlr0xxq4nbg64014261741.gif
& o; v# a4 E( `4 ^- P
点击上方蓝色字体,关注我们
  l" D2 Q1 }: ~. h
  O2 q; u( m$ ^% h1 N与桌面应用程序不同,嵌入式系统通常具有严格的内存限制,即使是小规模的内存泄漏也可能迅速导致系统崩溃或功能异常。
2 L2 O+ s2 N- _7 U3 I
  K, t4 u* L) y$ ^: G3 [0 C! i. ]内存泄漏是指程序在申请内存后,无法释放已经不再使用的内存空间,通常发生在程序员创建了一个新的内存块,但忘记在使用完之后释放它。
6 U% v, t! K) C# @  B. k3 M8 q& @; e, r: S5 ~" w/ V
在嵌入式C++开发中,内存泄漏的常见原因包括:
6 |# k: U# h$ K. t
  • 忘记释放动态分配的内存:最常见的内存泄漏原因,程序员分配了内存但忘记在不再需要时释放它。
  • 异常处理不当:在函数执行过程中发生异常,导致提前退出,而未释放之前分配的内存。
  • 循环引用:对象之间相互引用,导致它们的引用计数永远不为零,无法被正确释放。
  • 递归调用过深或栈上分配大量数据:这可能导致堆栈崩溃,表现为内存泄漏。
  • 使用不规范的库接口:某些旧库或API需要显式内存管理,使用不当可能导致内存泄漏。
    $ q2 R/ F' K: F. K场景一:忘记释放动态分配的内存
    : M- M% i! E% [! b) x7 V/ M( [这是最常见的内存泄漏原因。当使用new关键字分配内存后,如果没有调用delete操作符释放内存,就会导致内存泄漏。5 Y0 G+ E( X: Q  U& Z
    # F+ B, O. @( r7 @" _8 f8 ]/ A
  • void someFunction() {    int* ptr = new int(10); // 分配内存    // 没有 delete,导致内存泄漏}在这个例子中,someFunction函数分配了一个整数指针ptr,但在函数结束时没有释放这个内存。) Y) D1 u, r2 g- }% C4 E* m

    ) C* O2 [" g- c当函数返回时,ptr将被销毁,但分配的内存仍然存在,无法被访问,从而导致内存泄漏。  U. H7 c! J- t* N% N0 N) n5 |% n
    ( x, }6 S0 E0 X! m9 a
    确保在不再需要内存时调用delete释放它。
    4 s/ R4 ~1 y3 e- g* L  W
    / }3 w. U% G$ P! ^- z9 M
  • void someFunction() {    int* ptr = new int(10); // 分配内存    delete ptr; // 释放内存}或者,使用智能指针自动管理内存:
    7 F% L: b+ D2 R! _# Y6 H) c' A/ Y* @
      R% T' @3 V, L8 L
  • void someFunction() {    std::unique_ptrptr(new int(10)); // 使用智能指针    // 智能指针会自动释放内存}场景二:异常情况下的内存泄漏
    1 C' Q; F( D+ R/ l+ D当函数执行过程中发生异常,可能会导致提前退出,而未释放之前分配的内存,从而造成内存泄漏。
    . t5 @) c5 d' C8 d+ f+ S& }( P2 G2 ?) a5 |& h
  • void someFunction() {    int* ptr = new int(10); // 分配内存    // 可能在此处引发异常    someFunctionThatThrows(); }如果someFunctionThatThrows()函数抛出异常,控制流会直接跳到catch块或函数外,而不会执行后续的代码。
    . M  {  T; v) e. \6 A
    + w# ?  w6 x4 j3 X3 u) e- s8 Q; v% c: I这意味着ptr指向的内存将永远不会被释放,导致内存泄漏。
    - S: T# E! T% v- {. W. t1 M" b0 g. H, E1 j2 W( K3 X
    使用try-catch块捕获异常,并确保在异常情况下释放已分配的内存。
    8 {. n% o) F! k, x! L1 c6 G# Y+ ]
  • void someFunction() {    int* ptr = nullptr;    try {        ptr = new int(10); // 分配内存        someFunctionThatThrows(); // 可能抛出异常的函数    } catch(...) {        delete ptr; // 释放内存        throw; // 重新抛出异常    }}或者,使用智能指针自动管理内存:6 J* E7 D0 G+ D3 c( _& {
    ) x$ K& H& L9 S( o4 A! c8 E
  • void someFunction() {    std::unique_ptrptr;    try {        ptr = std::unique_ptr(new int(10)); // 分配内存        someFunctionThatThrows(); // 可能抛出异常的函数    } catch(...) {        // 智能指针会自动释放内存,即使抛出异常        throw; // 重新抛出异常    }}
    : E1 R/ q$ A9 i" T( y场景三:循环引用导致的内存泄漏" l* N6 a, {+ }5 ^0 D+ r6 V+ p
    在使用共享指针(shared_ptr)时,对象之间的循环引用可能导致内存泄漏,因为每个共享指针都引用对方,导致引用计数永远不为零。
    , j+ ~4 B6 g% h7 h/ _  D$ \" L, u
    ; `1 F  ]# m# D, h
  • class Node {public:    std::shared_ptrnext;    std::shared_ptrprev;};int main() {    std::shared_ptrnode1(new Node());    std::shared_ptrnode2(new Node());    node1->next = node2;    node2->prev = node1;    // 此时node1和node2互相引用,无法被自动释放    return 0;}在这个例子中,node1和node2互相引用,导致它们的引用计数永远不为零,无法被自动释放,从而导致内存泄漏。# Y! f( O1 T0 R0 B9 V; s& F) R

    $ N- ]5 ^' m* R使用弱指针(weak_ptr)打破循环引用:/ q- q% ~0 R& y
    7 G# s1 z5 H3 Q# s/ H) o/ Z! f
  • class Node {public:    std::shared_ptrnext;    std::weak_ptrprev;};int main() {    std::shared_ptrnode1(new Node());    std::shared_ptrnode2(new Node());    node1->next = node2;    node2->prev = node1;    // 当node1被销毁后,node2的prev将不再有效    return 0;}
    - H) }) @' i( Z9 V" G" J9 f场景四:递归调用过深导致的堆栈崩溃
    % _4 `4 t/ C  @/ O( g4 u在C/C++编程中,堆栈崩溃是一种常见的错误,它通常是由于递归调用过深、内存溢出或者栈上分配的大量数据导致栈空间耗尽而引发的。
    1 v) {  c4 X5 H* o' B; Y) H  C
    0 g* `) `% _8 T/ l
  • void recursiveFunction(int depth) {    int array[1000]; // 在栈上分配大量数据    if (depth 1000) {        recursiveFunction(depth + 1);    }}int main() {    recursiveFunction(0); // 可能导致栈溢出    return 0;}在这个例子中,recursiveFunction函数递归调用自身1000次,并且每次在栈上分配一个大小为1000的整数数组。这可能导致栈溢出,引发堆栈崩溃。
      P" z2 {( y4 b8 T' b5 [+ F" f  \
  • 减少递归深度:将递归转换为迭代,或者减少递归深度。
  • 增加栈大小:在编译或运行时增加程序的栈大小。
  • 使用内存池:将大数组的分配从栈上转移到堆上。[/ol]; B1 h3 j4 Z$ ?* c+ Q+ H
    场景五:使用不规范的库接口# q( e; X. \* B" k
    某些旧库或API可能需要显式内存管理,使用不当可能导致内存泄漏。" }- I' u; ~1 ?) g1 T5 ]

    6 s* P6 e- ?( f7 y
  • void someFunction() {    char* buffer = someOldAPIFunction(); // 分配内存    // 使用缓冲区    // 没有释放内存}在这个例子中,someOldAPIFunction()函数可能在堆上分配了一个字符缓冲区,并返回指针。
    ( S" s. J9 ]; P8 C! C
    3 C, `5 O* J$ \- I$ w& J如果调用者没有显式释放这个内存,就会导致内存泄漏。- P& L# K( O5 A/ Y% F

    9 |: d! s4 `6 `3 y确保在不再需要内存时调用适当的释放函数:5 B# x# x9 C  I3 X
    + o3 P7 d$ v, _5 L* i+ K
  • void someFunction() {    char* buffer = someOldAPIFunction(); // 分配内存    // 使用缓冲区    free(buffer); // 释放内存}* b: h- z4 o; h3 U+ q. S1 r& ?

    rm22s4ajyzj64014261841.jpg

    rm22s4ajyzj64014261841.jpg
    1 [/ }9 S* f0 H

    ccvzbg1coya64014261941.gif

    ccvzbg1coya64014261941.gif

    : n( P& I# N# G) r点击阅读原文,更精彩~
  • 回复

    使用道具 举报

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则


    联系客服 关注微信 下载APP 返回顶部 返回列表