电子产业一站式赋能平台

PCB联盟网

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

嵌入式软件编程,使用动态内存分配有哪些坑,怎样预防?

[复制链接]

568

主题

568

帖子

4219

积分

四级会员

Rank: 4

积分
4219
发表于 2025-3-26 12:03:00 | 显示全部楼层 |阅读模式
我是老温,一名热爱学习的嵌入式工程师9 @) e# H3 L' C+ R; C, f' u2 s
关注我,一起变得更加优秀!一.  常见错误与预防 1.   分配后忘记释放内存void func(void)
. S& P. G* m4 P, K$ }& C0 C: q* j5 }{
3 ~& {' p* i* X  m9 a; \    p = malloc(len);
: |; j8 o- b- b9 O    do_something(p);  h( E: Y* q0 Q# W) P+ D
    return;  /*错误!退出程序时没有释放内存*/
6 }$ G8 T7 E' l1 g}
  p8 F# m* r+ a+ Z$ [* ~3 j3 z预防:  编写代码时malloc()和free()保证成对出现,避免忘记资源回收。; H  L+ [8 Z5 B: x- _$ r+ j* X
int func(void)- ^& S$ Q5 X. G, L( S  x2 M
{
5 \% M! x! Z' t$ _# @% |0 n: H    p = malloc(len);
) x1 j8 r& V* t2 S1 M5 l+ U1 F    if (condition)
& Y, T* q1 M5 p- U        return -1;  /*错误!退出程序时没有释放内存*/3 w1 a4 Z) V' V  N
    free(p);' a! D: x& u* u; t% O1 M: ?
    return 0;
  e' y* `0 {/ u6 V/ A6 z}
+ ?% }. u9 l$ K+ [0 n预防: 一旦使用动态内存分配,请仔细检查程序的退出分支是否已经释放该动态内存。
7 V  E9 @" F+ p6 P) x2.   释放内存调用错误指针void func(void)! G" w7 {( F( u5 j- _2 b8 E" Z
{
4 U( j# ^1 v2 |: H    p = malloc(len);- V6 s  y. ?5 `* K2 J0 l
    val = *p++;  /*错误!动态内存句柄不可移动*/
0 ]" A3 }7 ~1 @/ U% L    free(p);
. e" a# s4 Q) ]0 s, M. I( x}
( @% ^  r8 B: i$ Y预防: 千万不要修改动态内存句柄!可以另外赋值给其他指针变量,再对该动态内存进行访问操作。
/ m* |8 v7 j( [1 `7 R% h3.   分配内存不够导致溢出void func(void)9 W/ G& D1 N" w+ G$ e
{  T) i- ]# n8 g$ _. g0 F
    len = strlen(str);  r# j  U3 U" a" x# [3 t
    p = malloc(len);
( W1 H8 \4 a: S0 V0 a6 [1 Y9 [    strcpy(p, str);  /*错误!str的’\0’写到动态内存外*/3 O2 B1 j' o' a9 r, N
}0 i" [" E9 O( \8 @
预防:  分配内存前仔细思考长度是否足够,千万注意字符串拷贝占用内存比字符串长度大1。
$ J# f! L' E# G; }二.  自动查错机制 尽管在开发过程中坚守原则和谨慎编程甚至严格测试,然而内存泄露的错误还是难以杜绝,如何让系统自动查出内存泄露的错误呢?6 K2 T' ^! u# ?5 G. m: K
一种比较好的方法是建立日志块,即每次分配内存时记录该内存块的指针和大小,释放时再去除该日志块,如果有内存泄露就会有对应的日志块记录这些内存没有释放,这样就可以提醒程序员进行查错。0 Y- k5 R9 {/ F. r" A1 T
有了上述日志块操作函数,再来实现动态内存分配与释放函数就很容易了。只有当处于DEBUG版本和打开内存调试DMEM_DBG时才进行日志登录,否则MallocExt()和FreeExt()函数与malloc()和free()是等价的,这样保证了系统处于发布版本时的性能。, L/ |: ^; V2 I& x
(代码已经过严格测试,但这不是盈利的商业代码,即没有版权。但如果因代码错误带来的任何损失作者具有免责权利)5 y7 o$ d" e. }: c1 Q- f
代码部分:首先定义日志块结构体:
/ f6 Z  }: v2 I$ u/* Log of dynamic memory usage */8 J) d; F; t' ^
typedef struct _dmem_log
) }! R4 z3 I) E{) P( ?( W$ m2 q5 X( I
    struct _dmem_log *p_stNext; /* Point to next log */
+ Q7 _" U; A0 x* s0 L    const void *p_vDMem; /* Point to allocated memory by this pointer */
( i0 p8 p: Z/ R' l9 I    INT32S iSize; /* Size of the allocated memory */8 Z2 [. X& E0 i( _( }9 W7 z
} DMEM_LOG;
' m' r' [7 p2 V9 C& `, H然后为该结构体开辟内存:
' t2 x' y& a5 w# o! ]static DMEM_LOG *s_pstFreeLog; /* Point to free log pool by this pointer */
3 U$ ^; m5 F: g4 j# o* @static INT8U s_byNumUsedLog;' @& O5 g. ~' t3 s0 ]  @0 L
static DMEM_LOG *s_pstHeadLog; /* Point to used log chain by this pointer */( x. }; p6 t# L
/* Pool of dynamic memory log */
  F, W3 g6 r5 `- y$ V1 n" f#define NUM_DMEM_LOG 20# _0 v9 |) j+ d, x. z! z2 o
static DMEM_LOG s_astDMemLog[NUM_DMEM_LOG];4 ]! H3 P& m, Y/ C
下面是内存日志块的操作函数:初始化、插入日志和移除日志:
+ {( D: z# {  D% a/**********************************************************                                                             *                    Initialize DMem Log- d, `7 J. f: ?$ \; d+ l
* Description : Initialize log of dynamic memory$ d7 i; {3 ]6 R
* Arguments  : void
1 `) l8 |# Y3 |' ]1 |* Returns      : void
" C: T6 Q2 ^* h! O( h* Notes        :
& k. F' j3 |8 \2 N**********************************************************/1 u* g/ s4 l# l6 C! U+ Y
static void InitDMemLog(void)
1 [5 Y9 w# }  y9 o2 r4 y{
+ P, R0 h; }4 ~* O    INT16S    nCnt;
2 y' C* H% j- ^    /* Initialize pool of log */
, K& d) {: Y: a    for (nCnt = 0; nCnt % k  ?" m/ H! R+ w' o% b' M8 n
    {
- n# f$ H# G7 x7 l        /* Point to next one */$ Y& ^* p5 h6 }2 c
        s_astDMemLog[nCnt].p_stNext = &s_astDMemLog[nCnt + 1];- w: H# P* U. p4 J! [" }
    }
# |9 F+ v( S* w8 p% \    s_astDMemLog[NUM_DMEM_LOG - 1].p_stNext = NULL;2 p/ w) S, x& I' ?  P/ O5 Q) J
    s_pstFreeLog = &s_astDMemLog[0]; /* Point to the 1th log */2 s! a# E0 r' }& @4 w% t* L; ~( \& w
    return;
) W8 ]5 ~# t* F( n3 P2 X}/ h5 ?+ [; v! A: w
/**********************************************************                                                             *                       Log DMem) n: W9 }2 U4 S" h+ ~9 t
* Description : Join an allocated memory into log pool
) f; |. w, U1 ?) z- c% E7 B* Arguments  : const void *p_vAddr    point to address of this allocated memory by this pointer+ U4 v3 \2 r; D* M6 ]& J# U
*             INT32S iSize    size of this allocated memory
) p; |. ^" W) G6 I5 x! x! m2 y* Returns      : void# s  V: o6 i! d
* Notes        :4 K: ~4 E5 @+ W$ v$ q* o
**********************************************************/
- U7 T/ |( ~7 j  _( y! Mstatic void LogDMem(const void *p_vAddr, INT32S iSize)" b8 y8 |" q. V! J6 P, T& J
{
5 b1 T8 w( X8 \- Q    ASSERT(p_vAddr && iSize > 0);
4 s. E( g6 N# V/ y2 X: g    DMEM_LOG *p_stLog;
. u' p% {; {# X6 V$ o; |! x; Z" ~    #if OS_CRITICAL_METHOD == 33 ?) J: N- v* N- n
    OS_CPU_SR  cpu_sr;
$ ?3 q2 ^0 Z, r8 [. C9 P% d    #endif
( c" S4 C: d. m. y4 Z    - ?9 D. T7 _6 Y: g+ X  @: r
    /* Get a log from free pool *// ^0 d! x: d' _& o) }" }
    OS_ENTER_CRITICAL(); /* Avoid race condition on s_pstFreeLog */
$ D8 `5 W/ k! V5 Q: B4 g4 ?    if (!s_pstFreeLog)% k+ @: Y5 z7 Y1 p( V/ Z
    {6 Z2 k, ^0 @# s, w, Q# w
        OS_EXIT_CRITICAL();0 R  U# _* q) P, B
        PRINTF("Allocate DMemLog failed.\r) Q0 d/ a, G: v( j& R3 \) ]8 \
");      
% Y3 a& T3 m: J( r7 Y        return;& ]. q2 \, H% g% {; K* j7 B
    }
, \1 h' ~+ x1 d# q; @    p_stLog = s_pstFreeLog;
2 z+ V" R- E$ ^6 {6 {1 ]  d/ t  i! g    s_pstFreeLog = s_pstFreeLog->p_stNext;
3 s# T8 y. s& J" m6 t) w    OS_EXIT_CRITICAL();. x6 r  d  S9 x% q1 j
   
* _# l" e! @& a3 i8 [; J    /* Don't need to protect this log that is free one currently */# j( a* Y( p) v7 r6 _1 G
    p_stLog->p_vDMem = p_vAddr;  f# [# I4 D/ Z
    p_stLog->iSize = iSize;
. W+ B) [5 [9 h9 C6 `* z& z    /* Put this log into used chain */
$ ]" V/ o; v& X' n    OS_ENTER_CRITICAL(); /* Avoid race condition */
+ g- G5 s( l1 w% C6 y- J+ _, j3 F    p_stLog->p_stNext = s_pstHeadLog;
6 b; }1 L, K/ L3 ]; F: M    s_pstHeadLog = p_stLog;+ L/ O+ ]# y3 A' P/ _
    ++s_byNumUsedLog;7 B, g6 B- M; g/ V' \
    OS_EXIT_CRITICAL();4 b( v0 q8 i8 h' l9 r8 x& u
   
4 p9 @- A! v! `    return;1 x1 E5 Z( B8 g
}
- X5 ^4 [+ g+ T5 ~% _, j/**********************************************************                                                             *                       Unlog DMem
4 x4 H& x1 A5 J' @* Description : Remove an allocated memory from log pool
0 r) l) D/ o8 f+ {; Q* Arguments  : const void *p_vAddr point to address of this allocated memory by this pointer
; J* Z# `6 T4 B7 z) u: w* Returns      : void
8 x! Z) Y" d& H1 h: O  ]* Notes        :
1 s+ Z, v! F" ~**********************************************************/* ^& _  h/ i/ S6 u' M2 W7 O, e9 B
static void UnlogDMem(const void *p_vAddr)/ `( Z1 l3 Q! h3 A/ @8 G' V, k# F
{& R: z, G9 \) b! E
    ASSERT(p_vAddr);1 I  F2 ]# B; m! n
    DMEM_LOG    *p_stLog, *p_stPrev;2 x, o9 N5 @2 h, Z; v5 P
    #if OS_CRITICAL_METHOD == 3
3 u( _/ R( r$ ?4 E" b, p* p. ~    OS_CPU_SR  cpu_sr;& C2 ]& ~/ R, ]
    #endif+ Y8 ], m( B) f8 Z6 _7 F; c0 v- L
    /* Search the log */% O8 s9 g; ?. X! B) `: W  l
    OS_ENTER_CRITICAL(); /*Avoid race condition */
  o, ]  V) D# n- Q. O    p_stLog = p_stPrev = s_pstHeadLog;4 ?0 G9 s$ _+ i7 Q9 i  F, O$ l7 y9 K
    while (p_stLog)
$ e3 n! ~+ y9 G# F5 e, W, f    {
. O6 }2 M& ?( Q5 ]& x5 k! s        if (p_vAddr == p_stLog->p_vDMem)
0 |. h+ m/ ]5 i9 J2 n5 h        {/ `: X) E' M9 [% b
         break; /* Have found */
. Y2 l6 u) }2 F- @        }          / Q# N  W5 m5 L9 t0 ?
        p_stPrev = p_stLog;        - Q# C( K: v# g% C% m: v
        p_stLog = p_stLog->p_stNext;    /* Move to next one */
6 O. ?. y3 J0 E) z' s1 L    }
) g/ v7 J2 q9 x0 F" K   
  t; ~* g8 }, h  b  S! w    if (!p_stLog)9 M0 n8 d" E  t
    {
0 Z  W. a* B. I4 `. v        OS_EXIT_CRITICAL();
( q1 b! Z" S& Q* L6 P        PRINTF("Search Log failed.\r0 |( u+ _# o: J5 u* W$ H
");         . \  }; I  M& @$ n/ d
        return;
4 N  ~% ?7 {4 ?+ k+ e    }
5 `! \2 _9 z$ X    /* Remove from used pool */
9 q4 ~1 C7 r, L9 O1 b% U- x    if (p_stLog == s_pstHeadLog)" o' d# r- }" x1 v3 Q6 m: Y2 J) a+ {
    {
* J5 \3 ~9 S; E' U' {9 D1 b( ^     s_pstHeadLog = s_pstHeadLog->p_stNext;
; q' r0 Z5 ]% F$ ~2 j    }
- M# e! O) y$ d$ |. c" p( _    else
% a. g3 I0 |+ t7 h3 T) E5 _    {0 W, F7 J# M3 V) p0 w
     p_stPrev->p_stNext = p_stLog->p_stNext;& j( A! c) V+ ~. h  }
    }
: c2 Z1 u$ [2 y+ M1 [4 _: W    --s_byNumUsedLog;/ G& I! u' _+ H+ I3 F$ o/ Z% c
    OS_EXIT_CRITICAL();% r4 c6 b% {" F
    /* Don't need to protect this log that is free one currently */
1 P4 |3 ~2 |0 s7 o    p_stLog->p_vDMem = NULL;
4 T. O6 E  `+ k+ A- W  R    p_stLog->iSize = 0;$ E) B- E( _2 t' r9 M8 @
    /* Add into free pool */
6 M) D: }. m( m0 ?+ Q    OS_ENTER_CRITICAL(); /* Avoid race condition */
* ?9 x0 f$ A. K  x$ N) {- W    p_stLog->p_stNext = s_pstFreeLog;/ F5 F% c: c- T  `4 _1 p; M$ J  W6 g
    s_pstFreeLog = p_stLog;
! T6 ^1 n$ |" d- |  C1 t# ^6 \) o    OS_EXIT_CRITICAL();
6 r+ r. I+ B% ~$ M; l    return;+ O0 _0 k8 N: G
}5 }$ ]2 S2 j, p8 o) j4 C
带日志记录功能的内存分配MallocExt()和内存释放FreeExt()函数:4 d& b2 h, U" u
/*********************************************************                                                    3 k" ^8 [+ _8 A
*                      Malloc Extension
* _/ W# }2 ?% z! g! N8 B* Description : Malloc a block of memory and log it if need& C: p3 r2 F# n; O! i) g
* Arguments : INT32S iSize    size of desired allocate memory
, T2 I. J- g# c7 x* Returns: void *NULL= failed, otherwise=pointer of allocated memory- @$ Q1 D6 K. b/ Z  Y) H
* Notes        :" `! s2 `" e3 y" J& F
**********************************************************/
! ^7 }6 X6 F+ Z* K3 I  Fvoid *MallocExt(INT32S iSize)5 \- O! T9 q) }& y5 A  k
{
7 X6 P2 p% d6 L% f  m    ASSERT(iSize > 0);/ G! `1 M( f4 h' Q
    void *p_vAddr;6 M9 B. X- N3 z
    p_vAddr = malloc(iSize);
4 \! a6 I/ J# E8 Y    if (!p_vAddr)
) W+ g! T1 E0 I    {2 f5 a( g2 r2 ]) Q
     PRINTF("malloc failed at %s line %d.\r
4 m! B- \+ d5 o7 v+ P1 ]" A4 L; `", __FILE__, __LINE__);
- r! \" b  J! m$ v. _4 B3 g    }
; M* ^! H: R9 W* j    else
5 P/ f) D* H+ c& Y- \( d    {
9 ~& E' r( f  U: Z- M+ L" u* x) g        #if (DMEM_DBG && DBG_VER)
; g0 `$ }  Y4 P: a, w) P3 u$ a        memset(p_vAddr, 0xA3, iSize); /* Fill gargage for debug */3 S4 T9 D- k+ H1 q) l
        LogDMem(p_vAddr, iSize);    /* Log memory for debug */5 t/ A$ r8 u% @! g- M9 ~8 _
        #endif# o6 d0 J1 m% H# n  w! M
    }
, O3 E8 t; D0 J7 b* ?0 c6 S! p# y    return p_vAddr;     
  l5 O. v% z8 y+ |" P}5 D! S$ L' s/ l9 c8 p5 u
/**********************************************************% s( m# i& v+ o7 q) m
*                      Free Extension- d3 G: r( Z, I5 {+ R4 g3 b
* Description : Free a block of memory and unlog it if need! S) w6 }9 C- |. R0 D
* Arguments  : void * p_vMem point to the memory by this pointer9 I/ ~; o& o) W3 p5 Z3 U# q, G
* Returns      : void
2 Q6 m% T0 O7 X1 a. b0 {: ]* Notes        :" w) t0 N( x( J% B$ Z1 b
**********************************************************/
9 {5 j  L/ P! _5 u/ {% D! Ovoid FreeExt(void *p_vMem)- p8 F( Z# y& ^; [* P
{/ n* M; s8 Y1 B' @/ @6 n4 @' I! p
    ASSERT(p_vMem);
7 U% S4 \0 I  V$ o! e  T, c    free(p_vMem);  
, D( x/ f2 W) P. m" z& o* I    #if (DMEM_DBG && DBG_VER)
* P& U+ ]9 a% v, k( y8 a" O    UnlogDMem(p_vMem);    /* Remove memory from log */
' h* n% h; O8 ~2 ~) a: f( C2 W    #endif! S3 L& s& O4 D' p8 r' l& X5 u
    return;* w, U- ^0 r% A0 g3 b' |
}
1 G7 [, C: O+ \( F3 q9 N
' k, _9 N/ P4 L原文:https://blog.csdn.net/jiangjunjie_2005/article-END-1 ]9 M6 E: ]* ]8 C) M! m
往期推荐:点击图片即可跳转阅读5 P) u9 K: v% Z6 q. R5 ^

tcypmv2piaj64020244812.jpg

tcypmv2piaj64020244812.jpg
2 j) P+ R. @+ K: j
最近在画图,电源稳定性对嵌入式硬件设备来说,实在是太重要了!
. W. y$ t9 T% J- y& h* P

4unjps1ahtc64020244912.jpg

4unjps1ahtc64020244912.jpg
8 V, B5 o4 D3 a1 @& Y
嵌入式设备能在LCD屏幕上显示中文,是基于什么原理?
7 \4 p) Z7 V$ U1 F3 x0 Y6 O  p) c

zogznx4bkjz64020245012.jpg

zogznx4bkjz64020245012.jpg

( s, m7 W6 r6 Q! \7 V嵌入式软件,代码的可读性与可运行性,哪个更重要?
0 n7 A' f2 ]) Q0 R我是老温,一名热爱学习的嵌入式工程师
- c- D# S. q$ d& x9 S关注我,一起变得更加优秀!
回复

使用道具 举报

发表回复

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

本版积分规则


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