C vs C++ struct alignment
Asked Answered
G

3

63

I've been asked in a recent interview about C++ struct fields alignment and theoretized that C and C++ follows the same strategy in struct packing.

Hovewer, it was the wrong assumption. The interviewer said that in general C and C++ are packing structs in different ways and we should never expect the opposite. IMHO it's strange statement. There is no pack "C" qualifier for structs in C++ for using in bilingual C/C++ header files.

So in practice it could mean that you can't create a struct in C++ and pass it to a C library because in general its fields will be aligned in a different way and have different offsets. But, in fact, most programmers seriously rely on this interoperability up to the point when they convert a pointer to a C POD struct to a reference to C++ wrapper around this struct with some helper methods. Can you please clarify this matter?

Glarus answered 20/11, 2015 at 20:31 Comment(19)
What a weird question. If structure padding was different between C and C++, I'd be screwed. Maybe the interviewer found some really weird gotcha on some obscure compiler -- or maybe he was thinking how C and C++ compilers could be totally different, and expecting two compilers to align fields the same way with identical padding may not be something we should be too confident to rely on (but that would hold true even between two C compilers or two C++ compilers -- has little to do with the language).Jambeau
You are correct. Packing is a processor implementation detail, it ensures that alignment rules are observed. A C compiler and a C++ compiler that target the same processor have to deal with the same implementation detail.Grum
Structure alignment is decided on compiler level and depends on the processor architecture. Compilers are most like have compatible alignments within one suite (GCC, Clang/LLVM) due to ABI requirements but may have different rules between each other.Apophasis
For sure most likely -- I was just trying to debug this scenario and think up some really obscure case where the same struct would have different alignment between two otherwise equal conditions -- only the difference between C and C++ (which the interviewer might think involves a separate compiler).Jambeau
structs in c & c++ definitely aligned differently - I had issues when was compiling the same code which was using unpacked structs with the same version of gcc vs g++. .so was compiled with g++ and main binary was working correctly only when compiled by g++, gcc binary had different offsets for some fieldsProvenance
@ISanych, what?? g++/gcc are just frontends. It doesn't matter what you call -the actual driver is called based on file extension as far as the compilation goes (linking is different).Barrington
@Provenance Are you sure the code didn't have preprocessor branches which treated C++ branches differently or something like that?Jambeau
Padding and alignment are normally identical in both languages. They are mandated by the ABI of the target platform. However, the layout may differ, as C++ might add hidden fields e.g. for RTTI, etc. This is also defined in the (C++)ABI.Progenitor
@Olaf But since you can't have inheritance in C, an identical struct in C++ would never require RTTI or a v-table pointer.Atul
Yeah -- I think it has to be an identical struct we're comparing, or otherwise one might as well ask, "Do two structs with different fields have the same alignment?" The question really puts the emphasis on the language, so I'd think we'd need as close to an apples to apples comparison as we can get.Jambeau
@ISanych, I told you - linking is different. Compile them.Barrington
@SamCristall: The question was about the alignment/padding anyway, which is mandated by the CPU and would be the same for comparable types in any language. But the layout may differ. There is no word about interworking. But, yes, if you use a C interoperation-header (extern "C" { ...), that should be identical. I would not bet on the reverse, however.Progenitor
@Olaf, no. Padding is not neccessarily mandated by CPU - othwerise, there would be no pargma pack directives.Barrington
@Barrington you right - but it was case with changing extension - will try to reproduce it, I don't have these sources at homeProvenance
@SergeyA: Padding is the result of alignment requirements, that's why I used the slash in-between, not the ampersand. You normally do not pad without actual need. The CPU may not be able to access its native types with a single instruction. E.g. for some ARM CPUs, gcc emits special code for packed structs which read an int byte-wise and assembles them in registers (and vice-versa for writes). Quite the same as manual (de)serialisation. And the pack directive is not standard anyway and not supported by all compilers.Progenitor
@Olaf, I know it all. I have just said, and I mean it, not all CPUs are mandating a partiucular alignment. In particular, Intel does not.Barrington
@SergeyA: Well, actually, every CPU requires a specific alignment. But who stated this cannot be 1 octet? IIRC, there was only one CPU Family which had a bit-alignment, the TMS34010/20. So, of course the CPU mandates aligmnent, thus padding. Anyway, the current ABIs for x86 also mandate a specific alignment for performance reasons. Admitted, this is not mandated, but _recommended. (although without it you will loose performance).Progenitor
Sounds like the interviewer has no idea what he or she is talking about. They do not comprehend the notion that both C and C++ are abstractions. For a reason.Generalissimo
@LightnessRacesinOrbit Interviews work both ways.Equalitarian
A
68

Both the C and C++ language standards make no requirements of struct padding and leave it to be a compiler implementation detail. A strict interpretation of this would mean that there is no guarantee a struct would be the same between the two.

In practice, however, a given version of a toolchain capable of both C and C++ (such as GCC or Clang) can pack an identical struct in the same manner, if needed. Without this, a lot of production code in the world simply wouldn't work. This is a guarantee given by the toolchain, however, and not the language.

It is worth noting that if you were to declare a similar struct to the C original, but added access specifiers (private, public and protected), that the layout would change, but that's a bit of a stretch since the struct is no longer identical.

Atul answered 20/11, 2015 at 20:46 Comment(12)
Yes, and if you add virtual function it would be different too. Similar is similiar.Barrington
Thiings can be different if there are hidden fields in C++, e.g. for virtual functions and RTTI.Progenitor
@Olaf A C-compilable struct would never have virtual functions or RTTI information since it wouldn't have inheritance of any kind, but you're right that adding those would change the layout.Atul
When I create a C .h, I use #ifdef __cplusplus and extern "C" ... to ensure that C++ will interpret things in a C compatible way.Finery
@CraigEstey Me too, but I think (not 100% sure) that it only has an effect on symbol names used for linkage. I don't think this ever affects structure alignment.Jambeau
@SamCristall: Please see my other comment. I understood the question such that if you compile the same struct { ..., with C and C++ compiler, not for interworking. Anyway, I think I would have made the interviewer cry for his mom anyway ;.)Progenitor
@CraigEstey: Exactly my point. (See my comment at the question)Progenitor
@Ike You may be thinking of extern "C" int myfnc(return 0;) but extern "C" { struct mystruct { int y; int z; }; } guarantees alignment [for a given tool chain]. This tells C++ to not insert a vtable pointer, and to use C's alignment [if it were different]. Also, C++ will not define mystruct as a type [if it did, a lot of C .h would break]. Most tool chains want to interoperate (e.g. gcc and clang, possibly MS) so it holds there too. It's as much about the computer arch (e.g. x86, arm, IBM/370) and specific HW restrictions on alignmentFinery
That's true, but I think where extern "C" involves struct alignment would actually involve a compiler error and require the developer to change the struct (ex: if it did have a vptr) -- then it's the dev affecting alignment and not the qualifier. Put another way, and focusing on the vptr example, only way C++ should ever try to insert one was if the struct used inheritance, which would be impossible in C -- at that point it becomes apples and oranges -- it's like comparing two structures with different fields... of course their ABIs would differ then, and the code wouldn't compile in C.Jambeau
Put another way, is there an effect extern "C" has to guarantee struct alignment (not merely linker symbols) that wouldn't involve a compiler error? Maybe I'm thinking too much on the side of what compilers typically do instead of standards.Jambeau
No compiler error. An extC block guarantees C semantics within it: No name mangling(!), struct alignment to C's version [if it were different--IMO, the notion from interviewer that they don't match even without extC smells], and not defining a type. It's a "future proofing". We're all spinning on this because some random interviewer [probably a know-nothing LL] spewed BS and we're left to clean up after. What the known tools do, spec aside, is the common sense thing to do. Otherwise, a lot of programs break. It's an arch thing (e.g. mythical arch requires 16 byte align for int)Finery
@Craig Estey I haven't suspected that extern "C" could affect structure declarations inside its scope. I thought it just disables C++ name mangling for exported symbols. This is the most useful piece of information i've extracted from this post. Thank you.Glarus
B
29

