std :: map节点对齐

时间:2011-02-15 16:55:29

标签: c++ stdmap memory-alignment

我面临着非常惊人的事情。 (场景: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

的_Node结构
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

为1个字节

当一个新的元素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中存在一些错误,在某些特定情况下会跳到舞台上......问题是“造成这种情况的条件是什么?”。

等待一个想法......

3 个答案:

答案 0 :(得分:0)

我只是在猜测,但我的直接猜测是你的问题并非源于对齐问题。

使用VC ++,如果你在DLL中放置这样的东西,你必须编译代码(主程序和DLL)以使用DLL中的标准库。执行此操作时,主程序和DLL共享一个公共堆,因此它们可以分配/释放内存,并保持同步。

如果您静态链接到标准库,您的主程序和DLL将具有单独的堆。当/如果一个代码中的代码试图删除在另一个中分配的项目时(例如),您将遇到重大问题,因为它会尝试将其返回到它不是来自的堆中第一名。

答案 1 :(得分:0)

编译器选项为/Zp4。你确定在所有情况下都使用了正确的选项吗?

答案 2 :(得分:0)

您的描述肯定指向对齐/包装问题的方向。

也许您可以在sizeof(Props) == 8上添加编译时检查。 Boost有一个编译时检查模板,但在互联网上找到一个或者自己动手并不难。

如果sizeof总是相同,那么让我指向checked iterators以寻找替代方案。

之后,我的想法已经不多了。