C++编程学习——内存出错


. 数组越界;

. 内存泄露;

. 悬空指针(野指针);

. 错误分配。

 

1.     数组越界:

 

程序反应:

 跳出“内存不足。”提示框。

调试的时候无法定位到准确的出错点,也就是程序崩溃的地方不确定。

但是大致出错会在数组所在的函数里面或调用了数组的函数里,如果总弹出以上对话框,就检查出错代码附近是否有用到数组,特别是边界处要仔细检查。

应用vector的时候也要注意越界问题。

2. 内存泄露

 

new后一定要delete,malloc之后一定要free。

程序反应:

一是可能像上面一样弹出对话框提示。//一般不会出现。

二是可能在Debug版下编译运行均不会崩溃,但是Release版下会导致程序崩溃,这一般是内存泄露的问题。

2.1:1.delete的时候为什么会报错?2.在哪里delete比较合适?

 

BOOL Test(char*& ptStr)

{

 

      CString  temp;

      temp = m_pListCtrl->GetItemText(iRow, iCol);

 

      ilen = temp.GetLength()+1;   //必须加1

      ptStr = new char[ilen];

      strcpy(ptStr,temp); 

 

      // delete ptStr;

      return TRUE;

}

void CallTest()

{

      char* cstemp = NULL;

for( i=0;i<Count;i++)

      {

             Test(cstemp);

delete cstemp;

      }   

}

2.2:构造函数和析构函数对指针的操作

 

class CFM3Dlg

{

public:

      CFM3Dlg();

      ~CFM3Dlg();

Cs2 * cs2;

}

CFM3Dlg::CFM3Dlg()

{

      cs2 = NULL;

}

CFM3Dlg::~CFM3Dlg()

{

      if (cs2)

      {

             delete cs2;

             cs2 = NULL;       

      }

}

// 用cs2指针之前最好先判断cs2指针是否为空。

2.3:来自资源错误管理的潜在堆内存丢失

 

int getkey(char *filename)

{

FILE *fp;

int key;

fp = fopen(filename, "r");

fscanf(fp, "%d", &key);

return key;

}

“资源”不一定仅仅指“内存”,如FILE句柄可能与内存块不同,但是必须对它们给予同等关注。

fopen的语义需要补充性的 fclose。在没有 fclose()的情况下,C 标准不能指定发生的情况时,很可能是内存泄漏。其他资源(如信号量、网络句柄、数据库连接等)同样值得考虑,像excel中的一些应用,用完后也都要release。

 

2.4:new时用了[],delete时也要用[]。new时没有用[],delete时也不要用[]。

 

string *stringarray = new string[100];

...

delete stringarray;

 

stringarray指向的100个string对象中的99个不会被正确地摧毁,因为他们的析构函数永远不会被调用。

 

string *stringptr1 = new string;

string *stringptr2 = new string[100];

...

delete stringptr1;// 删除一个对象

delete [] stringptr2;// 删除对象数组

 

2.5:内存耗尽怎么办?

 

如果在申请动态内存时找不到足够大的内存块,malloc和new将返回NULL指针,宣告内存申请失败。通常有三种方式处理“内存耗尽”问题。

(1)判断指针是否为NULL,如果是则马上用return语句终止本函数。例如:

void Func(void)

{

    A*a = new A;//int

   if(a == NULL)//对于内存有限的小机最好作此判断

    {

       return;

    }

    …

}

(2)判断指针是否为NULL,如果是则马上用exit(1)终止整个程序的运行。例如:

void Func(void)

{

    A*a = new A;

   if(a == NULL)

    {

       std::cout << “Memory Exhausted” << std::endl;

       exit(1);

    }

    …

}

(3)为new和malloc设置异常处理函数。例如Visual C++可以用set_new_hander函数为new设置用户自己定义的异常处理函数。

在<new>文件中有:

typedef void (*new_handler)();

new_handler set_new_handler(new_handler p)throw();

用户自定义:

void nomorememory()

{

      std::cerr << "unable to satisfy request for memory/n";

      abort();

}

void OnCancel()

{

      set_new_handler(nomorememory);    //报错

//set_new_handler(NULL);           //调用std::bad_alloc异常, bad_alloc是operatornew不能满足内存分配请求时抛出的异常类型。

      int *pbigdataarray = new int[1000000000];

}

3.   悬空指针(野指针)

 

“调试”难以识别悬空指针。“野指针”不是NULL指针,是指向“垃圾”内存的指针。人们一般不会错用NULL指针,因为用if语句很容易判断。但是“野指针”是很危险的,if语句对它不起作用。 “野指针”的原因主要有如下几种:

(1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。例如

char *ip = NULL;

char *ip = new char;

(2)指针ip被free或者delete之后,没有置为NULL,让人误以为ip是个合法的指针。

    Delete后ip所指的对象清空,但指针仍然还有值。

若内存在释放后立即被覆盖,并且新指向的值不同于预期值,但是却很难识别出新值是错误值。

(3)指针操作超越了变量的作用范围。这种情况让人防不胜防,示例程序如下:

class A

{

   public:

   void Func(void){ std::cout << “Func of class A” <<std::endl; }

};

void Test(void)

{

    A*p;

    {

       A a;

       p = &a; // 注意 a 的生命期

    }

   p->Func(); // p是“野指针”

}

函数Test在执行语句p->Func()时,对象a已经消失,而p是指向a的,所以p就成了“野指针”。但奇怪的是有些编译器运行这个程序时居然没有出错,这可能与编译器有关。

4.   错误分配

 

错误分配的管理不是很困难, 此类错误都会被快速地检测到。

4.1两个错误的内存释放

 

/* Allocate once, free twice. */

void f3()

{

   char *p;

    p =malloc(10);

    ...

   free(p);

    ...

   free(p);

 }

/* Allocate zero times, free once. */

void f4()

{

   char *p;

/* Note that p remains uninitialized here. */

   free(p);

}

4.2未初始化的指针

 

void f2(int datum)

{

int *p2;

/* Uh-oh! No one has initialized p2. */

*p2 =datum;

...

你可能感兴趣的:(C++编程学习——内存出错)