为什么Microsoft的std :: string实现需要40个字节?

时间:2016-11-03 03:24:33

标签: c++ string

最近看过this video关于facebook的字符串实现,我很想看到微软实施的内部情况。不幸的是,字符串文件(在%VisualStudioDirectory%/VC/include中)似乎并不包含实际定义,而只包含转换函数(例如atoi)和一些运算符重载。

我决定从用户级程序中做一些戳戳和刺激。当然,我做的第一件事就是测试sizeof(std::string)。令我惊讶的是,std :: string需要40个字节! (无论如何,在64位机器上。)前面提到的视频详细介绍了facebook的实现如何只需要24个字节而gcc需要32个字节,所以至少可以说这是令人震惊的。

我们可以通过编写一个简单的程序来更深入地挖掘数据的内容(包括c_str地址),如下所示:

#include <iostream>
#include <string>
int main()
{
    std::string test = "this is a very, very, very long string";

    // Print contents of std::string test.
    char* data = reinterpret_cast<char*>(&test);
    for (size_t wordNum = 0; wordNum < sizeof(std::string); wordNum = wordNum + sizeof(uint64_t))
    {
        for (size_t i = 0; i < sizeof(uint64_t); i++)
            std::cout << (int)(data[wordNum + i]) << " ";

        std::cout << std::endl;
    }

    // Print the value of the address returned by test.c_str().
    // (Doing this byte-by-byte to match the above values).
    const char* testAddr = test.c_str();
    char* dataAddr = reinterpret_cast<char*>(&testAddr);

    std::cout << "c_str address: ";
    for (size_t i = 0; i < sizeof(const char*); i++)
        std::cout << (int)(dataAddr[i]) << " ";

    std::cout << std::endl;
}

打印出来:

48 33 -99 -47 -55 1 0 0
16 78 -100 -47 -55 1 0 0
-52 -52 -52 -52 -52 -52 -52 -52
38 0 0 0 0 0 0 0
47 0 0 0 0 0 0 0
c_str address: 16 78 -100 -47 -55 1 0 0

检查这一点,我们可以看到第二个单词包含指向字符串分配数据的地址,第三个单词是garbage(短字符串优化的缓冲区),第四个单词是大小,第五个单词是字是容量。 但是第一个字怎么样?它似乎是一个地址,但是为了什么?难道不应该考虑所有事情吗?

为了完整起见,以下输出显示SSO(字符串设置为&#34;短字符串&#34;)。请注意,第一个单词似乎仍然代表一个指针:

0 36 -28 19 123 1 0 0
83 104 111 114 116 32 83 116
114 105 110 103 0 -52 -52 -52
12 0 0 0 0 0 0 0
15 0 0 0 0 0 0 0
c_str address: 112 -9 79 -108 23 0 0 0
编辑:好的,所以做了更多的测试,看起来std :: string的大小在编译发布时实际上减少到32个字节,并且第一个单词不再存在。但我仍然真的很想知道为什么会这样,以及在调试模式下使用的额外指针。

更新:根据用户Yuushi的提示,额外的单词似乎与Debug Iterator支持有关。这在我关闭Debug Iterator支持时得到了验证(这样做的示例显示为here)并且std :: string的大小减少到32个字节,现在缺少第一个单词。

但是,看看Debug Iterator Support如何使用附加指针来检查迭代器使用是否正确,仍然会很有趣。

1 个答案:

答案 0 :(得分:2)

Visual Studio 2015使用xstring代替string来定义std::basic_string

注意:此答案仅适用于VS2015,VS2013使用不同的实现,但它们或多或少相同。

它实现为:

template<class _Elem,
class _Traits,
class _Alloc>
class basic_string
    : public _String_alloc<_String_base_types<_Elem, _Alloc> >
{
// This class has no member data
}

_String_alloc使用_Compressed_pair<_Alty, _String_val<_Val_types> >存储其数据,std::string_Altystd::allocator<char>_Val_types_Simple_types<char> ,因为std::is_empty<std::allocator<char>>::valuetruesizeof _Compressed_pair<_Alty, _String_val<_Val_types> >sizeof _String_val<_Val_types>相同

_String_val继承自_Container_basetypedef _Container_base0#if _ITERATOR_DEBUG_LEVEL == 0_Container_base12。它们之间的区别是_Container_base12包含指向_Container_proxy的指针以用于调试目的。除此之外_String_val也有这些成员:

union _Bxty
    {   // storage for small buffer or pointer to larger one
    _Bxty()
        {   // user-provided, for fancy pointers
        }

    ~_Bxty() _NOEXCEPT
        {   // user-provided, for fancy pointers
        }

    value_type _Buf[_BUF_SIZE];
    pointer _Ptr;
    char _Alias[_BUF_SIZE]; // to permit aliasing
    } _Bx;

size_type _Mysize;  // current length of string
size_type _Myres;   // current storage reserved for string

_BUF_SIZE为16。

pointer_typesize_type在此系统中完全一致。不需要对齐。

因此,当_ITERATOR_DEBUG_LEVEL == 0时,sizeof std::string为:

_BUF_SIZE + 2 * sizeof size_type

否则就是

sizeof pointer_type +  _BUF_SIZE + 2 * sizeof size_type