6

I have the following structure in C++

struct A {
  int a;
  double b;
  float c;
}

Is there a difference in memory layout between this struct and one with a function added to it?

struct B {
  int a;
  double b;
  float c;
  void foo();
}
B::foo() { //do stuff }
YSC
  • 38,212
  • 9
  • 96
  • 149
Iliketoproveit
  • 445
  • 6
  • 15
  • Simple enough to figure out: `printf("A: %zx, B: %zx\n", sizeof(struct A), sizeof(struct B))` – HardcoreHenry Mar 21 '18 at 13:17
  • 4
    XY problem? Why do you want to know about the memory layout? – n. m. could be an AI Mar 21 '18 at 13:18
  • 3
    @n.m. That's just silly. Because he does. – Zebrafish Mar 21 '18 at 13:20
  • "The XY problem is asking about your attempted solution rather than your actual problem" I don't believe that I posted an attempted solution, I'm just asking a general question about the way C++ lays out memory in both of these cases – Iliketoproveit Mar 21 '18 at 13:22
  • @Iliketoproveit could you clarify whether you're interested in standard-layout (~ C-friendly) classes, or any class with any members and/or base classes? – Quentin Mar 21 '18 at 13:28
  • @Zebrafish high-level programming languages are invented precisely because we don't want to know about the low-level bits. – n. m. could be an AI Mar 21 '18 at 13:28
  • 1
    @n.m. I personally work in embedded systems where memory layout (particularly structure sizes) can be very important due to limited memory and caching optimizations, so I would say this is a reasonable question. (and yes, I've used C++ for embedded) – HardcoreHenry Mar 21 '18 at 13:32
  • 1
    @n.m. You know that's completely rubbish when it comes to C++ because it's not high-level as you claim. The reason I say you comment is silly is because I could ask about static-initialisation order in the same compilation unit, which is of utmost importance to know about, and yet it seems your XY remark would be directed to that question as well. It's important to know how memory is laid out in a language like this. – Zebrafish Mar 21 '18 at 13:32
  • @HardcoreHenry I don't quite see how this relates to the question. It's OK to ask "does adding a member function increase the struct size?" It would be a very reasonable question. But it has little to do with layout compatibility of these two particular structs. – n. m. could be an AI Mar 21 '18 at 13:48
  • @Zebrafish it is important to know exactly how memory is laid out, in a very limited set of rare circumstances. Most of the time it makes no difference at all (i.e. we're using the high level bits). If you do want to know, the immediate suspicion is that you are trying to do something fishy, rather than there's a real need to know, simply because the former situation is way more common. – n. m. could be an AI Mar 21 '18 at 13:58

4 Answers4

8

The C++ standard guarantees that memory layouts of a C struct and a C++ class (or struct -- same thing) will be identical, provided that the C++ class/struct fits the criteria of being POD ("Plain Old Data"). So what does POD mean?

A class or struct is POD if:

All data members are public and themselves POD or fundamental types (but not reference or pointer-to-member types), or arrays of such

  • It has no user-defined constructors, assignment operators or destructors
  • It has no virtual functions
  • It has no base classes

So yes in your case, the memory layout is the same.

Source: Structure of a C++ Object in Memory Vs a Struct

Benjamin Barrois
  • 2,566
  • 13
  • 30
  • 2
    The concept is named StandardLayoutType, see [`std::is_standard_layout`](http://en.cppreference.com/w/cpp/types/is_standard_layout) – Caleth Mar 21 '18 at 13:23
  • 1
    This covers PODs (or Standard-Layout Types as they're known now), but I'm unsure whether OP intended to restrict their question to them. A member `std::string`, for example, wouldn't reasonably break that layout guarantee even though the type is no longer standard-layout. – Quentin Mar 21 '18 at 13:24
  • standard layout is a term I learned after POD, are they equivalent or are there subtle differences? – Zebrafish Mar 21 '18 at 13:27
  • 1
    Wow, just found out that there's std::is_standard_layout and std::is_pod, I better look into this. – Zebrafish Mar 21 '18 at 13:28
  • The change from POD to Standard-Layout now *requires* `class A { private: int one; float two; }` to match `struct B { public: int one; float two; }`. POD requires everything to be public – Caleth Mar 21 '18 at 13:29
  • @YSC "has the same access control (Clause 11) for all non-static data members" vs "All data members are public" – Caleth Mar 21 '18 at 13:39
  • @Caleth I meant _"The C++ standard guarantees that..."_: where? – YSC Mar 21 '18 at 13:43
  • They are both standard layout types, all the data members form a common initial sequence. – Caleth Mar 21 '18 at 13:45
  • @Caleth I know (see my answer). What I don't know is where does the Standard mandates that layout-compatible types have the same memory representation. – YSC Mar 21 '18 at 13:51
  • @YSC Oh, I wasn't arguing that. All along I meant layout compatible – Caleth Mar 21 '18 at 13:53
  • I'm highly skeptical about that guarantee from the C++ Standard. The source you quote does not provide any proof. I'd be glad to be proven wrong. In the meantime, I've downvoted this answer as opinion-only backed. – YSC Mar 22 '18 at 14:56
7

Is there a difference in memory layout between this struct and one with a function added to it?

It might.

Since A and B are standard layout1, and their common initial sequence consists of every non-static data member2, they are layout-compatible.

The Standard only describe the semantics of an abstract machine, so there is no guarantee an object of type A will be represented in memory as an object of type B, but layout-compatible types tend to be.


1) [class]/7

A standard-layout class is a class that:

  • has no non-static data members of type non-standard-layout class (or array of such types) or reference,
  • has no virtual functions (10.3) and no virtual base classes (10.1),
  • has the same access control (Clause 11) for all non-static data members,
  • has no non-standard-layout base classes, either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
  • has no base classes of the same type as the first non-static data member.

2) [class.mem]/21 & [class.mem]/22

Two standard-layout struct (Clause 9) types are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in declaration order) have layout compatible types (3.9).

YSC
  • 38,212
  • 9
  • 96
  • 149
  • 1
    The standard doesn't actually say what "layout-compatible" entails, except that pointers to such types must have the same representation. Or I couldn't find anything. – n. m. could be an AI Mar 21 '18 at 14:14
  • 1
    @n.m. I'm not sure to understand what had motivated your comment. I'm not saying the Standard mandates anything about layout-compatible types, quite the opposite. – YSC Mar 22 '18 at 13:23
  • I'm sorry I just wanted to make it clear, 's all. Didn't see you are saying it too. – n. m. could be an AI Mar 22 '18 at 13:36
  • @n.m. OK i'm fine with it: explicit>implicit. – YSC Mar 22 '18 at 14:51
  • Why do people talk about standard layout and layout compatibility then?? – haelix Oct 11 '18 at 01:58
  • 1
    @haelix Because they offer some guarantees. Like [`[class.mem]/25`](http://eel.is/c++draft/class#mem-25) for instance. – YSC Oct 11 '18 at 09:21
  • @YSC perfect, thanks. Do you have a reference to some other guarantee offered? Perhaps something to do with C-struct compatibility? If there is one. – haelix Oct 11 '18 at 12:31
2

It formally depends on your compiler, but a compiler where declaring a non-virtual member function changes the layout of the class would be borderline sabotage. You need this kind of stability to enforce the compatibility on which shared objects rely on every platform.

Quentin
  • 62,093
  • 7
  • 131
  • 191
1

Yes and no...

In your specific case, no. The struct is nothing more than a data container, and the function resides elsewhere. When the function is called, a pointer to the struct is passed as an additional, implicit first parameter that appears as this pointer within the function.

Matter changes, though, if you add a virtual function. Although the C++ standard does not mandate it, vtables are the defacto standard, and the class will receive a pointer to a vtable as very first, but invisible member. You can try this out by printing out the size of an object before and after adding the virtual function.

On the other hand, if the class is virtual already because of inheriting from another class that already has virtual functions, memory layout won't change any more again as there is already a pointer to the vtable included (although it will point to a different location for instances of the sub-class).

Aconcagua
  • 24,880
  • 4
  • 34
  • 59