36

I was just messing around and learning about vectors as well as structs, and at one point, I tried outputting the size of a vector in bytes. Here's the code:

#include <iostream>
#include <vector>

struct Foo{
    std::vector<int> a;
};

int main()
{
    using std::cout; using std::endl;   

    Foo* f1 = new Foo;

    f1->a.push_back(5);
    cout << sizeof(f1->a) << endl;
    cout << sizeof(f1->a[0]) << endl;

    delete[] f1;
}

The output is 24 and 4.

Obviously the second line printed 4, because that is the size of an int. But why exactly is the other value 24? Does a vector take up 24 bytes of memory? Thanks!

Archie Gertsman
  • 1,601
  • 2
  • 17
  • 45
  • 10
    A vector is usually [implemented with three pointers](http://stackoverflow.com/questions/30422205/why-the-libc-stdvector-internally-keeps-three-pointers-instead-of-one-pointe) – jaggedSpire Dec 01 '15 at 16:12
  • 1
    That's the size of `class std::vector`. It's implementation-dependant, and it's the sum of the sizes of the members of such a class. Usually a pointer to the underlying C-style array and various members like size, capacity and so on. – Banex Dec 01 '15 at 16:14
  • 4
    On a side note `delete[] f1` is incorrect, it should be `delete f1` only – Uchia Itachi Dec 01 '15 at 16:22
  • @Uchia Itachi Thanks, I wasn't too sure about that. Noted. – Archie Gertsman Dec 01 '15 at 16:23
  • 4
    Since you seem to be learning C++: normally we'd not even use `new/delete`. We'd write `Foo f1;` and `fi.push_back` (dot not arrow). – MSalters Dec 01 '15 at 16:38
  • @MSalters Yeah, that makes sense. In all honesty this whole block of code was just me familiarizing myself with multiple concepts, but thanks anyway, I'll keep it in mind. – Archie Gertsman Dec 01 '15 at 16:40
  • 1
    Closely related: http://stackoverflow.com/questions/32931360/total-memory-of-a-c-class-object/32933422#32933422 – Christian Hackl Dec 01 '15 at 17:00

2 Answers2

56

While the public interface of std::vector is defined by the standard, there can be different implementations: in other words, what's under the hood of std::vector can change from implementation to implementation.

Even in the same implementation (for example: the STL implementation that comes with a given version of Visual C++), the internals of std::vector can change from release builds and debug builds.

The 24 size you see can be explained as 3 pointers (each pointer is 8 bytes in size on 64-bit architectures; so you have 3 x 8 = 24 bytes). These pointers can be:

  • begin of vector
  • end of vector
  • end of reserved memory for vector (i.e. vector's capacity)
Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • Add to that solutions that try to be smart with 48 bit pointers, SSO, custom allocators, and the size might reasonably be anywhere between 20 and 64 bytes, plus whatever is allocated on the heap. – Peter Dec 01 '15 at 18:44
  • 3
    Even 16 bytes is realistic: 8 byte pointer to the start, 4 byte element _count_, 4 byte reservation. – MSalters Dec 02 '15 at 08:49
0

Here is the code from libc++'s (ignore the compressed pair, it is an iterator which maintains the capacity of the vector). Here are 3 pointers in the implementation of .

pointer                                    __begin_;
pointer                                    __end_;
__compressed_pair<pointer, allocator_type> __end_cap_;

As pointers are 8 Byte in size on a 64-bit CPU, that is why 8*3 = 24 Byte.

Nikhil
  • 1
  • Compressed pair is used to make sure that, if `allocator_type` is stateless, the stored allocator doesn't occupy space (it could be implemented e.g. relying e.g. on [empty base optimization](https://en.cppreference.com/w/cpp/language/ebo)), but since C++20 you can also use [`[[no_unique_address]]`](https://en.cppreference.com/w/cpp/language/attributes/no_unique_address) member for that. – YurkoFlisk May 19 '23 at 15:14
  • The reason standard libraries still use their own compressed pairs is that 1) their code should be compatible with previous standards and 2) they abstract away which optimization is used to avoid platform-specific behaviour, because aforementioned optimizations could be sometimes ignored, while library writers can make sure to use a method which will definitely work on target compiler/platform. – YurkoFlisk May 19 '23 at 15:18