找回密码
 注册
关于网站域名变更的通知
查看: 376|回复: 3
打印 上一主题 下一主题

常见的内存错误及对策

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2021-10-20 13:37 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
对于用C或C++除了考虑上层应用,还需要考虑底层的内存管理,或者说内存泄漏的问题。
$ Q: ^# V, `3 W1、指针没有指向一块合法的内存4 l8 l7 ^9 b) R1 F
定义了指针变量,但是没有为指针分配内存,即指针没有指向一块合法的内存。
& a5 m1 {" h8 ^( P  s①结构体成员指针未初始化
& Q, E$ |3 c5 g0 {8 m. f7 y定义一个结构体变量,但是结构体内部定义了指针成员,往往应用结构体变量的时候如果不给这 个成员指向一个合法的地址只是给成员分配了字节数,应用的时候对应内存的区域指针成员是无权 访问的,所以需要对结构体分配内存,结构体成员中指针变量也要分配内存,否则访问不到有效地址。
' W$ r! r5 o9 s0 G  W! V% [1 c& l1 i4 B6 ]; K6 O* K
②没有为结构体指针分配足够的内存
1 g: [, H: }3 }: y: j0 U分配内存的时候,分配的内存大小不合适,比如开辟内存空间时sizeof(struct stu)误写为sizeof(struct stu *),书写错误会导致开辟空间不正确。! A* x6 v2 H2 h, K; W( R
! _6 x; B6 `2 ~& m0 z+ e( `2 `$ {
③函数的入口校验! |+ a) Q6 o$ Y) c1 q
不管什么时候,我们使用指针之前一定要确保指针是有效的。一般在函数入口处使用         assert (NULL !=p)对参数进行校验。在非参数的地方使用if(NULL != p)来校验。但这都有一个要求,也就 是p在定义的同时被初始化为NULL,如果没有被初始化为NULL,那么校验也起不了作用,没有被 初始化的指针变量,内部是一个非NULL的乱码  n3 ]' ]; ]& C' o2 @
assert是一个宏,而不是函数,包含在assert.h头文件中。如果其后面括号里的值为假,则程序终 止运行,并提示出错;如果后面括号里的值为真,则继续运行后面的代码。
1 Z* p1 _0 x# y+ H4 r! T5 W$ y) S1 o! R1 Q3 W
2、为指针分配的内存太小或内存访问越界
  v  \$ z8 ~/ S! w6 M为指针分配了内存,但是内存大小不够,导致出现越界错误。
1 E, I& ?7 m$ P, p1 [" z9 Q0 u/ F" a% E6 K$ N* c0 J+ x
通常这种问题都会出现在我们容易忽略的字符串常量中,往往会忘记结束标志“\0”,在开辟内存的时候sizeof计算中需要把结束标志加上,还有计算分配空间大小的时候最好用sizeof来操作,移植性也好。
- k; x; ?+ K  h7 A
3 z8 `) J2 Q4 k" m再有内存分配成功,且已经初始化,但是操作越过了内存的边界。
. Y" l/ X+ d6 n5 h% D  S' S+ p# J/ `! K1 I! o. _
这种错误经常是由于操作数组或者指针时出现“多1”或“少1”而出现的。比如:
/ A+ C- i& T8 l/ @# x$ C2 b) t; Jfor(int i = 0; i < = 10;i++)
; a: |. V3 F/ D( E, t6 N1 p. P) i
* v# X9 F' _, ], p; i8 V7 ]一般for循环的循环变量一定要使用半开半闭的区间,而且如果不是特殊情况,循环变量尽量从0开始。
$ @  j+ x, q7 ?- p) |2 K2 l; E& P0 K& K0 I# p1 m% c
3、内存泄漏
. s5 r2 \, O9 ^内存泄漏几乎是很难避免的,不管是老手还是新手,都存在这个问题。甚至包括Windows、Linux这类软件,都或多或少有内存泄漏。也许对于一般的应用软件来说,这个问题似乎不是那么突出,重启一下也不会造成太大损失。但是如果你开发的是嵌入式系统软件,比如汽车制动系统、心跳起搏器等对安全要求非常高的系统,你总不能让心脏起搏器重启吧。
* l0 E% p9 e( e) _( \8 ]7 q% F3 b/ H( Y/ L/ T9 G7 T8 g
会产生内存泄漏的内存就是堆上的内存,也就是由malloc系列函数或new操作符分配的内存。如果用完没有及时free或delete,这块内存就无法释放,直到整个程序终止。
9 _2 T  _- h6 |3 u% b1 d% P1 }4 B/ N: A& x! G$ D# g
malloc是一个函数,专门用来从堆上分配内存,malloc函数的返回值是一个void类型的指针,参数是申请分配的内存大小,内存分配成功后,malloc函数返回这块内存的首地址,需要一个指针来接收这个地址,那么申请了就能成功吗?不一定的,如果所申请的内存块大于目前堆上剩余内存块,则内存分配失败,函数返回NULL。我们需要知道内存申请分配是连续的一块内存,如果剩余内存不够返回就是空,那么我们可以用校验的方式(if(NULL != p))来验证是否分配成功。当然也可以申请0字节内存,返回不是NULL,是一个正常的内存地址,但是你却无法使用这块大小为0的内存,这就好比尺子上的某个刻度,刻度本身并没有长度,只有某两个刻度一起才能量出长度。' i" p2 \  t8 h0 R8 q3 e

