C++指针使用的好坏直接反映了编程人员水平的高低,下面从指针和数组的区别、指针参数是如何传递内存、野指针、malloc/free、new/delete和内存耗尽怎么办方面进行总结。
C++/C程序中,指针和数组在很多地方能够相互替换着用,让人产生一种错觉,觉得二者是等价的。数组要么在静态存储区被建立(如全局数组),要么在栈上被建立。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容能够改变。指针能够随时指向任意类型的内存块,它的特征是“可变”,因此咱们经常使用指针来操做动态内存。指针远比数组灵活,但也更危险。编程
char a[] = “hello”; a[0] = ‘X’; // 数组能够修改字符串内容 char *p = “world”; // 注意p指向常量字符串 // 编译器不能发现该错误 // 但该语句企图修改常量字符串的内容而致使运行出错 p[0] = ‘X’;
// 数组… char a[] = "hello"; char b[10]; strcpy(b, a); // 不能用 b = a; if(strcmp(b, a) == 0) // 不能用 if (b == a) // 指针… int len = strlen(a); char *p = (char *)malloc(sizeof(char)*(len+1)); strcpy(p,a); // 不要用 p = a; if(strcmp(p, a) == 0) // 不要用 if (p == a)
char a[] = "hello world"; char *p = a; cout<< sizeof(a) << endl; // 12字节 cout<< sizeof(p) << endl; // 4字节
注意当数组做为函数的参数进行传递时,该数组自动退化为同类型的指针数组
void Func(char a[100]) { cout<< sizeof(a) << endl; // 4字节而不是100字节 }
void GetMemory(char *p, int num) { p = (char *)malloc(sizeof(char) * num); } void Test(void) { char *str = NULL; GetMemory(str, 100); // str 仍然为 NULL strcpy(str, "hello"); // 运行错误 }
编译器老是要为函数的每一个参数制做临时副本,指针参数p的副本是 _p,编译器使 _p = p。若是函数体内的程序修改了_p的内容,就致使参数p的内容做相应的修改。这就是指针能够用做输出参数的缘由。函数
在上面的例子中,_p申请了新的内存,只是把_p所指的内存地址改变了,可是p丝毫未变。因此函数GetMemory并不能输出任何东西。事实上,每执行一次GetMemory就会泄露一块内存,由于没有用free释放内存。spa
void GetMemory2(char **p, int num) { *p = (char *)malloc(sizeof(char) * num); } void Test2(void) { char *str = NULL; GetMemory2(&str, 100); // 注意参数是 &str,而不是str strcpy(str, "hello"); cout<< str << endl; free(str); }
char *GetMemory3(int num) { char *p = (char *)malloc(sizeof(char) * num); return p; } void Test3(void) { char *str = NULL; str = GetMemory3(100); strcpy(str, "hello"); cout<< str << endl; free(str); }
注:(1)在上面的例子中,要特别注意在函数调用完后用free释放malloc的内存;
(2)不要在函数体内返回栈内存的指针
“野指针”不是NULL指针,是指向“垃圾”内存的指针。指针
人们通常不会错用NULL指针,由于用if语句很容易判断。可是“野指针”是很危险的,if语句对它不起做用。 code
“野指针”的成因主要有三种:对象
(1)指针变量没有被初始化。任何指针变量刚被建立时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。blog
(2)指针p被free或者delete以后,没有置为NULL,让人误觉得p是个合法的指针。内存
(3)指针操做超越了变量的做用域范围。作用域
class A { public: void Func(void){ cout << “Func of class A” << endl; } }; void Test(void) { A *p; { A a; p = &a; // 注意 a 的生命期 } p->Func(); // p是“野指针” }
malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们均可用于申请动态内存和释放内存。
对于非内部数据类型的对象而言,光用maloc/free没法知足动态对象的要求。对象在建立的同时要自动执行构造函数,对象在消亡以前要自动执行析构函数。因为malloc/free是库函数而不是运算符,不在编译器控制权限以内,不可以把执行构造函数和析构函数的任务强加于malloc/free。
所以C++语言须要一个能完成动态内存分配和初始化工做的运算符new,以及一个能完成清理与释放内存工做的运算符delete。注意new/delete不是库函数。
若是在申请动态内存时找不到足够大的内存块,malloc和new将返回NULL指针,宣告内存申请失败。一般有三种方式处理“内存耗尽”问题。
(1)判断指针是否为NULL,若是是则立刻用return语句终止本函数。
(2)判断指针是否为NULL,若是是则立刻用exit(1)终止整个程序的运行。
(3)用_set_new_hander函数为new设置用户本身定义的异常处理函数。