This was most obviously wrong (on the interviewer side). It is clear to see that struct packing is the same for C and C++ for anybody who worked with any low-level API dealing with structs - for instance, a network API. All those are C functions, which accept 'C' structs, yet they are safely called millions of millions times a single day from C++ code.

You should be lucky you had this question. It makes it clear you should not be working there.

Barrington answered 20/11, 2015 at 20:44 Comment(5)
I like this answer (though I like the other one too) as a practical and deservedly consoling one. It really sounds like the interviewer had this strange conception in his head more than he was trying to get people to think in this uber theoretical way about language standard hypotheticals -- mainly because of this part: "C and C++ are packing structs in different ways and we should never expect the opposite." This doesn't sound like the interviewer firmly grasped what he was talking about at all. It's the kind of interview question I'd expect out of a company with...Jambeau
... weird coding standards based out of superstitions. At least I've seen that type. Maybe I'm being too harsh.Jambeau
Yes, there are no good answers for bad question, should be glad to not working thereWaldheim
Wow. Someone just got there and downvoted all my answers in bulk. Nice job!Barrington
@SergeyA: the interviewer didn't like your answer.Seaden
A
4

When C++ was developed, the developers figured out that C programmers relied on some things that the C++ developers didn't want to guarantee, but not guaranteeing them would mean that a lot of C code that was also valid C++ code would be broken when used as C++ code. Not desirable.

That's why they invented "POD" structures: A struct that didn't use any C++ features would behave in a C++ program exactly as it would in a C program (apart from the fact that implementation defined behaviour could change, since a C compiler and a C++ compiler are clearly not the same implementation. On the other hand, the C++ compiler would probably just copy the implementation definition from the C compiler).

If you take any plain C struct that is also a valid C++ struct (no members named "class", for example), and then you just add "public:" just after the opening brace, then its layout, order of members, alignment and so on can all change. Even though all struct members are by default public, so nothing really changed. Except that because of the "public:" it's not a POD anymore.

Actually answered 21/11, 2015 at 19:38 Comment(1)
And why do you think so? Of course, an example of your class with all public members (as well as all private members, for this matter) is POD alright.Barrington

© 2022 - 2024 — McMap. All rights reserved.