在C语言中,指针的重要性不言而喻,但在很多时候指针又被认为是一把双刃剑。一方面,指针是构建数据结构和操作内存的精确而高效的工具。另一方面,它们又很容易误用,从而产生不可预知的软件bug。下面总结一下指针使用的常见错误。
一、使用未初始化的指针
这个错误很常见,指针未初始化时,系统会给指针分配个随机地址,示例如下:
int *p; //或者 int *p = NULL;
···
*p = 10; //错误,指针未初始化
上述程序将值10写到未知的内存位置,如果p指向系统内存空间,这样很可能把系统本来地址里的内容给覆盖了,会导致程序或者系统的崩溃。
二、没有释放内存
在堆中开辟内存以后,使用完成必须释放内存,否则会造成内存泄漏,示例如下:
int *p = (int *)malloc(100);
···
free(p);
p = NULL;
三、不断修改内存指针变量
很多时候使用指针开辟了内存空间,然后如果对指针指向进行改变操作,操作完成后直接释放内存,会释放了不该释放的位置;另外程序丢失了对已开辟内存空间的控制,造成内存泄漏,示例如下:
//这种时候一般会定义两个指向同一个开辟的内存空间的指针变量,一个用于操作,一个用于释放,避免造成内存泄漏
char *p = (char *)malloc(100);
strcpy(p, "abcdefg");
p += 1; //对指针指向进行改变操作
*p = \'a\';
free(p);
p = NULL;
四、注意"野指针"
"野指针"不是NULL指针,是指向“垃圾”内存的指针。人们一般不会错用NULL指针,因为用if语句很容易判断。但是“野指针”是很危险的,if语句对它不起作用。“野指针”的成因主要有两种:
第一种,指针变量没有初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。示例如下:
char *p; //错误,未初始化,成为野指针
第二种,指针变量被free或者delete之后,没有置为NULL,让人误以为是个合法的指针。示例如下:
char *p = (char *)malloc(100);
...
free(p); //错误,没有置为NULL,成为野指针
五、指针参数申请内存的常见错误
如果函数的参数是一个指针,不要指望用该指针去申请动态内存,示例如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void getMemory(char *p, int num)
{
p = (char*)malloc(sizeof(char) * num);
}
int main()
{
char *pStr = NULL;
pStr = getMemory(pStr, 200);
strcpy(pStr, "hello world!"); //错误,pStr仍然是空指针
printf("%s", pStr);
free(pStr);
pStr = NULL;
return 0;
}
上面getMemory(str, 200)
并没有使pStr获得期望的内存,pStr依旧是NULL,为什么?
问题出在getMemory
函数中。编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是_p,编译器使_p = p。在本例中,_p申请了新的内存,只是把_p所指的内存地址改变了,但是p丝毫未变,仍然是空指针。事实上,每执行一次getMemory
函数就会泄漏一块内存,因为没有用free释放内存。
指针参数申请内存的两种正确用法
第一种用法:使用函数返回值来传递动态内存,示例如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char* getMemory(char *p, int num)
{
p = (char *)malloc(sizeof(char) * num);
return p;
}
int main()
{
char *pStr = NULL;
pStr = getMemory(pStr, 200);
strcpy(pStr, "hello world!");
printf("%s", pStr);
free(pStr);
pStr = NULL;
return 0;
}
这里强调不要用return语句返回指向”栈内存“的指针,因为”栈内存“指针在函数结束时已经被自动回收。
第二种用法:使用指向指针的指针作为参数来传递动态内存,示例如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void getMemory(char **p, int num)
{
*p = (char *)malloc(sizeof(char) * num);
}
int main()
{
char *pStr = NULL;
getMemory(&pStr, 200);
strcpy(pStr, "hello world!");
printf("%s", pStr);
free(pStr);
pStr = NULL;
return 0;
}
在需要修改指针变量本身的时候,需要使用指向指针的指针作为参数,这也是传值与传地址的差别所在。
参考: