最近遇到了一个奇妙的问题,在一个抽奖活动里抽奖偶尔会出现发给客户端的数据不正确的情况,结果初步排查,发现这是由于有一个指针指向的对象里的数据发生了错乱。而且很奇妙的,经常是抽了好几十次才出现一次这种情况 先来看一段代码
const Item* OnePiece::getRandItem(Lib* pLib, const std::set<DWORD>& extraSet)
{
std::vector<Item>& itemListCfg = pLib->itemList;
std::vector<Item> itemList;
DWORD totalWeight = 0;
//初始化奖池
std::vector<Item>::const_iterator iter = itemListCfg.begin();
for (; iter != itemListCfg.end(); ++iter)
{
if (extraSet.count(iter->indexId) == 0)
{
//该奖品加入奖池
itemList.push_back(*iter);
totalWeight += iter->weight;
}
}
//抽随机数
DWORD tValue = 0;
if (totalWeight > 0) {
tValue = Tool::randBetween(0, totalWeight - 1);
}
DWORD maxTotalWeight = 0;
const Item* pItem = NULL;
iter = itemList.begin();
for (; iter != itemList.end(); ++iter)
{
maxTotalWeight += iter->weight;
if (tValue < maxTotalWeight)
{
pItem = &(*iter);
break;
}
}
return pItem;
}
该函数用于抽奖,用一个集合过滤掉不符合要求的奖品生成新的奖池并从这个新生成的奖池抽奖,函数最后返回一个指针。 这一段代码乍一看没啥问题,然而却隐藏着巨大的隐患。 原因在于vector执行push_back操作时会进行对象的拷贝。而itemList又是一个在函数里的局部变量,在离开这个函数的作用域之后,这个局部变量便脱离了作用域,而返回的指针又是存在这个vector里面的,这就会出现调用该函数的调用者拿到的指针指向一片非法区域!!! 那么为什么这种问题只是偶现,原因在于在离开了函数的作用域时,操作系统并不一定会马上会覆盖这片内存,所以大部分情况下这片内存指向的数据还是正确,但当操作系统将这块内存分配给其他变量后,那么这片内存可能就被修改了,这时如果我们再拿着原来的指向那块地址的指针去取数据,就可能会拿到一个奇怪的数据! 可以将上述代码修改如下:
const Item* OnePiece::getRandItem(Lib* pLib, const std::set<DWORD>& extraSet)
{
std::vector<Item>& itemListCfg = pLib->itemList;
std::vector<Item*> itemList;
DWORD totalWeight = 0;
//初始化奖池
std::vector<Item>::iterator iter = itemListCfg.begin();
for (; iter != itemListCfg.end(); ++iter)
{
if (extraSet.count(iter->indexId) == 0)
{
//该奖品加入奖池
itemList.push_back(&(*iter));
totalWeight += iter->weight;
}
}
//抽随机数
DWORD tValue = 0;
if (totalWeight > 0) {
tValue = Tool::randBetween(0, totalWeight - 1);
}
DWORD maxTotalWeight = 0;
const Item* pItem = NULL;
std::vector<Item*>::const_iterator it= itemList.begin();
for (; it!= itemList.end(); ++it)
{
maxTotalWeight += *it->weight;
if (tValue < maxTotalWeight)
{
pItem = *it;
break;
}
}
return pItem;
}
|