
简介
对于使用C语言的任何人,如果被问到C语言最大的烦恼是什么,许可能会回答指针和内存泄漏. 这些确实消耗了大多数开发人员的调试时间. 指针和内存泄漏对于某些开发人员来说似乎令人生畏,但是一旦您了解了指针的基本知识及其相关的内存操作,它们便是您在C语言中最强大的工具.
本文将与您分享开发人员在开始使用指针进行编程之前应了解的秘密. 本文包括:
1. 导致内存损坏的指针操作的类型
2. 使用动态内存分配时必须考虑的检查点
3. 导致内存泄漏的场景
如果您事先知道哪里可能出了问题,可以小心避免陷阱,并消除大多数与指针和内存有关的问题.
什么是内存泄漏
在Wikipedia中,内存泄漏的解释如下:
在计算机科学中,内存泄漏是指由于疏忽或错误而无法释放不再使用的内存的程序. 内存泄漏不是指内存的物理消失,而是在应用程序分配内存部分后,由于设计错误,在释放内存部分之前丢失了对内存部分的控制,从而导致内存浪费.
C ++中内存泄漏的主要原因是程序猿申请了内存(malloc(),new)之后,它没有及时释放无用的内存空间,甚至消除了指针,因此该区域的存储空间根本无法释放.
了解内存泄漏的原因,您可以知道如何处理内存泄漏,即: 记住释放未使用的内存空间,并将其留给新年!
内存泄漏可能会导致严重的后果:
●程序运行后,随着时间的推移它将占用更多的内存,最后在没有可用内存时崩溃;
●该程序占用大量内存,导致其他程序无法正常使用;
●该程序占用大量内存,导致消费者选择别人的程序而不是您的程序;
●该公司经常发布会引起内存泄漏错误的程序猿,但该程序很贫乏.
我怎么知道我的程序是否存在内存泄漏?
根据引起内存泄漏的原因及其严重后果,我们可以通过程序的主要性能来查找程序是否存在内存泄漏: 在程序运行了一段时间后,内存占用率一直在缓慢地上升. 时间长了,实际上在您的逻辑中,其中没有太多的内存需求.

如何找到泄漏点?
根据原理,我们可以首先查看我们的代码,使用“查找”功能,查询新建和删除,并查看应用程序和内存释放是否成对释放,这使您可以快速找到更简单的逻辑泄漏情况.
如果仍然发生内存泄漏,则可以确定申请和释放的对象数是否一致. 将静态变量static int count附加到该类;在构造函数中执行count ++;通过在程序结束前对所有类进行销毁,在析构函数中执行count--,然后输出静态变量以查看count的值是否为0. 如果为0,则问题不存在. 如果不为0,则此类型的对象尚未完全释放.
检查在类中申请的空间是否被完全释放,尤其是在父类具有继承的情况下,并查看子类中是否调用了父类的析构函数. 可能是因为子类在被销毁时没有父类. 所请求的内存空间.
对于函数中请求的临时空间,请仔细检查函数是否事先跳出了函数并且没有释放内存.
怎么可能出问题了?
可能会出现几种问题情况,这可能会在生成完成后引发问题. 您可以使用本文中的信息来避免在处理指针时出现许多问题.
未初始化的内存
在此示例中,已为p分配了10个字节. 这10个字节可能包含垃圾数据,如图1所示.
char *p = malloc ( 10 );
图1.垃圾数据
如果代码段在为p赋值之前尝试访问它,则可能会得到垃圾值,并且程序可能会发生不可预测的行为. p可能具有您的程序从未期望的值.
优良作法是始终同时使用memset和malloc分配内存,或使用calloc.
char *p = malloc (10);
memset(p,’\0’,10);
现在,即使相同的代码段在为其分配值之前尝试访问p,该代码段也可以正确处理空值(理想情况下应具有的值),然后将具有正确的行为.
内存覆盖
由于已为p分配了10个字节,因此如果代码段尝试向p写入11个字节的值,则该操作将自动从其他位置“吃掉”而不告诉您“一个字节”. 让我们假设指针q代表此内存.

图2.原始q内容


