Deque陷阱

本文探讨了使用MSVC6编译器时遇到的一个关于Deque的潜在问题。当结构体大小超过特定阈值并进行erase操作时,程序可能出现崩溃。文章深入分析了问题原因,并给出了注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 
Deque陷阱
 
程序开发过程中deque的使用频率非常高,一个是它比较方便,同时效率上也非常的高。从来也没有怀疑MS VC6自带的deque会有问题。不过今天确确实实让他害了一把。
下面是我的写的简单测试程序:
#include "stdafx.h"
#include <deque>
 
struct TestStruct
{
       char test[1366]; //如果这个SIZE >= 1366就会crash
};
int APIENTRY WinMain(HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
      // TODO: Place code here.
       std::deque<TestStruct> test;
       TestStruct a;
       test.push_back(a);
       test.erase(test.begin());
       return 0;
}
从代码上来看没有任何的问题,可是程序执行到erase的时候就会出现crash。
从crash的位置来看是拷贝内存数据时候的内存地址乱了,可是自己只加了一个TestStruct,怎么也会出现内存乱了的情况。
通过对DEQUE代码的跟踪,发现MS VC6自带的DEQUE还是有一点小小的问题(还是特意这样处理的)。
当我把a压到test的时候,事实上DEQUE一次alloc了两个TestStruct,同时生成一个长度为2的MAP来记录new出来的TestStruct。
       void _Buyback()
              {_Tptr _P = allocator.allocate(_DEQUESIZ, (void *)0);
              if (empty())
                     {_Mapsize = _DEQUEMAPSIZ;
                     size_type _N = _Mapsize / 2;
                     _Getmap();
                     _Setptr(_Map + _N, _P);
                     _First = iterator(_P + _DEQUESIZ / 2, _Map + _N);
                     _Last = _First; }
所以push_back应该是没有问题。返回的_First的数据都是记录在_Map + 1的位置上了。
那我们再来看一下erase执行的代码。
       iterator erase(iterator _P)
              {return (erase(_P, _P + 1)); }
       iterator erase(iterator _F, iterator _L)
              {size_type _N = _L - _F;
              size_type _M = _F - begin();
              if (_M < end() - _L)
                     {copy_backward(begin(), _F, _L);
                     for (; 0 < _N; --_N)
                            pop_front(); }
              else
                     {copy(_L, end(), _F);
                     for (; 0 < _N; --_N)
                            pop_back(); }
              return (_M == 0 ? begin() : begin() + _M); }
其实erase的实现也是非常的简单,主要是内存和指针的移动和删除的数据的清理。
问题的关键是出现在_P + 1上了。下面是P +1的真正实现函数:
              void _Add(difference_type _N)
                     {difference_type _Off = _N + _Next - _First;
                     difference_type _Moff = (0 <= _Off)
                            ? _Off / _DEQUESIZ
                            : -((_DEQUESIZ - 1 - _Off) / _DEQUESIZ);
                     if (_Moff == 0)
                            _Next += _N;
                     else
                            {_Map += _Moff;
                            _First = *_Map;
                            _Last = _First + _DEQUESIZ;
                            _Next = _First + (_Off - _Moff * _DEQUESIZ); }}
由于只加了一个TestStruct,所以_Next - _First为1,那么_Off =2,计算出来的_Moff为1,
由于只加了一个元素,我们只有一个_Map,那么我们执行_Map += _Moff,就导致取得的_First,_Last,_Next都指向了非法地址,最终导致程序Crash。
 
所以通过上面的分析,如果一个结构的大小超过1365的时候就要注意了,因为只加一个元素的时候就调用了erase函数会导致程序crash。其实大部分的情况下这种结构的大小已经有点偏大了,可能有VECTOR应该会比DEQUE取得不错的性能。
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值