0

My C++ class has data members of string and int. I thought its size is 36 bytes but the output says 40 bytes. Could someone explain?

#include <iostream>

using namespace std;

class SizeTest {

private:
    std::string name;
    int i;

};

int main(int argc, char** argv) {

    cout<<"sizeof(SizeTest) is : " << sizeof(SizeTest)<<endl;
}

Output:

sizeof(SizeTest) is : 40
phuclv
  • 37,963
  • 15
  • 156
  • 475
  • 4
    First, consider why you think 36 was the expected size. There's nothing in the C++ standard that says `sizeof(string)` is any specific value. – selbie Mar 22 '20 at 03:25
  • I thought, string is 32 bytes and int is 4 bytes. Class SizeTest has data memers, So sizeof(SizeTest) supposed to be 36 bytes in 64 bit gcc – Abdul Rahuman Seeni Mohamed Mar 22 '20 at 03:30
  • @AbdulRahumanSeeniMohamed Have you actually tested that `sizeof(std::string) == 32` with your compiler/platform? There is no requirement that it is the same everywhere. – walnut Mar 22 '20 at 03:32
  • 2
    *I thought, string is 32 bytes* - no way is that ever correct. `sizeof(string)` varies widely between debug\retail builds and between 32/64 bit compilers. You could update your compiler tomorrow and find that it's been changed to 99 bytes for no particular reason. – selbie Mar 22 '20 at 03:32
  • The compiler aligns all types to some boundary, including members of a `struct`/`class`. That means it can add padding between members of a `struct`/`class` to ensure ALL members are properly aligned. The alignment is needed due to requirements of hardware, and incorrect alignment can result in operations being VERY slow or (in worst cases) triggering hardware interrupts that cause the host operating system to terminate the program. – Peter Mar 22 '20 at 03:34
  • @selbie I get that you are saying that the size is an unspecified implementation detail, but it would not change overnight, because changing the size of `std::string` would be an ABI break. – walnut Mar 22 '20 at 03:35
  • 1
    I was making the point that a simple compiler upgrade or system upgrade that touches the stdc++ libraries could easily break your assumptions. You should never link code together that wasn't using the exact same compiler, with the exact same switches and the exact same linker libraries. Otherwise, there is no ABI to begin with. – selbie Mar 22 '20 at 03:41
  • 2
    @walnut - there are plenty of compiler vendors that have deliberately elected to accept an "ABI break" between two versions of their compiler. Yes, that forces rebuilding of all code and libraries. But if often gives other benefits. For example, g++ introduced an ABI break on most (all?) platforms between gcc 2 and 3 (where it introduced the Itanium ABI). It also has options to select between different ABIs. – Peter Mar 22 '20 at 03:42
  • In addition to what @Peter said, GCC also broke ABI when standard library was updated for C++11. In particular, the size of `std::string` changed. – eerorika Mar 22 '20 at 03:45
  • @eerokia strictly they didn't break the ABI, they introduced a new `std::string` implementation in a different namespace, the old implementation still exists and is available by defining a macro – Alan Birtles Mar 22 '20 at 07:11

2 Answers2

3

Could someone explain?

sizeof always returns the correct size. Thus, we can deduce that your expectation of the size was wrong.

You didn't explain why you thought what you did, but most likely cause for the mistake is failing to take memory alignment into consideration.

I thought, string is 32 bytes

It can be on some systems. That is not the case on all systems.

and int is 4 bytes.

It can be on some systems. That is not the case on all systems.

So sizeof(SizeTest) supposed to be 36 bytes

No. The size of a class is not "supposed" to be the sum of the size of its members.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Probably more than memory alignment. sizeof(string) changes widely between 32-bit and 64-bit compiles. And it's also different between debug and release builds. – selbie Mar 22 '20 at 03:29
2

First, std::string size isn't necessarily 32. For example it's 8-byte long on some 64-bit platforms and 24-byte on 64-bit libc++

On most implementations std::string contains a pointer to the char array, and 2 pointers or size_ts for the size and capacity. On a 64-bit platform those are 64-bit types so std::string must be aligned to 8 bytes, hence your class is also 8-byte aligned. Some implementations add another 8 dummy bytes to help align the data to 32-byte which is better than 8, and it's also better for small-string optimization since you can store more characters in a small string without resorting to allocate memory on heap

In your implementation std::string is a 32-byte type and int is a 4-byte type which makes the total size 36 bytes which isn't a multiple of 8. As a result 4 bytes of padding must be added to round the size to the next multiple of 8 so that when putting in an array all the instances of the class are properly aligned. The result is a 40-byte class

See also Why isn't sizeof for a struct equal to the sum of sizeof of each member?

You can pack the struct in some compilers using __attribute__ ((__packed__)) or #pragma pack to make the size 36-byte but it's a really really bad idea to do. Anyway you can see the demo on Godbolt

phuclv
  • 37,963
  • 15
  • 156
  • 475
  • `std::string contains a pointer to the array, and 2 pointers or size_ts for the size and capacity` is not true, it might be true for some implementations but is not guaranteed. It could be implemented as a single pointer to some other data structure or any other implementation chosen by the library implementer – Alan Birtles Mar 22 '20 at 07:15
  • @AlanBirtles yes, but the important thing here is that the first item is a pointer and will be aligned accordingly – phuclv Mar 22 '20 at 07:58