图3.覆盖q的内容
结果,指针q将具有未曾期望的内容. 即使您的模块编码足够好,由于某些共存模块执行某些内存操作,它也可能具有错误的行为. 以下示例代码段也可以说明这种情况.
char *name = (char *) malloc(11);
// Assign some value to name
memcpy ( p,name,11); // Problem begins here
在此示例中,memcpy操作尝试向p写11个字节,而p仅分配10个字节.
作为一种好习惯,每当将值写入指针时,请确保交叉检查可用的字节数和写入的字节数. 通常,memcpy函数将是为此目的的检查点.
内存超出范围
读取的内存溢出意味着读取的字节数超出了应有的数量. 这个问题不是太严重,这里不再详细描述. 下面的代码提供了一个示例.
char *ptr = (char *)malloc(10);
char name[20] ;
memcpy ( name,ptr,20); // Problem begins here
在此示例中,memcpy操作尝试从ptr读取20个字节,但后者仅分配了10个字节. 这也会导致不良的输出.
内存泄漏
内存泄漏真的很烦人. 以下列表描述了导致内存泄漏的一些情况.
char *memoryArea = malloc(10);
char *newArea = malloc(10);
这将为下面的图4中所示的存储位置分配值.

图4.内存位置
memoryArea和newArea分别分配了10个字节,它们各自的内容如图4所示. 如果有人执行以下所示的语句(指针重新分配)...
memoryArea = newArea;
那肯定会在模块开发的后续阶段给您造成麻烦.

在上面的代码语句中,开发人员将memoryArea指针分配给newArea指针. 结果,先前由memoryArea指向的存储位置变为孤立的,如下图5所示. 由于没有引用该位置,因此无法发布. 这会导致10个字节的内存泄漏.

图5.内存泄漏
在分配指针之前,请确保内存位置没有被隔离.

图6.动态分配的内存
free(memoryArea)
如果通过调用free释放memoryAreacalloc 释放,则newArea指针也将变为无效. 由于没有指向该位置的指针,因此无法释放newArea指向的内存位置. 换句话说,newArea指向的内存位置变成孤立的,导致内存泄漏.
每当释放结构化元素并且该元素包含指向动态分配的内存位置的指针时calloc 释放,应先遍历子内存位置(在这种情况下为newArea),然后从那里释放,然后遍历到父节点.
此处的正确实现应为:
free( memoryArea->newArea);
free(memoryArea);
char *func ( )
{
return malloc(20); // make sure to memset this location to ‘\0’…
}
void callingFunc ( )
{
func ( ); // Problem lies here
}
在上面的示例中,对callingFunc()函数中func()函数的调用未处理内存位置的返回地址. 结果,func()函数分配的20字节块丢失,并导致内存泄漏.
返回您得到的东西
在开发组件时,可能会有很多动态内存分配. 您可能会忘记跟踪所有指针,并且某些内存段没有释放,而是分配给程序.
始终跟踪所有内存分配,并在任何适当的时间释放它们. 实际上,可以开发出一些机制来跟踪这些分配,例如在链接列表节点本身中保留一个计数器.
访问空指针
访问空指针非常危险,因为它可能会使程序崩溃. 始终确保您没有访问空指针.
没有坑可避免指针(内存泄漏)
C ++是母亲最受批评的地方.

当夜晚安静时,取出一些容易用指针出现的凹坑. 也许我的语言描述有些费劲,请尝试用代码说话.
通过指向该类的NULL指针调用该类的成员函数
尝试使用空指针调用类的成员函数导致崩溃:
#include <iostream>using namespace std;class A{int value;public:void dumb() const {cout << "dumb()\n";}void set(int x) {cout << "set()\n"; value=x;}int get() const {cout << "get()\n"; return value;}};int main(){A *pA1 = new A;A *pA2 = NULL;pA1->dumb();pA1->set(10);pA1->get();pA2->dumb();pA2->set(20);//崩溃pA2->get();return 0;}
为什么会这样?
通过非法指针调用函数等同于将非法指针传递给函数!
但是为什么pA2-> dumb()成功了?
因为导致崩溃的原因是访问成员变量! !
使用已释放的指针
struct X{int data;};int foo(){struct X *pX;pX = (struct X *) malloc(sizeof (struct X));pX->data = 10;free(pX);...return pX->data;}
使用未初始化的指针
如果您这样编写,编译器将提示您使用未初始化的变量p.
void fooA(){int *p;*p = 100;}
那如果我释放一个初始化的指针怎么办?
void fooB(){int *p;free(p);}
结果是一样的! !
释放已释放的指针
直接查看代码:
void fooA(){char *p;p = (char *)malloc(100);cout << "free(p)\n";free(p);cout << "free(p)\n";free(p);}
这样的问题可能不会立即使您的程序崩溃,那么后果将更加严重! !
未调用子类的析构函数
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-165535-1.html
弃我就宰你
美日口粮做事的啦