C语言学习手记—释放内存失败?指针赋值有问题!

今天想总结一个C语言初学者刚接触到指针的时候,很容易出现的指针使用误区。
首先,来回顾一下初学指针最常遇见的用法例子之一——让指针指向一个已初始化的变量的地址。
比如:

int number = 10;
int *number_ptr = &number;

常见的剧情发展是:当你刚熟悉这种简单明了的指针用法还没多久,你又遇上了新的“更高级”的指针使用方式:通过动态分配(malloc)和释放(free)内存来使用指针。
在这时,你已经或即将见到的例子,一般是借助指针来动态分配字符串。
比如:

#define SIZE 100 //用宏定义一下size大小为100
...
    // 用malloc分配一段足以容纳100个字符的字符串内存空间
    char *string = (char *)malloc(sizeof(char)*(SIZE+1)); 
    ...
    free(string); // 释放这个内存空间
...

以上的两个例子都是规范的指针用法,此时一切正常。然而,有的初学者有时却会把以上两种用法混淆在一起,问题从此而生。
请看下面这段代码:

    char *string = (char *)malloc(sizeof(char)*(SIZE+1)); 
    string = "Hello, pointer!";
    printf("%s\n", string);
    free(string);

你能否猜到程序的运行结果?
显然,出错了。但似乎又不全错。

./easyPtr
Hello, pointer!
easyPtr(30867,0x7fffd5da53c0) malloc: *** error for object 0x107043f6c: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

你觉得很纳闷:malloc和free的配套使用法则毫无问题,赋值看上去也成功了,因为在报错之前,printf已经成功打印出了 Hello, pointer! 。到底是哪儿出错了呢?
如果你觉得字符串的例子看起来有点乱,那么再看看下面这个核心问题相同的整型指针的例子:

int number = 10;
// 这次我们尝试动态分配一个指向number的指针
int *number_ptr = (int *)malloc(sizeof(int));
number_ptr = &number;
printf("number: %d\n", *number_ptr);
free(number_ptr);

这回的运行结果会如何?

./easyIntPtr 
number: 10
easyIntPtr(30878,0x7fffd5da53c0) malloc: *** error for object 0x7fff59edb89c: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

有没有一种熟悉的郁闷感?
同样是看似成功的赋值,却紧接着发生了无法用指针释放内存的错误。
让我们来研究下错误信息,我们看到是malloc出了问题。
一看更纳闷了:系统居然说,想要释放的对象并没有被动态分配过!
其实,以上两个例子的问题都是对动态分配内存后的指针进行赋值的时候“操作不当”引起的。
也就是以下两行的问题:

string = "Hello, pointer!";
number_ptr = &number;

先说结论,无法用free通过指针释放内存,是因为指针所指向的内存地址早已经被你轻率的赋值给“调包”了。
malloc分配的内存地址位于堆(heap)上,而在以上两个例子中,字符串“Hello, pointer!"以及整型值10都是在栈(stack)上拥有自己的内存地址,
像这样直接用等号进行赋值,

string = "Hello, pointer!";
number_ptr = &number;

其实是将本来指向堆(heap)上地址的指针,转而指向了栈(stack)上的地址。你当然可以直接访问并打印出"Hello, pointer!"和数字10,但是你已经丢失了原本动态分配得到的地址。关键的是,那两个动态分配的内存空间也并没有如你所愿存入字符串和数字10。
所以,当不知情的你仍然对指针进行free的时候,你其实是在尝试free栈(stack)上的内存空间,这当然是行不通的了!不仅如此,你还遗失掉了此前malloc动态分配的内存空间的访问方式,造成了内存泄漏(memory leak)。

总结:
以上错误,归根结底都是对于c语言指针、内存空间、*alloc系列内存分配函数的工作方式理解不够所引起的。
以本文malloc函数为例,malloc函数是分配指定大小的一段连续的内存空间,并将这段内存空间的首地址返回。所以当你用一个指针来承接malloc时,这个指针就指向了这段内存空间的开头。在使用free释放这段空间之前,都千万不能丢失这段内存的(首)地址,不然将造成内存泄漏。
谨记一点:在c语言中,对一个指针变量使用‘=’等号形式的赋值语句,永远都要三思而后行。

你可能感兴趣的:(C语言学习手记—释放内存失败?指针赋值有问题!)