最近看过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如何使用附加指针来检查迭代器使用是否正确,仍然会很有趣。
答案 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
,_Alty
为std::allocator<char>
,_Val_types
为_Simple_types<char>
,因为std::is_empty<std::allocator<char>>::value
为true
,sizeof _Compressed_pair<_Alty, _String_val<_Val_types> >
与sizeof _String_val<_Val_types>
相同
类_String_val
继承自_Container_base
,typedef
_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_type
,size_type
在此系统中完全一致。不需要对齐。
因此,当_ITERATOR_DEBUG_LEVEL == 0时,sizeof std::string
为:
_BUF_SIZE + 2 * sizeof size_type
否则就是
sizeof pointer_type + _BUF_SIZE + 2 * sizeof size_type