12

This should be simple but I have no clue where to look for the issue:

I have a struct:

struct region
{
public:
    long long int x;
    long long int y;
    long long int width;
    long long int height;
    unsigned char scale;
};

When I do sizeof(region) it gives me 40 when I am expecting 33.

Any ideas?

(mingw gcc, win x64 os)

sth
  • 222,467
  • 53
  • 283
  • 367
George
  • 1,224
  • 12
  • 21
  • @sth: What was the point of those edits? – eriktous Apr 08 '11 at 14:51
  • 1
    @eriktous: I liked the question and tried to increase the signal-to-noise ratio by removing the text that didn't add anything to the question. – sth Apr 08 '11 at 15:41

3 Answers3

13

It's padding the struct to fit an 8-byte boundary. So it actually is taking 40 bytes in memory - sizeof is returning the correct value.

If you want it to only take 33 bytes then specify the packed attribute:

struct region
{
public:
    long long int x;
    long long int y;
    long long int width;
    long long int height;
    unsigned char scale;
} __attribute__ ((packed));
Claudiu
  • 224,032
  • 165
  • 485
  • 680
  • 7
    Note that using an array of `packed` structures can severely hamper performance due to elements of the struct being unaligned. – Mark B Apr 08 '11 at 14:21
  • 2
    Is there a way to get some kind of packed_sizeof(MyStruct)? This way, one could use the struct as unpacked and get good performance, but you can use the real size of the struct for reading from the file – codymanix Apr 08 '11 at 14:25
  • 3
    @codymanix: how are you saving the struct to a file? if you have an array of unpacked structs, say 10 of them, then it'll save 400 bytes to the file. you'll want to read 400 bytes back in order to get 10 properly-formed structs. reading 330 bytes will leave you short a few. but also you shouldn't save and load to a file dependent on the struct padding/alignment since different compilers might do things differently. pack it manually to the file and read it back manually. – Claudiu Apr 08 '11 at 14:39
  • @codymanix: You assume that the padding is always at the end. It's not true, padding can occur anywhere in the middle, see the post from Nawaz. Padding and unpadding is typically tedious boilerplate code, just like file I/O in general. Boost.Serialization helps a bit, but it isn't completely automatic either. There's no C# [Serializable] equivalent in C++. – Tamas Demjen Apr 08 '11 at 17:36
6

long long int values are 8 bytes each. scale is only 1 byte but is padded for alignments, so it effectively takes up 8 bytes too. 5*8 = 40.

André Caron
  • 44,541
  • 12
  • 67
  • 125
  • brilliant, I didn't know that... any way of avoiding it? want to read the struct from file and need to get the exact size – George Apr 08 '11 at 14:20
  • @George: it usually pays off to store data as plain text instead of in binary formats. Check out the Power of Plain Text section in the Pragmatic Programmer book. – ssegvic Apr 08 '11 at 14:28
  • @George, most compilers have an attribute syntax which can specify the packing on a structure. In gcc I think it is `__attribute__((packed))`, look that up. Note of course your binary data is totally non-portable. – edA-qa mort-ora-y Apr 08 '11 at 14:30
  • 1
    @George: You may be asking for worse trouble by reading binary data from a file straight into a struct, because of byte ordering. You may be better with your struct having padding and reading fields separately, writing them to elements of the struct. – quamrana Apr 08 '11 at 14:31
  • In instead of reading sizeof(region) bytes, you just read 33 bytes to get a single reagion you'll get what you expect. If you try to read multiple regions at the same time (an array of regions), it won't work. – John Gordon Apr 08 '11 at 14:35
  • @edA-qa mort-ora-y: Apart from integral type sizes why would you say it is 'totally' non-portable? – George Apr 08 '11 at 14:37
  • 3
    @George, certain architectures don't like misaligned data types -- though most will allow it. Different compilers will also do different things, but there is likely a way in all of them to get the packing right. Endianess may be an issue between chipsets. And yes of course, the size, use the specifically sized types like `uint32_t`, since the size of those types varies between chips and even OSs. – edA-qa mort-ora-y Apr 08 '11 at 14:50
  • To elaborate with a specific example, SPARC systems will throw a bus error and crash your program on misaligned memory accesses. – Mark B Apr 08 '11 at 14:52
5

As others said, structs are padded for alignments, and such padding not only depends on the type of the members, but also on the order of the members in which they're defined.

For example, consider these two structs A and B as defined below. Both structs are identical in terms of members and types; the only difference is that the order in which members are defined isn't same:

struct A
{
    int i;
    int j;
    char c;
    char d;
};

struct B
{
    int i;
    char c;
    int j;
    char d;
};

Would the sizeof(A) be equal to sizeof(B) just because they've same number of members of same type? No. Try printing the size of each:

cout << "sizeof(A) = "<< sizeof(A) << endl;
cout << "sizeof(B) = "<< sizeof(B) << endl;

Output:

sizeof(A) = 12
sizeof(B) = 16

Surprised? See the output yourself : http://ideone.com/yCX4S

Nawaz
  • 353,942
  • 115
  • 666
  • 851