繁体   English   中英

C ++中的内存管理问题

[英]memory management issues in C++

我想知道与C和C ++相关的常见内存管理问题是什么。 我们如何调试这些错误。

我知道的很少

1)未初始化的变量使用

2)删除指针两次

3)写出数组越界

4)未能释放内存

5)比赛条件

1)malloc传回一个NULL指针。 您需要将此指针强制转换为您想要的任何内容。

2)对于字符串,需要为结束字符分配一个额外的字节。

3)双指针。

4)(删除和malloc)和(免费和新)不一起去

5)查看实际函数在失败时返回(返回代码)的内容,如果失败则释放内存。 6)检查大小分配内存malloc(func +1)

7)检查你如何通过双pointe ** ptr来运行

8)检查行为未定义函数调用的数据大小

9)内存分配失败

使用RAII(资源获取是初始化)。 您几乎不应该在代码中直接使用new和delete。

首先抢先防止这些错误:

1)将警告转到错误级别以克服未初始化的错误。 编译器会经常发出这样的警告,并将它们作为错误访问,您将被迫解决问题。

2)使用智能指针 你可以在Boost中找到这些东西的好版本。

3)使用载体或其他STL容器 除非您使用其中一种Boost变种,否则不要使用数组。

4)同样,使用容器对象或智能指针为您处理此问题。

5)在任何地方使用不可变数据结构,并在共享可变对象的修改点周围放置锁。

处理遗留应用程序

1)与上述相同。

2)使用集成测试来查看应用程序的不同组件如何发挥作用。 这应该找到很多这种错误的情况。 认真考虑由另一个小组完成正式的同行评审,编写与您的裸指针接触的应用程序的不同部分。

3)您可以重载new运算符,以便在对象之前和之后分配一个额外的字节。 然后应该用一些易于识别的值填充这些字节,例如0xDEADBEEF。 您需要做的就是检查之前和之后的前一个字节,以确认您的内存是否以及何时被此类错误损坏。

4)通过多次运行应用程序的各种组件来跟踪内存使用情况。 如果你的记忆力增长,检查是否缺少解除分配。

祝你好运。 对不起,但这是99.9%的时间可以工作的事情之一,然后,繁荣! 客户抱怨。

除了已经说过的所有内容之外,使用valgrind或Bounds Checker来检测程序中的所有这些错误(竞争条件除外)。

一个你忘了:

6)释放后取消引用指针。

到目前为止,每个人似乎都在回答“如何预防”,而不是“如何调试”。

假设您正在使用已经存在其中一些问题的代码,这里有一些关于调试的想法。

  • 未初始化的变量使用

    编译器可以检测到很多这种情况。 将RAM初始化为已知值有助于调试那些转义的内容。 在我们的嵌入式系统中,我们在离开引导加载程序之前进行内存测试,这使得所有RAM都设置为0x5555。 这对调试非常有用:当一个整数== 21845时,我们知道它从未被初始化。

    1. 删除指针两次

Visual Studio应该在运行时检测到这一点。 如果您怀疑在其他系统中发生这种情况,您可以通过将自定义代码替换为删除调用来进行调试

void delete( void*p){ assert(*(int*)p!=p); _system_delete(p); *(int*)p=p;}
  • 写数组越界

Visual Studio应该在运行时检测到这一点。 在其他系统中,添加自己的哨兵

int headZONE = 0xDEAD;
int array[whatever];
int tailZONE = 0xDEAD;

//add this line to check for overruns 
//- place it using binary search to zero in on trouble spot
assert(headZONE==tailZONE&&tailZONE==0xDEAD)
  • 未能释放内存

    观察堆栈增长情况。 记录创建和销毁对象的点之前和之后的空闲堆大小; 寻找意想不到的变化。 可能会在内存函数周围编写自己的包装来跟踪块。

  • 竞争条件

    aaargh。 确保您拥有具有准确时间戳的日志记录系统。

  1. 使用一个好的编译器并将警告级别设置为最大值
  2. 包装new / malloc并删除/释放并预订所有分配/解除分配
  3. 用一个执行边界检查的数组类替换原始数组(或使用std :: vector)(在C中更难做)
  4. 见2。
  5. 这很难,有一些特殊的调试器,如jinx专门研究这个,但我不知道它们有多好。

确保您了解何时将对象放在堆上以及何时放在堆栈上。 作为一般规则,如果必须,只将对象放在堆上,这将为您带来很多麻烦。学习STL并使用标准库提供的容器。

看一下我之前的回答“任何理由来重载全局新的并删除?” 您将在这里找到许多有助于早期检测和诊断的内容,以及一系列有用的工具。 大多数工具和技术可以应用于C或C ++。

值得注意的是, valgrind的memcheck会发现你的4个项目,而helgrind可能会帮助发现最后一个(数据竞赛)。

1)未初始化的变量使用

由编译器自动检测(将警告变为完全并将警告视为错误)。

2)删除指针两次

不要使用RAW指针。 所有指针都应该在智能指针或管理指针生命周期的某种形式的RAII对象内部。

3)写出数组越界

不要这样做。 这是一个逻辑错误。 你可以通过uisng一个容器和一个抛出越界访问的方法来缓解它(vector :: at())

4)未能释放内存

不要使用RAW指针。 见上文(2)。

5)比赛条件

不要允许他们。 按优先级顺序分配资源以避免冲突锁定,然后在可能存在多个写入访问时锁定对象(或在重要时读取访问权限)。

我所知道的最好的技术是避免直接进行指针操作和动态分配。 在C ++中,使用引用参数优先于指针。 使用stl对象而不是滚动自己的列表和容器。 使用std::string而不是char * 如果做不到这一点,请采取Rob K的建议,并在需要进行调整的地方使用RAII。

对于C,你可以尝试做一些简单的事情,但你几乎注定要失败。 获取Lint的副本并祈求怜悯。

我使用的一种常见模式如下。

我在所有分配器类中保留以下三个私有变量:

  size_t news_;
  size_t deletes_;
  size_t in_use_;

在allocator构造函数中,所有这三个都初始化为0。

然后,每当分配器执行一个新的时,它会增加news_,并且每当分配器执行删除时,它会增加deletes_

基于此,我在分配器代码中放了很多断言:

 assert( news_ - deletes_ == in_use_ );

这对我很有用。

另外 :我将断言作为前置条件后置条件放在分配器的所有非平凡方法上。 如果断言blovs,那么我知道我做错了什么。 如果断言没有吹,我可以做的所有测试,那么我对我的程序的内存管理正确性有了足够的信心。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM