gpt4 book ai didi

c++ - 为什么 Microsoft 的 std::string 实现需要堆栈上的 40 个字节?

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:08:01 26 4
gpt4 key购买 nike

最近看了this video关于 facebook 的 string 实现,我很想看看 Microsoft 实现的内部结构。不幸的是,字符串文件(在 %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

检查这个,我们可以看到第二个字包含指向字符串分配数据的地址,第三个字是垃圾(用于短字符串优化的缓冲区),第四个字是大小,第五个词是容量。 但是第一个词呢?它似乎是一个地址,但有什么用呢?难道不是所有事情都已经考虑清楚了吗?

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

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 个字节,并且第一个单词不再存在。但我仍然非常想知道为什么会这样,以及在 Debug模式下该额外指针的用途。

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

但是,了解 Debug Iterator Support 如何使用该附加指针来检查不正确的迭代器使用仍然非常有趣。

最佳答案

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>>::valuetrue , 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

关于c++ - 为什么 Microsoft 的 std::string 实现需要堆栈上的 40 个字节?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40393350/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com