Will sizeof always be the same?
Asked Answered
B

4

5

I have a quick question about class structuring, padding, and the resulting sizeof the class. In the below example, on every compiler I've tested, the result is always 40 bytes for sizeof A, which makes sense to me.

#include <iostream>
class A {
    int a, b; // 4 + 4
    short c;  // + 2
    double f; // not currently 8 byte aligned. Currently 10 bytes, so pad 6 extra bytes, so: + 6 + 8
    char w;   // + 1
    bool d;   // + 1
    double g; // not currently 8 byte aligned. Currently 26 bytes, so pad 6 extra bytes, so: + 6 + 8

    // 4 + 4 + 2 + 6 + 8 + 1 + 1 + 6 + 8 = 40
};

int main() {
    std::cout << sizeof( A );
}

My question is, will this always be true? (assume the alignof and sizeof each individual member doesn't change). I know I can reorder it, so that the doubles come first, and it shrinks down to 32 bytes; but can a compiler make this decision? Or is it the always the programmer's responsibility? (I'm assuming the compiler can't reorder)

Belly answered 13/12, 2019 at 15:40 Comment(7)
@KamilCuk, hence why I also said if sizeof each individual member doesn't change ;)Belly
It will always be true on your machine. If you compile it on another one or with a different compiler it might be different.Protector
If you use a struct, you can force disable padding, so the struct will always the same size, regardless of ordering of the members https://mcmap.net/q/545357/-locally-disable-paddingNicky
@John: Only if your compiler supports that.Must
The Fundamental Types can have a range of sizes see: en.cppreference.com/w/cpp/language/typesViburnum
@RichardCritten, yes, but the assumption in the question is they are the same size/alignmentBelly
It will always be true when you compile the code with the same compiler, the same options, and the same target environment.Hartnett
P
5

sizeof(A) in the execution of your program will always be the same. If you change the order of the members or how A is packed and recompile, then you can/will get a different value. If you compile with a different compiler the size can also change. It can also change on different machines using the same compiler because different machines can have different sizes for the fundamental types.

Long story short, if you depend on your class being a specific size then add a static_assert that checks for that. That way if it does change, you'll get a nice compiler error instead of possible UB.

I know I can reorder it, so that the doubles come first, and it shrinks down to 32 bytes; but can a compiler make this decision?

No, the compiler cannot reorder the members for better packing since your class is a standard layout class and all members must be laid out in memory in declared order.

Protector answered 13/12, 2019 at 15:55 Comment(2)
Curious, why would a compiler do something to change the size compared to another compiler on the same machine, if it can't reorder? I currently have several classes that have static_assert( sizeof( class ) == 240 ) ), but wasn't sure if that was necessary.Belly
@Belly They might decide it needs different padding for some reason. I doubt it would if none of the underlying sizes change, but is a possibility.Protector
M
5

The compiler is not allowed to reorder the members because they all have the same access level. So if there was, for example, a public member then the compiler could reorder your member variables.

Reference: https://timsong-cpp.github.io/cppwp/class.mem#19

The C++ standard offers no guarantees for the size of your class, other than it must be such that the members are individually addressable.

Must answered 13/12, 2019 at 15:40 Comment(3)
Is "The compiler is allowed to reorder the members since" true for C++? (I know it can happen in D, but I had thought C++ laid them out in memory in declaration order.)Mccammon
@Eljay: It wouldn't be the first time I was wrong on something this fundamental but it's what I've been brought up to think. Please downvote if I'm incorrect.Must
It's when you mix access levels in a class that ordering can change: timsong-cpp.github.io/cppwp/class.mem#19Protector
P
5

sizeof(A) in the execution of your program will always be the same. If you change the order of the members or how A is packed and recompile, then you can/will get a different value. If you compile with a different compiler the size can also change. It can also change on different machines using the same compiler because different machines can have different sizes for the fundamental types.

Long story short, if you depend on your class being a specific size then add a static_assert that checks for that. That way if it does change, you'll get a nice compiler error instead of possible UB.

I know I can reorder it, so that the doubles come first, and it shrinks down to 32 bytes; but can a compiler make this decision?

No, the compiler cannot reorder the members for better packing since your class is a standard layout class and all members must be laid out in memory in declared order.

Protector answered 13/12, 2019 at 15:55 Comment(2)
Curious, why would a compiler do something to change the size compared to another compiler on the same machine, if it can't reorder? I currently have several classes that have static_assert( sizeof( class ) == 240 ) ), but wasn't sure if that was necessary.Belly
@Belly They might decide it needs different padding for some reason. I doubt it would if none of the underlying sizes change, but is a possibility.Protector
M
3

Will sizeof always be the same?

sizeof is compile time constant. So for any program that has been compiled, the sizeof will not change between executions. There is no such guarantee for different compilations of the program however. In fact, when program is compiled for another system, it is quite typical for sizeof and alignof of even fundamental types to be different.

I know I can reorder ... can a compiler make this decision?

There are restrictions for standard layout classes. The first member is guaranteed to have the same address as the super object. Also, for any such classes with same types of members in same order at the beginning of declaration order, that "common initial sequence" may be accessed through separate members of union, which in practice mean that they must have the same address, which effectively prevents any re-ordering of members because compiler cannot know what other classes with potentially shared common initial sequences there may be. A is a standard layout class.

These restrictions don't apply to non-standard layout classes, with which the compiler is allowed to be more relaxed. As far as I know, the language doesn't prevent the compiler from choosing the order of members of non-standard layout classes. But in practice, in order to link together object files that have been compiled separately, those object files must conform to the same binary interface, i.e. they need to have the same representation for types. If the compiler used one layout for one compilation, and different layout for another, that would break the ABI. So, whatever order the compiler chooses, it should stick to it. And any compiler that intends to be compatible, should use that same ABI. For what it's worth the Itanium ABI, used by GCC, specifies:

2.4 Non-POD Class Types

II. Allocation of Members Other Than Virtual Bases

For each data component D (first the primary base of C, if any, then the non-primary, non-virtual direct base classes in declaration order, then the non-static data members and unnamed bit-fields in declaration order) ...


Or is it the always the programmer's responsibility? (I'm assuming the compiler can't reorder)

It's best for the programmer to assume that sub objects are in declaration order, and to choose that order in a way to minimise padding. It's best to not assume the order of sub objects for purposes of accessing memory of non-standard layout classes.

Metrical answered 13/12, 2019 at 16:11 Comment(0)
R
1

The compiler is not allowed to reorder the attributes in your class (this order is used when constructing and deconstructing members so it would potentially change the meaning of your program to do so).

However you also can't count on the result of sizeof always being the same. What if there came about an architecture for which a 64 bit int made more sense than 32 bit? In that case the size would be larger.

Ramses answered 13/12, 2019 at 15:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.