今天在调试一个程序,发现返回的数值有问题。
程序结构如下所述:
为了防止对内存的反复分配和销毁带来的效率问题,在程序中自己写了个内存管理程序,其目的是调用它的接口分配内存,调用它的接口销毁内存。实际上所谓的销毁并不是真正地销毁,而是把内存放到了一个没有使用的列表中。这个里面有两个列表:
使用的内存列表,采用map管理,key为内存的地址,值为自定义的一个结构:
struct Smm
{
char *buf;
int len;
};
map<int, Smm> mapUsed;
空闲的内存列表,采用vector管理:
vector<Smm> vecFree;
由于使用中的问题发生在分配内存的函数中,所以本文只说明关于分配接口的逻辑流程。
分配内存的接口非常简单:
char *CreateMm(int len);
内部的实现逻辑如下:
char *CreateMm(int len)
{
Smm *pmm = NULL;
int index = 0;
if ( (index = search_in_free(len)) >= 0 )
{
pmm = &vecFree[index];
mapUsed[pmm->buf] = *pmm; // 重新加入到正在使用的列表中
vecFree.erase(vecFree.begin()+index);// 从空闲列表中删除
return pmm->buf;
}
// 空闲列表中没有足够的内存,所以重新创建
Smm mm;
mm.buf = new char[len];
mm.len = len;
mapUsed[(int)mm.buf] = mm;
return mm.buf;
}
刚开始两圈没有问题,跑了两圈,就固定地出了问题,上层对一个内存的内容改变时,另外一个也随着改变了,一跟踪查看,发现两个内存的地址一样!而这两个内存地址正是通过函数CreateMm()返回的。
后跟踪进来查看,很容易就发现了问题所在。在上面的函数中的if语句中,使用&vecFree[index]返回了索引index节点的指针给pmm,然后加入到map中也没有问题,当vecFree.erase()一行一过,则pmm就变了,发现buf的地址不是通过&vecFree[index]取出来时的地址了,而是其后面的一条记录的地址了。所以,第一次调用CreateMm()时返回的是vecFree中的第3条记录的buf,本应该是第二条,但在erase()之后,就变成了第3条。接着再一次调用CreateMm()时,就把第二条记录返回了出来,而这条记录和前一次调用时的记录实际上是同一个,所以两次调用得到的内存地址是一样的。
注意第二次,因为这个空闲列表vecFree中只有3条记录,所以第二次调用时,erase并没有改变pmm原本取出来的内容。
知道了原来,对这个函数稍做修改即可:
char *CreateMm(int len)
{
Smm *pmm = NULL;
char *pret = NULL; // 添加这一行
int index = 0;
if ( (index = search_in_free(len)) >= 0 )
{
pmm = &vecFree[index];
pret = pmm->buf; // 添加这一行
mapUsed[pmm->buf] = *pmm; // 重新加入到正在使用的列表中
vecFree.erase(vecFree.begin()+index);// 从空闲列表中删除
return pret; // 添加这一行
}
// 空闲列表中没有足够的内存,所以重新创建
Smm mm;
mm.buf = new char[len];
mm.len = len;
mapUsed[(int)mm.buf] = mm;
return mm.buf;
}
如上,只添加了3行,直接记录当时的地址就行了。
以上的函数只是个大致流程,在实际代码中还需要进行其它的一些处理,比如分配的内存清空等操作,此处只做为流程上的演示。