0CCh Blog

记VC6中STL的map的一处BUG

今天和同事一起调了一个vc6.0中stl的map的一个bug。

BUG的起因是,我们的项目中使用了stl的map类。而这个map类的对象被用在了不同的DLL模块中,在这样的条件写,BUG就产生了,一个模块内部map对象指针能正常工作,另一个模块内部就出了问题。起初我们就觉得很奇怪,很简单一份代码,怎么会出现访问无效内存的情况,我们还是通常的思路,先在自己身上找问题。调了一会发现,自己的代码确实没有错误。于是我们把目光转向了vc6的stl本身。

跟踪了一下stl的代码,发现错误发生在下面这段代码内部。

20130419140128

在正常的模块中while (_x != _Nil)这个循环只经历了一次,就跳出循环了。而发生错误模块中第二次进入这个循环,也就在这次的循环中,出现了内存访问异常的情况。很明显就要看两次_x != _Nil比较的详细结果。刚开始我被误导了,以为_Nil就是一个为0的常量,把注意力留在_X上后来发现,正确和错误的模块中,_X值一直都是相同的。这才缓过神来,_Nil这个值有问题。

确实,这个不是一个0,更不是一个常量,他是一个静态指针变量。在map对象被创建的时候,生成了一个填充为0的结构体,并且把结构体指针存到了这个变量中。

真相大白了,由于在不同的模块中都是用了stl的map代码,这样map的代码就被编译了两份,同样每个模块中map的_Nil也存放在各自的模块地址范围内。这样就使得_Nil值是不相同的,如果在非创建这个map对象的模块中引用对象指针,并且调用map的函数。如果遇到了_Nil,就会引用此模块自己的_Nil,而不是创建对象模块的_Nil,如果这个模块没有初始化过map对象,那么这个模块的_Nil就是0,即使初始化过,两个模块的_Nil也没可能是同一个值。

新版stl中这个bug必然已经解决,简单来看看vs2010的stl

20130419142503

循环中检查是否为空,用到了函数_Isnil这个,而这个函数查看了_Nodeptr结构中的_Isnil成员变量。判断空放在成员对象内部,这样在多模块之间调用该对象就不会有任何问题了。

话说,vc6的stl确实是bug一堆,很早之前,人们就喜欢用sgi-stl来代替vc6自带的stl了。