计算分配给std :: string的内存(以及std :: vector中字符串的使用)

时间:2019-01-17 13:52:52

标签: c++ string memory sizeof allocation

我想计算在创建字符串并将值分配给字符串时分配了多少内存。

string s = "";
cout << sizeof(s) << endl;
cout << sizeof(s.at(0)) * s.capacity() << endl;
s = "1234567890qwertz";
cout << sizeof(s) << endl;
cout << sizeof(s.at(0)) * s.capacity() << endl;

这是我的字符串s占用的所有内存吗?我通过简单地调用sizeof(s)(在我的计算机上为40个字节)获得的初始/静态部分动态部分-一个字符的大小乘以分配的占位符以使字符串有效地可调整大小(在我的机器上,字符串s首先分配了15个字节的块,直到文本太长为止,因此在第二次分配之后,动态部分为31个字节。为何不使用16和32字节呢?

这种思考方式(每个字符串静态+动态是它占用的所有内存)是否正确?

这意味着,如果我有一个字符串的std :: vector,并且我也想计算该向量的所有内存,我将需要做同样的事情:我添加了我的初始/静态大小向量以获得正的动态部分,这意味着一个字符串所占用的总内存与我上面对向量内部的每个字符串所做的方式相同?

vector<string> cache;
// fill cache with strings of dynamic length
int size = sizeof(cache);
for (int i = 0; i < cache.size(); i++)
{
    size += sizeof(cache[i]);
    size += sizeof(cache[i].at(0)) * cache[i].capacity();
}

总而言之,我的“缓存”占用的内存量是否正确?

编辑: 或者我是否还需要考虑到std :: vector本身也具有.capacity()> = .size(),这可能意味着我实际上需要这样做:

对于每个cache.capacity()-我需要添加sizeof(cache[i])并另外添加 为每个cache.size()-添加sizeof(cache[i].at(0)) * cache[i].capacity() ??

3 个答案:

答案 0 :(得分:1)

这个问题将很难回答。天真的,您会认为消耗的内存总量是

vector_capacity * sizeof(std::string) + sum_of_capacity_of_each_string_in_the_vector

但这是一个上限,而不是实际消耗的上限。例如,short string optimization允许std::string将字符串数据存储在字符串对象本身消耗的存储空间中(称为静态大小)。如果是这样,那么实际消耗的空间将是

vector_capacity * sizeof(std::string)

和向量中每个字符串的容量就是您要占用多少空间而不分配任何额外空间。您将需要检查实现,以查看其是否使用SSO,并将其存储在字符串对象中的一长串字符串,以实际知道容量值是使用字符串内部空间还是实际消耗了额外的内存。这样就消耗了实际空间

vector_capacity * sizeof(std::string) + sum_of_capacity_of_each_string_in_the_vector_where_
                                        the_capcity_is_more_than_the_sso_amount

在您进行计算时,不需要sizeof(cache[i].at(0))std::string使用char,并且保证sizeof(char)1

答案 1 :(得分:1)

字符串的容量比您的预期少一个简单的原因,那就是

s.c_str()

C ++字符串存储在一块内存中,其容量给出了总大小和所用空间的大小。但是C字符串以0结尾。 C ++字符串在内存块的末尾保留一个额外的字节来存储0。这样,s.c_str()总是以0结尾。

因此,字符串的动态部分使用的内存为容量+ 1。

关于一个字符串或字符串向量消耗的总内存,NathanOliver回答说。但是要提防向量多次持有相同的字符串。

答案 2 :(得分:1)

如果您想知道std::vector<std::string>使用了多少空间,请计算:

auto netto_memory_use(std::vector<std::string> const& x) noexcept {
    return std::accumulate(
        begin(x),
        end(x),
        sizeof x + sizeof x[0] * x.capacity(),
        [](auto n, auto&& s) {
            if (std::less<void*>()(data(s), &s)
            || std::greater_eq<void*>()(data(s) + s.capacity(), &s + 1))
                return n + s.capacity() + 1;
            return n;
        });
    }

与仅使用比较运算符相比,我使用std::less<void*> / std::greater_eq<void*>来利用它们定义完整顺序。

累加器会在添加字符串的电容之前测试应用的小字符串优化(SSO)。当然,所有容量为0的字符串都可以共享相同的静态分配终止符。或者可以将容量和/或长度与字符数据一起分配。
尽管如此,除了内存管理系统的开销之外,这应该是所用内存的一个很好的近似值。