为啥 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 等这样的编译时常量?

为啥将 C-Array 传递给函数时 sizeof() 值错误? [复制]

关于sizeof()size()的有些问题