在更安全使用malloc()、calloc()、realloc()等内存分配函数的议题中,有许多事可以做,下面的函数模板处理了一函数getmem(),这个函数既可以分配新
的内存空间,或者调整已分配内存空间的大小,它把新空间全部置0, 并检查操作是否成功。这样,只需要告诉它需要多少空间就行了,这样做可以减少程序
出错的可能性。函数代码如下:
template<class T>
void getmem(T* &oldmem, int elems)
{
typedef int cntr;
const int csz = sizeof(cntr);
const int tsz = sizeof(T);
if (0 == elems)
{
free(&(((cntr *)oldmem)[-1]));
return;
}
T *p = oldmem;
cntr oldcount = 0;
if (p)
{
cntr *tmp = reinterpret_cast<cntr *>(p);
p = reinterpret_cast<T *>(--tmp);
oldcount = *(cntr *)p;
}
T *m = (T *)realloc(p, elems * tsz + csz);
if (0 == m)
{
cout << "ERROR!" << endl;
exit(1);
}
*((cntr *)m) = elems;
const cntr increment = elems - oldcount;
if (increment > 0)
{
long startadr = (long)&(m[oldcount]);
startadr += csz;
memset((void *)startadr, 0, increment * tsz);
}
oldmem = (T *)&(((cntr *)m)[1]);
}
template<class T>
inline void freemem(T *m) { getmem(m, 0); }
为了能够清空新的内存空间,程序分配了一个计数器来记录有多少内存块被分配了,typedef cntr就是这个计数器的类型。
有一个指针的引用(oldmem)非常关键,因为在分配新内存空间的时候,原来的内存头指针就改变了,他可以帮我们找回头指针。
如果elems参数为0,则这块内存就被释放掉,这是附加功能freemem()所借用的。getmem()的操作时相当底层的,这里有许多
类型和字节的操作,例如,指针oldmem并没有指向内存的开始空间,它把内存的起始空间让给计数器使用,所以,当我们要释放
这块内存时,getmem()必须倒退这个指针cntr所占用的字节数,因为oldmem()是一个T*,它必须首先被转换成cntr*,然后索引倒
退一个位置,最后在该地址执行free(): free(&(((cntr *)oldmem)[-1]));
类似的,如果预先分配过内存,getmem()也必须先拿到目前内存的分配情况,然后再重新计算调用realloc()的方法。如果
尺寸增加了,为了清空新的地址空间,就必须算出使用memset()的起始地址,最后,oldmem的地址依然是越过计数器的地址空间。
old = (T *)&(((cntr *)m)[1]);
oldmem是一个对指针的引用,它可以改变外界传进来的任何参数。
以下是使用getmem函数的实例:
int main(int argc, char *argv[])
{
int *p = 0;
getmem(p, 10);
for (int i = 0; i < 10; ++i)
{
cout << p[i] << ' ';
p[i] = i;
}
cout << endl;
getmem(p, 20);
for (int j = 0; j < 20; ++j)
{
cout << p[j] << ' ';
p[j] = j;
}
cout << endl;
getmem(p, 25);
for (int k = 0; k < 25; ++k)
{
cout << p[k] << ' ';
}
freemem(p);
cout << endl;
float *f = 0;
getmem(f, 3);
for (int u = 0; u < 3; ++u)
{
cout << f[u] << ' ';
f[u] = u + 3.14159;
}
cout << endl;
getmem(f, 6);
for (int v = 0; v < 6; ++v)
{
cout << f[v] << ' ';
}
freemem(f);
cout << endl;
return 0;
}
运行结果为:
0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 0 0 0 0 0
0 0 0
3.14159 4.14159 5.14159 0 0 0