在更安全使用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