首先看一下下面的C程序片断:
#include <malloc.h>
char *p;
p = (char * ) malloc (10);
p = (char * ) realloc (p,20);
…………………………
这段程序的意思很简单,只有稍有点C基础的人都可以看懂。函数首先定义了一个字符型的指针p,然后为指针p分配了一个10个字节大小的内存空间,接着将这个内存块的大小增加到20个字节。
这里有什么问题吗?上机运行一下,好像没有问题!
是的,这样上机运行是没有问题的,但是这里存在着也许我们不太注意的隐患!隐患在那里?这就是我在本文中要详细说明的realloc()函数了。
再看一下下面一段来自MSDN的话:
realloc returns a void pointer to the reallocated (and possibly moved) memory block. The return value is NULL if the size is zero and the buffer argument is not NULL, or if there is not enough available memory to expand the block to the given size. In the first case, the original block is freed. In the second, the original block is unchanged. The return value points to a storage space that is guaranteed to be suitably aligned for storage of any type of object. To get a pointer to a type other than void, use a type cast on the return value.
这段E文还不算是晦涩难懂,所以我就不翻译了,大致的意思是说关于realloc返回值的。但是这里对他的返回值分了几种情况:
1、 返回void * 指针,调用成功。
2、 返回NULL,当需要扩展的大小(第二个参数)为0并且第一个参数不为NULL,此时原内存变成了“freed(游离)”的了。
3、 返回NULL,当没有足够的空间可供扩展的时候,此时,原内存空间的大小维持不变。
第一种情况告诉了我们在得到需要的内存空间后需要做类型转换的工作;
第二种情况可能只有傻瓜才会去使用吧!
第三种情况,内存空间不够的时候就会维持未来的大小不变。
MSDN上面说内存空间不够的时候就不会扩展原来的内存空间的大小,这话固然没有错,但是有点含糊,似乎遗漏了一种情况!我们知道,realloc是从堆上分配内存的,当扩大一块内存空间时, realloc()试图直接从堆上现存的数据后面的那些字节中获得附加的字节,如果能够满足,自然天下太平;可如果数据后面的字节不够的话,问题就出来了,那么就使用堆上第一个有足够大小的自由块,现存的数据然后就被拷贝至新的位置,而老块则放回到堆上。这句话传递的一个重要的信息就是数据可能被移动!看到这里,也许我们已经发现一开始我给出的程序的问题了。为了更清楚地说明问题,可以将上面的程序改成下面的形式:
#include <malloc.h>
char *p,*q;
p = (char * ) malloc (10);
q=p;
p = (char * ) realloc (p,20);
…………………………
这段程序也许在编译器中没有办法通过,因为编译器可能会为我们消除一些隐患!在这里我们只是增加了一个记录原来内存地址的指针q,然后记录了原来的内存地址p,如果不幸的话,数据发生了移动,那么所记录的原来的内存地址q所指向的内存空间实际上已经放回到堆上了!这样一来,我们应该终于意识到问题的所在和可怕了吧!
这个问题似乎有点牛角尖的味道,因为我们也许从来不曾遇上过,但是我们应该明白这样的事情的始终存在,只有这样,在万一我们碰上的时候才会去有意识的去避免这种隐患,否则,一旦这样的隐患一旦发作,程序崩溃不说,恐怕查错也不是一件容易的事!
候俊杰在《深入浅出MFC》中引用林语堂的《朱门》中的一句话,我很有感触,虽然不可能有他的感触深,但是抱着向前辈学习的心态,所以也拿来作为本为的结束:
“只用一样东西,不明白他的道理,实在不高明”。