C / C ++.

Consider the following C and C ++ structure declarations:

extern "C" { // if this matters typedef struct Rect1 { int x, y; int w, h; } Rect1; } struct Vector { int x; int y; } struct Rect2 { Vector pos; Vector size; } 
  • Are the memory layouts of the Rect1 and Rect2 same?

  • In particular, can I safely reinterpret_cast from Rect2* to Rect1* and assume that all four int values ​​in a Rect2 object Rect2 mapped one to one with four Rect1 in Rect1 ?

  • Does it matter if I change Rect2 to a non-POD type, for example. adding a constructor?

+2
source share
4 answers
  • I would think so, but I also think that it could (legally) be filled between Rect2::pos and Rect2::size . Therefore, to be sure, I would add compiler-specific attributes to β€œpackage” the fields, thereby ensuring that all int are contiguous and compact. This is less about C and C ++ and that you probably use two "different" compilers when compiling in two languages, even if these compilers belong to the same provider.
  • Using reinterpret_cast to convert a pointer to one type to a pointer to another, you are likely to break the rules of "strict anti-aliasing." Assuming you find a pointer after that, which you would in this case.
  • Adding a constructor will not change the layout (although it will make a non-POD class), but adding access specifiers, such as private between two fields, can change the layout (in practice, not only in theory).
+1
source

Are the memory layouts of the Rect1 and Rect2 objects the same?

Yes. As long as certain obvious requirements are met, they are guaranteed to be identical. These obvious requirements are that the target platform / architecture is the same in terms of alignment and word size. In other words, if you are dumb enough to compile C and C ++ code for different target platforms (for example, 32 bits versus 64 bits) and try to mix them, then you will have problems, otherwise you will not have to worry, C ++ compiler basically required to create the same memory layout as if it were in C, and the ABI is fixed in C for a given size and word alignment.

In particular, can I safely reinterpret_cast from Rect2 * to Rect1 * and assume that all four int values ​​in a Rect2 object are mapped one to one with four ints in Rect1?

Yes. This follows from the first answer.

Does it make a difference if I change Rect2 to a non-POD type, for example. adding a constructor?

No, or at least no more. The important thing is that the class remains the standard-layout class, which is not affected by the constructors or any other non-virtual member. This is valid with the C ++ 11 (2011) standard. Before that, the language was about "POD types", as explained in the link I just gave for the standard layout. If you have a pre-C ++ 11 compiler, then it most likely works the same way anyway as the C ++ 11 standard (the standard C ++ 11 rules (for standard layouts and trivial types) were in mostly written according to what all compiler manufacturers have already done).

0
source

For a standard layout class such as yours, you can easily check how the elements of the structure are located from the beginning of the structure.

 #include <cstddef> int x_offset = offsetof(struct Rect1,x); // probably 0 int y_offset = offsetof(struct Rect1,y); // probably 4 .... pos_offset = offsetof(struct Rect2,pos); // probably 0 .... 

http://www.cplusplus.com/reference/cstddef/offsetof/

0
source

Yes, they will always be the same. You can try running the example below here cpp.sh It works as you expect.

  // Example program #include <iostream> #include <string> typedef struct Rect1 { int x, y; int w, h; } Rect1; struct Vector { int x; int y; }; struct Rect2 { Vector pos; Vector size; }; struct Rect3 { Rect3(): pos(), size() {} Vector pos; Vector size; }; int main() { Rect1 r1; r1.x = 1; r1.y = 2; r1.w = 3; r1.h = 4; Rect2* r2 = reinterpret_cast<Rect2*>(&r1); std::cout << r2->pos.x << std::endl; std::cout << r2->pos.y << std::endl; std::cout << r2->size.x << std::endl; std::cout << r2->size.y << std::endl; Rect3* r3 = reinterpret_cast<Rect3*>(&r1); std::cout << r3->pos.x << std::endl; std::cout << r3->pos.y << std::endl; std::cout << r3->size.x << std::endl; std::cout << r3->size.y << std::endl; } 
-3
source

All Articles