电子产业一站式赋能平台

PCB联盟网

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

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

[复制链接]

545

主题

545

帖子

3475

积分

四级会员

Rank: 4

积分
3475
发表于 3 天前 | 显示全部楼层 |阅读模式
我是老温,一名热爱学习的嵌入式工程师4 B4 U! b; O( P$ I% `! P  C: N
关注我,一起变得更加优秀!一.  常见错误与预防 1.   分配后忘记释放内存void func(void)
/ [1 G9 Z: F$ |& u" H4 |. A. I{
) G3 I3 M0 [4 W( n    p = malloc(len);% i# a0 {! |/ f; R/ w: Q' S% f6 y
    do_something(p);
- g( O) u6 d& `6 Q2 [- z4 S: Z' L    return;  /*错误!退出程序时没有释放内存*/
! {( Z) \( O- ~! o( C}
. d9 o5 v1 p. D/ ]预防:  编写代码时malloc()和free()保证成对出现,避免忘记资源回收。
# @* A& o" t8 j5 E; P! Wint func(void)
- V: e' M7 g: {3 c* W7 Z; u{
8 h$ x) i1 b" Q6 u- f    p = malloc(len);
+ ]* ]: y1 a* \* O    if (condition)
( [* N$ Z0 ]# I( U7 y        return -1;  /*错误!退出程序时没有释放内存*/: J/ n% }8 N: p
    free(p);
; S( Y: \' ~/ B* S/ k2 V" H    return 0;
7 q* d3 D  \7 x' S% ^( P, ~}
" q# N9 A4 \" e预防: 一旦使用动态内存分配,请仔细检查程序的退出分支是否已经释放该动态内存。& H7 V: N( t$ G7 x# T7 Q  t' Z
2.   释放内存调用错误指针void func(void)0 I  F* m& r" z/ S2 ~, l/ f
{# H* h2 i2 O! P+ Q7 O' f2 X# F
    p = malloc(len);
! ~2 u8 t! e+ m; B    val = *p++;  /*错误!动态内存句柄不可移动*/& K, D( Y: a. C- g; c3 x
    free(p);) P) x* ?6 K3 B5 J+ O# R$ ^
}
3 E3 X- |$ n8 y( @) N! {预防: 千万不要修改动态内存句柄!可以另外赋值给其他指针变量,再对该动态内存进行访问操作。  g. L  }" Z; y7 k
3.   分配内存不够导致溢出void func(void)
2 k. L/ x2 s. u5 r3 l& r{  E8 l; k' [, q% Q1 i) @2 j
    len = strlen(str);
# V- z/ u$ ~7 W$ ^/ |7 H    p = malloc(len);
3 E  |5 V! V4 n    strcpy(p, str);  /*错误!str的’\0’写到动态内存外*/
% U' ~: A0 s3 g* U}
! u" b7 M+ j8 I4 J预防:  分配内存前仔细思考长度是否足够,千万注意字符串拷贝占用内存比字符串长度大1。  o- \  d/ Q) ]
二.  自动查错机制 尽管在开发过程中坚守原则和谨慎编程甚至严格测试,然而内存泄露的错误还是难以杜绝,如何让系统自动查出内存泄露的错误呢?
, _+ Q5 M# }3 D一种比较好的方法是建立日志块,即每次分配内存时记录该内存块的指针和大小,释放时再去除该日志块,如果有内存泄露就会有对应的日志块记录这些内存没有释放,这样就可以提醒程序员进行查错。, g. m2 n2 W1 D( W9 {: P
有了上述日志块操作函数,再来实现动态内存分配与释放函数就很容易了。只有当处于DEBUG版本和打开内存调试DMEM_DBG时才进行日志登录,否则MallocExt()和FreeExt()函数与malloc()和free()是等价的,这样保证了系统处于发布版本时的性能。( c& y2 O1 p1 G$ u: D3 G4 e
(代码已经过严格测试,但这不是盈利的商业代码,即没有版权。但如果因代码错误带来的任何损失作者具有免责权利)2 l# c) i4 C6 e# @& Y5 U
代码部分:首先定义日志块结构体:
  ]( R% i" g& L+ F. u/* Log of dynamic memory usage */* C. o. A9 `( _6 n& _
typedef struct _dmem_log; c4 N( P0 M+ M* u
{
" ^; S* t5 a3 Z9 {7 b  q    struct _dmem_log *p_stNext; /* Point to next log */
4 |1 T+ t+ @: }    const void *p_vDMem; /* Point to allocated memory by this pointer */2 x- A+ X& |7 ]7 j. h9 F! s
    INT32S iSize; /* Size of the allocated memory */- b( V, w3 `0 c  Y7 r) F
} DMEM_LOG;
3 r$ m$ g8 x7 W  h7 M& {# D! l" O然后为该结构体开辟内存:
& D' B6 s( Q# R* J) B) r- N  gstatic DMEM_LOG *s_pstFreeLog; /* Point to free log pool by this pointer */
/ E6 |+ U* s3 f- {$ Astatic INT8U s_byNumUsedLog;' @6 M8 k  S& c& g/ r" d
static DMEM_LOG *s_pstHeadLog; /* Point to used log chain by this pointer */8 k2 f' f9 ^" I  ]
/* Pool of dynamic memory log */* d5 F4 I' D' S# X
#define NUM_DMEM_LOG 20
' I+ K/ M: X; a* Sstatic DMEM_LOG s_astDMemLog[NUM_DMEM_LOG];# _# w3 i4 j/ v" E( D5 K
下面是内存日志块的操作函数:初始化、插入日志和移除日志:; k% h: J- }, y1 d6 h- `& x9 I
/**********************************************************                                                             *                    Initialize DMem Log
' d; E! H2 e5 J; X/ {  ~7 ^% f* Description : Initialize log of dynamic memory2 z& T5 [. a1 M9 Z/ D% P' M7 {/ C
* Arguments  : void( W- T, J+ [6 U: H
* Returns      : void. j# |  E) z8 @9 e( D
* Notes        :. v' h) m6 u: i( S! @0 }7 n/ _
**********************************************************/- N4 z& T4 y* I0 a2 S4 }% w9 n& w/ R. k
static void InitDMemLog(void)
9 l& k' ]( x1 E& w& J! j{. ]* o7 v: }4 R! L
    INT16S    nCnt;/ Y/ f+ D0 V& \, i7 h! m
    /* Initialize pool of log */  [+ t" W2 e0 }" q
    for (nCnt = 0; nCnt ' _1 {+ i5 W# i) X3 b
    {6 h" A1 n1 R: k6 q5 d+ \- M
        /* Point to next one */
' B3 G4 z( m3 x% v5 `& F; A        s_astDMemLog[nCnt].p_stNext = &s_astDMemLog[nCnt + 1];
: E1 A+ u, {) m! ^    }7 `  U5 F* i* H4 w8 g8 e
    s_astDMemLog[NUM_DMEM_LOG - 1].p_stNext = NULL;: s( w; H0 I  F3 O& y- [" [8 c3 ]
    s_pstFreeLog = &s_astDMemLog[0]; /* Point to the 1th log */9 c+ v( @; v9 L. o2 k' l. m, V4 j
    return;
$ v: `8 B/ l" d( x" S, T8 t. j}
3 J0 p+ P" ?2 A! [& L/**********************************************************                                                             *                       Log DMem
& u1 O$ H/ g% i9 v) v5 z( ~* Description : Join an allocated memory into log pool) r5 R0 w* w1 a8 W9 ^4 f
* Arguments  : const void *p_vAddr    point to address of this allocated memory by this pointer
) W2 x% ]6 t7 N- S/ U3 @* h*             INT32S iSize    size of this allocated memory
6 Q  c7 n/ U& s3 p9 ]5 U/ o. j1 W* Returns      : void6 G  v0 ?9 u5 G
* Notes        :) d3 ~# y' W$ m& ~7 G( ~7 [; t
**********************************************************/6 c. {3 k3 {  {6 t- {0 r/ j( J
static void LogDMem(const void *p_vAddr, INT32S iSize)
) Z, `9 x; r0 j0 O$ u% T{2 }& t- I' Z& v8 z8 z$ t6 U& D; V
    ASSERT(p_vAddr && iSize > 0);
8 Z* u) b% N- _2 K0 X    DMEM_LOG *p_stLog;
8 h5 Q6 J1 H& Y6 C    #if OS_CRITICAL_METHOD == 3
( O2 M4 |* e; ]/ R    OS_CPU_SR  cpu_sr;; W, _( {0 _' u9 G
    #endif7 v, ~( ^" g! d$ X( s
    + R( t7 r" x; U1 ]7 B
    /* Get a log from free pool */$ x: s; U  @/ V: y/ A' ?3 i
    OS_ENTER_CRITICAL(); /* Avoid race condition on s_pstFreeLog */+ F( m2 [4 K% G# t0 Z- Z+ N6 ?) \+ U
    if (!s_pstFreeLog)
6 v- e( F6 b! @; ?  \* a7 }    {
+ e! f3 C! E  Z; u7 H        OS_EXIT_CRITICAL();
8 {$ [( h, D0 p5 b7 K1 _8 v        PRINTF("Allocate DMemLog failed.\r
; L. M8 G: N( a  ~7 _7 {2 `; I");      
" [2 q0 W  {3 p( w3 ]- k+ S3 f' |        return;! B1 g, c' X; |; o7 L' D/ L
    }" I9 J8 J8 S+ V
    p_stLog = s_pstFreeLog;
) B# a7 \* c! P# D; t    s_pstFreeLog = s_pstFreeLog->p_stNext;
7 R% T- o5 X1 u5 Z% k' B    OS_EXIT_CRITICAL();: l/ M$ o+ p/ |$ r! ]
   
0 c; I# ~. g/ V8 \6 V    /* Don't need to protect this log that is free one currently */+ b$ i& l, ^/ a# m& w$ a0 S/ y" ^
    p_stLog->p_vDMem = p_vAddr;
: Q! D. z1 I6 ~/ L0 a8 ?* L1 W    p_stLog->iSize = iSize;
% k# W, Y3 i2 T% G5 ~: \    /* Put this log into used chain */
0 i- {9 c( t1 @0 b& _2 \    OS_ENTER_CRITICAL(); /* Avoid race condition */0 G1 Q: N; B+ j) E7 V' S. j
    p_stLog->p_stNext = s_pstHeadLog;
1 i% \, P5 M) \" _& T    s_pstHeadLog = p_stLog;
% ]" Z* i, z$ ?: G' n1 e    ++s_byNumUsedLog;
0 c; X8 N: M5 w5 N# M% N5 d    OS_EXIT_CRITICAL();, W* v4 F, v4 ~) p1 G8 x
    ; S* V" M; l4 ]" ]* Y/ |
    return;9 c- U) H$ a6 {  e/ M4 ?
}
* c$ Z; h/ J' F, _/**********************************************************                                                             *                       Unlog DMem
- g+ }+ N  B7 ]3 Z* Description : Remove an allocated memory from log pool
0 H4 Z4 n$ x  `* Arguments  : const void *p_vAddr point to address of this allocated memory by this pointer; e8 C- @' [5 ~6 j
* Returns      : void) K+ i% I8 Y" \4 F6 K+ O3 j- x: o
* Notes        :
0 V& S* [4 X3 S**********************************************************/
2 m" I, G/ J( Y; j" M. f" x# kstatic void UnlogDMem(const void *p_vAddr)# D+ S& l& r* W7 ?$ W
{
# h$ N3 a% g! Z/ j# k8 u8 a, ^    ASSERT(p_vAddr);
  m1 b& P" N- v5 ]    DMEM_LOG    *p_stLog, *p_stPrev;
5 ~0 w$ i- H! ?0 w8 R    #if OS_CRITICAL_METHOD == 3
6 }- ~/ c6 G* X3 g8 h    OS_CPU_SR  cpu_sr;* T% H) ~( c3 O$ a
    #endif& |2 `( ~( R- Q, e! P
    /* Search the log */
0 c! N9 ^& r0 [    OS_ENTER_CRITICAL(); /*Avoid race condition */
7 B( o8 I* h) N# l# P    p_stLog = p_stPrev = s_pstHeadLog;
& F  d- Q0 y& v# R    while (p_stLog)
6 l; x9 C* j/ O1 z9 q7 h6 ~    {" R% S' _; v& T, T7 }5 Q2 Q
        if (p_vAddr == p_stLog->p_vDMem)
& r! o# O6 o# V) @- _# T        {. l- J/ ^$ {$ |0 W% f6 J$ V
         break; /* Have found */
5 Q+ [7 J* d! ?9 V0 {" {: A( [6 A* a        }         
% o7 ]+ K& L7 r' ~; r, J        p_stPrev = p_stLog;        
- {2 }! M$ k) Y; \; P7 ]* L        p_stLog = p_stLog->p_stNext;    /* Move to next one */
3 q& F0 Y/ h# K% a$ X" E: B: U    }
0 {4 Q4 [% u; q    4 `: c% m/ d0 |$ [0 p5 F( |
    if (!p_stLog)
' s) B9 U/ J% J% c3 v) u    {% M: Y4 a& _# F; l$ a+ y9 m! U
        OS_EXIT_CRITICAL();3 n! P5 F3 g! g- D( C, J( I2 i2 S. S
        PRINTF("Search Log failed.\r
% S2 p$ o( W$ p! D- V");         
) J( |7 I: c( U5 a7 H        return;# L+ G# H. Q& ?0 x0 A( ^
    }
* R' I* z6 V$ c1 o2 U" J% @1 Q    /* Remove from used pool */1 p- R% j( ?0 X0 a1 T2 }' K  Y
    if (p_stLog == s_pstHeadLog)* H8 k! [3 Q$ Q8 F2 R9 A  E
    {
  M+ a% ~6 T* u' @* V2 X     s_pstHeadLog = s_pstHeadLog->p_stNext;- c" a; t3 |* [; @  o  g4 y8 O1 V
    }
. w3 Y2 j& n% q0 p. p7 R/ S! t    else
, B8 m% _/ e$ N4 o    {6 a, M! _: J6 f+ C4 e+ d
     p_stPrev->p_stNext = p_stLog->p_stNext;
8 s( E* C' S6 U% h/ x+ H" [  K    }/ `) R# e' \) y7 Q8 y. M. l- E
    --s_byNumUsedLog;! \8 R0 y  @5 x/ r
    OS_EXIT_CRITICAL();. \& A& A# K5 G1 _  R+ t' y; Y
    /* Don't need to protect this log that is free one currently */, O# d: Q* l$ W
    p_stLog->p_vDMem = NULL;1 j0 s+ P4 @4 U, a+ k1 t
    p_stLog->iSize = 0;) [/ T' I! N2 a1 F
    /* Add into free pool */
4 ?# N( i) ?. p$ u9 b7 O: v    OS_ENTER_CRITICAL(); /* Avoid race condition */
# H, ]* r, {: p1 M- }- Y: b! m    p_stLog->p_stNext = s_pstFreeLog;
( O* b; M" C8 S' K    s_pstFreeLog = p_stLog;
* T  B8 C1 |5 E3 G# l( `: P; ], c    OS_EXIT_CRITICAL();* c0 r# g+ J) U8 C) |7 A
    return;# m7 ~( g3 Y7 Z* z) ]& _$ `
}5 {  m$ _6 b+ X6 H/ t/ v% i
带日志记录功能的内存分配MallocExt()和内存释放FreeExt()函数:1 Q; W. S& l2 \
/*********************************************************                                                   
' y! W+ {$ ^+ u( z*                      Malloc Extension
% L$ w! F( S5 K8 [) _5 j) f/ z* Description : Malloc a block of memory and log it if need
5 @5 Y( p8 c4 G  h; c5 ^) R$ }" v; e' U* Arguments : INT32S iSize    size of desired allocate memory( j4 K; E7 w  `4 r; J& u3 D
* Returns: void *NULL= failed, otherwise=pointer of allocated memory
; W% \' I+ Q$ l6 M$ {; d2 p* Notes        :# ?' Z) C+ s& W& w2 q
**********************************************************/' z4 z4 w$ F4 `- _4 l# @% R
void *MallocExt(INT32S iSize)
4 T3 l+ O3 _+ j/ s6 L4 t( _{
3 U: k4 w6 q: l/ j7 Z3 P2 @! X    ASSERT(iSize > 0);. K$ N! B- }! @8 p" Q
    void *p_vAddr;3 r8 R8 r* _2 J2 E; b
    p_vAddr = malloc(iSize);
( y* ~2 V5 F& v  I6 s# V    if (!p_vAddr): q0 m8 [8 o  v- g+ U
    {, U( b+ X/ K$ W0 i7 {- n: U
     PRINTF("malloc failed at %s line %d.\r
  `) M( t+ \9 N", __FILE__, __LINE__);
$ x& {* O+ {: u    }
. m+ ?" a) V2 L# i. U4 Y! c& W, G    else
& n3 i5 D5 \) ]; C; j- K    {
3 f6 P, ?* V% I. W% p5 v% J        #if (DMEM_DBG && DBG_VER)
. _& H9 g3 h1 S        memset(p_vAddr, 0xA3, iSize); /* Fill gargage for debug */
/ A, m  J0 y) z( o, c) Y! n5 O6 w1 N, Y        LogDMem(p_vAddr, iSize);    /* Log memory for debug */6 Y* R8 \5 P" q' r% }3 p
        #endif
( ]4 f0 \, e: J' S" t7 T    }3 a* C3 a* @$ B9 n+ I6 U3 r8 D' H
    return p_vAddr;     - T" g6 \* j$ s% `* {, j9 I. S
}
$ t9 A* ?* S0 R, g" S/**********************************************************
4 T# c. t7 }7 L! O9 n& J*                      Free Extension
& ?& t$ q( j9 \: z* Description : Free a block of memory and unlog it if need
5 a' B7 ?, ]) c* E  Z* Arguments  : void * p_vMem point to the memory by this pointer
7 m) R- v( h* T( \' b: R* Returns      : void
; n  _* _2 q" V5 w4 a. u* Notes        :
0 p! t/ O  t  z  d* \**********************************************************/% y  R6 ?- p7 T5 x( S
void FreeExt(void *p_vMem)0 A% u& I9 H" M7 s1 I
{, \& A' y! m( }
    ASSERT(p_vMem);
8 y) Q& H/ `3 j1 C1 \& l8 r, I    free(p_vMem);  
% F, S0 s- U, \; p5 T    #if (DMEM_DBG && DBG_VER)7 ^4 s) z/ T0 t' o3 y* c
    UnlogDMem(p_vMem);    /* Remove memory from log */. f# H  m* y" I. h
    #endif
, s& N7 N+ m: ^, \. p8 ?3 V0 P    return;
& r' ]7 N. y9 Z}3 g- O; P. p% W. V$ J9 |
% g% P6 L7 X% {4 B8 n8 K& g
原文:https://blog.csdn.net/jiangjunjie_2005/article-END-7 ?$ ~5 G" N$ x: q: N
往期推荐:点击图片即可跳转阅读3 d3 s6 C, y& X: c% ^! U  \

tcypmv2piaj64020244812.jpg

tcypmv2piaj64020244812.jpg
( k6 `; l% [2 \1 x3 U! d
最近在画图,电源稳定性对嵌入式硬件设备来说,实在是太重要了!2 ~# K5 y  ~2 a$ @  a% x

4unjps1ahtc64020244912.jpg

4unjps1ahtc64020244912.jpg
& a  r% ^3 p& X
嵌入式设备能在LCD屏幕上显示中文,是基于什么原理?
: b, k% E, ]! L9 b; q

zogznx4bkjz64020245012.jpg

zogznx4bkjz64020245012.jpg

- k& M1 ^6 M0 w! v嵌入式软件,代码的可读性与可运行性,哪个更重要?" Z6 k: w: k) E7 a. v' a
我是老温,一名热爱学习的嵌入式工程师$ H* U# Q2 l4 r1 f4 _9 S
关注我,一起变得更加优秀!
回复

使用道具 举报

发表回复

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

本版积分规则


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