[英]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时,我们知道它从未被初始化。
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。 确保您拥有具有准确时间戳的日志记录系统。
确保您了解何时将对象放在堆上以及何时放在堆栈上。 作为一般规则,如果必须,只将对象放在堆上,这将为您带来很多麻烦。学习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.