为啥 sizeof(string) == 32?
Posted
技术标签:
【中文标题】为啥 sizeof(string) == 32?【英文标题】:Why is sizeof(string) == 32?为什么 sizeof(string) == 32? 【发布时间】:2010-09-22 15:19:25 【问题描述】:导致 sizeof() 为 32 的字符串结构中的开销是什么?
【问题讨论】:
如果您打开平台的<string>
标头,您可以确切地看到为什么std::string
是那个大小。 @Queso:sizeof
产生对象的大小以字节为单位。
如果 sizeof 返回指针中的位数,那么您的编译器已损坏
@Martin:因为“湿度”几乎被定义为水(或任何液体)的属性。我不知道“32”被定义为字符串的大小。
@Steve Jessop:由于当前的实施,水是湿的(地球作为 STP 允许水变成液体)。在其他实现中,它不是湿的(就像木星一样,它是一种气体)。所以这个字符串实现是 32,因为这是它在这个实现中的构建方式,在其他实现中它将是 16,在另一个实现中是 64。字符串的大小(如水)取决于它所使用的环境。
好的,由于地球的 STP,水是液态的,我们可以通过查看影响它的因素来进一步钻探(例如,大气压力受质量和气体排放的影响)。那么问为什么一组实施者选择32,另一组选择64,与问为什么地球有一定的表面压力和温度有什么关系?一种是众生的选择。另一个不是 IMO,但即使是 IYO,我认为 C++ 实现者也不会像上帝那样声称不可言喻。
【参考方案1】:
大多数现代std::string
实现1 将非常小的字符串直接保存在堆栈中的静态大小的char
数组中,而不是使用动态堆存储。这称为Small (or Short) String Optimisation (SSO)。它允许实现避免小字符串对象的堆分配并提高引用的局部性。
此外,还有一个std::size_t
成员来保存字符串大小和一个指向实际char
存储空间的指针。
具体实现的方式有所不同,但以下几行是可行的:
template <typename T>
struct basic_string
char* begin_;
size_t size_;
union
size_t capacity_;
char sso_buffer[16];
;
;
在sizeof (void*)
= 8 的典型架构上,这给了我们 32 字节的总大小。
1 “三巨头”(GCC 的 libstdc++ 版本 5,Clang 的 libc++ 和 MSVC 的实现)都做到了。其他人也可以。
【讨论】:
@KonradRudolph 非常小的字符串直接保存到对象中,这可以是堆栈也可以是堆,具体取决于字符串本身的分配位置,不是吗? @ManuelSelva 完全正确。 @KonradRudolph 如何强制字符串总是堆分配? (为了让字符串对象小于 32 字节,例如 8 字节。) @LukeFisk-Lennon 你不能。小字符串优化是某些(嗯,所有现代的)标准库实现的实现细节,它不是由语言指定的。因此,您无法在 C++ 中更改它。您也不能在 C++ 之外更改它(例如通过编译器选项),因为这样的更改将是 ABI breaking。也就是说,GCC4 没有执行小字符串优化,因此原则上您可以使用--with-default-libstdcxx-abi=gcc4-compatible
配置您的 GCC,但这是一个糟糕的想法(= 非常古老的实现)。
@KonradRudolph 好的,我明白了。感谢您的快速回复。【参考方案2】:
std::string
通常包含一个用于“小字符串优化”的缓冲区 --- 如果字符串小于缓冲区大小,则不需要堆分配。
【讨论】:
“通常”==“在 Windows 上”;-) Windows 编译器并不是唯一进行小字符串优化的编译器 当然,但是如果您不愿意命名它们,那么很难判断这是否是“典型”行为,或者只是因为这是常见实现的行为(以及大概是其他人)。 据我了解,Dinkumware 和 STLPort 都可以,但 gcc 的实现没有。 顺便说一句,我提到它是因为“通常”的范围从“我有理由相信你永远不会看到其他任何东西”到“我使用的 50% 或更多的实现都可以这”。我认为这很容易被误解。无论是这种优化,还是没有优化,都不应被视为异常。【参考方案3】:我的猜测是:
class vector
char type;
struct Heap
char* start;
char* end;
char* allocatedEnd;
;
struct Stack
char size;
char data[27];
union
Stack stackVersion;
Heap heapVersion;
version;
;
但我敢打赌,有数百种方法可以做到这一点。
【讨论】:
awww...没有引用计数?折叠怎么了? @ErikAronesty 有一个阶段尝试使用std::string
进行引用计数,但很明显他的效率不是很高(多年来有几篇论文),而是短字符串优化变得流行起来。【参考方案4】:
在 g++5.2 中(例如 g++4.9,它是不同的)字符串基本上定义为:
class string
char* bufferp;
size_t length;
union
char local_buffer[16];
size_t capacity;
;
;
在普通计算机上,这加起来是 32 个字节 (8+8+16)。
实际定义当然是
typedef basic_string<char> string;
但想法是一样的。
【讨论】:
【参考方案5】:它依赖于库。您不应该依赖std::string
对象的大小,因为它可能会在不同的环境中发生变化(显然在不同的标准库供应商之间,但也在同一库的不同版本之间)。
请记住,std::string
实现是由针对各种用例进行优化的人员编写的,通常会导致 2 种内部表示,一种用于短字符串(小型内部缓冲区),另一种用于长字符串(堆分配外部缓冲器)。开销与在每个 std::string
对象中保存这两者有关。
【讨论】:
【参考方案6】:问:为什么狗是黄色的? A:不一定。
(一个?)std::string 对象的大小取决于实现。我刚刚检查了 MS VC++ 2010。它确实为 std::string 使用了 32 个字节。有一个 16 字节的联合,其中包含字符串的文本(如果合适的话)或指向堆存储的指针以存储更长的字符串。如果实现者选择在字符串对象而不是堆中保留 18 字节字符串,则大小将为 34 字节。其他 16 个字节构成开销,包括字符串长度和当前为字符串分配的内存量等内容。
不同的实现可能总是从堆中分配内存。这样的实现无疑需要更少的内存来存储字符串对象。
【讨论】:
以上是关于为啥 sizeof(string) == 32?的主要内容,如果未能解决你的问题,请参考以下文章
c语言中,为啥在64位系统中long跟指针的大小是8,而32位的却是4?是啥导致不一样?求详细解答
为啥 sizeof(my_arr)[0] 编译并等于 sizeof(my_arr[0])?
使用 sizeof(boost::lockfree::queue<std::string>) 时出错
为啥 sizeof 表达式不是像 2、4、8 等这样的编译时常量?