5 H- m  U! K' G) ~再来说下内存释放,既然有分配,就有释放,不然的话,有限的内存总会用光,而没有释放的内存却在空闲。与malloc对应的就是free函数了。free函数只有一个参数,就是所要释放的内存块的首地址,比如:free(p);这个函数主要是让指针变量和这块内存脱离关系,从此这个指针变量和分配过的这快内存没有关系了,那么指针变量p本身保存的地址并没有改变,但是它对这个地址处的那块内存却已经没有所有权了。释放后,那块内存里面保存的值并没有改变,只是再也没有办法使用了。' d  i) C3 F/ L- E: t1 s" f2 U9 Q

, T) \: d( J7 }' G3 L5 s. H, k% f需要记住一点malloc和free是匹配使用的,如果多写两次malloc或者free都会出错。
6 A( p# a6 N% p: D, A
' a# ~/ ?  d+ o- a2 m* O5 [再有内存释放之后需要给指针变量赋值为NULL,如果没有把指针置NULL,这个指针就成为了野指针,这是很危险的,也是经常出错的地方。$ e/ v) A% A$ @! D) V6 U

  r/ Z% O) T0 U, o! v6 ~% ^5 Z# n4、内存已经被释放了,但是继续通过指针来使用. }9 ]6 U, ?$ s
一般会有以下三种情况:, B+ E& @3 l- w, K7 }
①就是上面所说的,free(p)之后,继续通过p指针来访问内存,解决的办法就是给P指NULL。; g; g. B4 }8 U: v) E1 ]5 L9 ~

. ?/ ^+ J1 j) ^* \" t6 K) S$ `: r②函数返回栈内存,这是初学者最容易犯的错误。比如在函数内部定义了一个数组,却用return 语句返回指向该数组的指针。解决的办法就是弄明白栈上变量的声明周期。
7 I, u& Y( N0 Z3 w, ~8 i
4 u. J- k* l) t2 r1 t+ \③内存使用太复杂,弄不清到底哪块内存被释放,哪块没有释放。解决的办法是重新设计程序,改善对象之间的调用关系。- o. ?5 y5 D! p: h$ K3 s
写程序就是要多练习,多调试代码,同时多总结经验,少走弯路。共勉。
: n( [& p/ Y+ s) A! c1 c0 r8 a0 W0 ]( _8 R2 V. H! a6 b

该用户从未签到

2#
发表于 2021-10-20 14:37 | 只看该作者
会产生内存泄漏的内存就是堆上的内存,也就是由malloc系列函数或new操作符分配的内存

该用户从未签到

3#
发表于 2021-10-20 16:43 | 只看该作者
malloc是一个函数,专门用来从堆上分配内存

该用户从未签到

4#
发表于 2021-10-20 16:44 | 只看该作者
malloc和free是匹配使用的
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-10-6 05:32 , Processed in 0.125000 second(s), 23 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表