我面临着非常惊人的事情。 (场景:Win7 pro 64位,VC2008编译32位代码)
假设一个主程序实例化一个使用std :: map的类 它是一个std :: map< std :: string,Props>类Props只有两个成员:
class Props {
public:
/**
* value of property
* @var boost::any
*/
boost::any _a ;
/**
* expire time
* @var time_t
*/
time_t _tExpiry ;
/* Somethign more I'm not writing down here... */
}
现在......我构建了一个DLL,为自己的业务使用相同的类 DLL实例化该类并提供std :: map。
嗯......当主程序提供地图时,everythig正常,而DLL在第一个项目插入后崩溃。
更多东西(非常有趣)
如果我深入到主程序完成的插入,我将单个_Node的构造函数到达std :: map
_Node如下所示(c:\ Programmi \ Microsoft Visual Studio 9.0 \ VC \ include \ xtree)
struct _Node
{ // tree node
_Node(_Nodeptr _Larg, _Nodeptr _Parg, _Nodeptr _Rarg,
const value_type& _Val, char _Carg)
: _Left(_Larg), _Parent(_Parg), _Right(_Rarg),
_Myval(_Val), _Color(_Carg), _Isnil(false)
{ // construct a node with value
}
_Nodeptr _Left; // left subtree, or smallest element if head
_Nodeptr _Parent; // parent, or root of tree if head
_Nodeptr _Right; // right subtree, or largest element if head
value_type _Myval; // the stored value, unused if head
char _Color; // _Red or _Black, _Black if head
char _Isnil; // true only if head (also nil) node
};
很好......所以我们的_Node结构有_Left(4bytes),_ Parent(4字节),_ Right(4字节)_Myval(sizeof键和map的值),_ Color(1byte)和_Isnil(1字节) 。
现在......我想要为地图提供的元素是一对< std :: string,Props>。
根据我的调试器,std :: string需要0x20字节,而Props只需要8。
现在,当我向调试器询问单个节点的大小时,我可以看到它是0x38。
所以... 0x4 + 0x4 + 0x4 + 0x20 + 0x8 + 0x1 + 0x1 = 0x36 +填充= 0x38。
这意味着_Color成员在_Node开始后开始0x34字节。
(好的......我宁愿指定我的所有项目都使用/ Zp4所以所有结构都是4个字节打包)。
让我们继续......当我遵循DLL的行为时,我可以做一些非常了不起的事情 std :: map的_Buynode()方法调用分配器,使其具有我要插入的新元素的新大小。调用分配器而不是调用_Node()就地构造函数....它以另一种方式行事!!
在这种情况下,构造函数的行为为_Color成员从_Node开头的0x38字节后开始...好像有不同的填充。
在此之后,在下面的tryal中插入一个新值,该过程失败,因为_Color和_Isnil的值是......错误的(0xcc,因为该部分内存未初始化)。
我确定我将所有项目中的/ Zp4设置为解决方案所以...
怎么了?
我“觉得”有关于aligment的错误但我不能说是什么......
提前致谢!
好的......我要补充一点。
这是来自c:\ Programmi \ Microsoft Visual Studio 9.0 \ VC \ include \ xtree
struct _Node
{ // tree node
_Node(_Nodeptr _Larg, _Nodeptr _Parg, _Nodeptr _Rarg,
const value_type& _Val, char _Carg)
: _Left(_Larg), _Parent(_Parg), _Right(_Rarg),
_Myval(_Val), _Color(_Carg), _Isnil(false)
{ // construct a node with value
}
_Nodeptr _Left; // left subtree, or smallest element if head
_Nodeptr _Parent; // parent, or root of tree if head
_Nodeptr _Right; // right subtree, or largest element if head
value_type _Myval; // the stored value, unused if head
char _Color; // _Red or _Black, _Black if head
char _Isnil; // true only if head (also nil) node
};
_Tree_nod(const key_compare& _Parg,
allocator_type _Al)
: _Traits(_Parg, _Al), _Alnod(_Al)
{ // construct traits from _Parg and allocator from _Al
}
typename allocator_type::template rebind<_Node>::other
_Alnod; // allocator object for nodes
};
正如您所看到的,它的构造函数非常简单
看看我之前所说的std :: map asf时从这个结构中使用的内存
- _Left的4个字节
- _Parent的4个字节
- _Right的4个字节
- _Myval为0x28字节(std :: string为0x20,我自己的类为8。我检查过,它是8字节)
- _Color的1个字节
- _Innil
当一个新的元素placetd在树的顶部(我的意思是,最左边)被创建时,这个costructor填充_Left,_Parent,_Right,_Myval,而不是未初始化的4个字节(比如用0xcc填充)并填充它的想法应该是_Color和_Isnil提前4个字节 最荒谬的是std :: map的其他方法没有“感觉”那4个字节 这些是引起断言的线。
if (_Isnil(_Ptr))
{
_Ptr = _Right(_Ptr); // end() ==> rightmost
if (_Isnil(_Ptr))
#if _HAS_ITERATOR_DEBUGGING
{
_DEBUG_ERROR("map/set iterator not decrementable");
_SCL_SECURE_OUT_OF_RANGE;
}
#elif _SECURE_SCL
{
_SCL_SECURE_OUT_OF_RANGE;
}
#else
return; // begin() shouldn't be incremented, don't move
#endif
}
这是因为_IsNil(_Ptr)的测试,发现_Innil是0xcc,给出了错误。发布版本可能不坚持这不是一种快乐。
有什么想法吗?
另一步!
我采用了整个解决方案(2个大项目,18个小项目,大约250000个C / C ++代码行),并在Linux下编译(gcc4.1.2)。
一切都很好。没问题,std :: map正常工作。
我不得不说Linux Makefile同时使一切变得更加简单和复杂。很复杂,因为你必须自己做所有事情,因为如果你不想做就没有任何事情。
这告诉我一件事:Visual Studio 2008中存在一些错误,在某些特定情况下会跳到舞台上......问题是“造成这种情况的条件是什么?”。
等待一个想法......
答案 0 :(得分:0)
我只是在猜测,但我的直接猜测是你的问题并非源于对齐问题。
使用VC ++,如果你在DLL中放置这样的东西,你必须编译代码(主程序和DLL)以使用DLL中的标准库。执行此操作时,主程序和DLL共享一个公共堆,因此它们可以分配/释放内存,并保持同步。
如果您静态链接到标准库,您的主程序和DLL将具有单独的堆。当/如果一个代码中的代码试图删除在另一个中分配的项目时(例如),您将遇到重大问题,因为它会尝试将其返回到它不是来自的堆中第一名。
答案 1 :(得分:0)
编译器选项为/Zp4。你确定在所有情况下都使用了正确的选项吗?
答案 2 :(得分:0)
您的描述肯定指向对齐/包装问题的方向。
也许您可以在sizeof(Props) == 8
上添加编译时检查。 Boost有一个编译时检查模板,但在互联网上找到一个或者自己动手并不难。
如果sizeof总是相同,那么让我指向checked iterators以寻找替代方案。
之后,我的想法已经不多了。