1

I have the following class:

struct xyzid{
  uint16_t i;
  uint16_t z,y,x;
};
      
std::vector<xyzid> example;

(...) // fill example

uint16_t* data = reinterpret_cast<uint16_t*>(example.data());

Can I now be sure that my pointer data is basically such that the first 16 bits refer to i, then z, y, x, before moving to the next element? Or, is it not guaranteed that the order of declaration in my struct is preserved within my std::vector container?

AndyG
  • 39,700
  • 8
  • 109
  • 143
ATK
  • 1,296
  • 10
  • 26
  • 3
    Short answer: No. It'll probably work, but your in undefined behavior land. – NathanOliver Oct 05 '22 at 19:42
  • Possible dupe: https://stackoverflow.com/questions/73904491/in-c-is-it-valid-to-treat-scalar-members-of-a-struct-as-if-they-comprised-an – SuperStormer Oct 05 '22 at 19:43
  • how is the structure then defined.? – ATK Oct 05 '22 at 19:47
  • 1
    You're not guaranteed that even if you were just looking at a single struct, because the compiler might add padding for performance reasons (aligned access can be faster that nonaligned access). A typical (non-standard but works in Visual C++ and g++ at least) way of doing this is #pragma pack, but even that may not necessarily work the same way between compilers, if it works at all. – George Oct 05 '22 at 19:50
  • 2
    It's laid out as you expect, with `i` followed by `z`, then `y`, then `x`. The issue is the compiler is free to add padding between any of the elements if it wants to. If it does then you start reading those padding bits, which have an undefined value. – NathanOliver Oct 05 '22 at 19:50
  • That is why I chose to add four 16 bits integers? But then you always do packing to ensure that compiler dont add padding? – ATK Oct 05 '22 at 19:52
  • *"how is the structure then defined.?*" - There is no one structure definition to rule them all. That's why you are told it's **undefined**. – StoryTeller - Unslander Monica Oct 05 '22 at 19:52
  • 1
    @ATK it doesn't matter if you use 16-bit integers or 32-bit integers or 64-bit integers, the only way there would not be any padding between the fields is if the struct's alignment is declared equal to, or smaller than, the size of the integers. So, in your example, if the struct's alignment is 8-bit or 16-bit, then their likely won't be any padding. But you really shouldn't rely on this behavior if you can help it. If you need the memory to have an exact layout, use serialization instead – Remy Lebeau Oct 05 '22 at 19:59
  • If you want many `uint16_t` data items one after the other without padding, such that you can access each one by its number in the sequence, such as `something[42]`, use `std::vector`. That's what `vector` is for. If you want to access individual elements by name, such as `something.x` or `something_else->i`, use a `struct`. That's what `struct` is for. It is not interesting or important to know how exactly a `struct` is laid in memory unless you are writing a compiler or a similar toolchain component. If you think you do need to know, please explain why. – n. m. could be an AI Oct 05 '22 at 20:07

1 Answers1

6

The vector contains an allocated array of structs. The data pointer points at the 1st struct in the array. The structs in the array are stored sequentially in memory. And the fields of each struct are also stored sequentially in memory, in the order that they are declared. So yes, the array will consist of the 1st struct fields i, then z, then y, then x, then the 2nd struct fields, and so on.

However, what you CAN'T count on is that the 1st struct's z field will occupy the 2nd 16 bits of memory, or the y field will occupy the 3rd 16 bits of memory, etc. This is dependent on the struct's alignment padding. If you are expecting the memory to be filled in that way, the only way to guarantee that is to disable all alignment padding between the structs and their fields, by declaring the struct's alignment as 8-bit/1-byte, such as with #pragma pack(1) or __attribute(packed) or other similar compiler directive.

But, you really shouldn't rely on this behavior if you can help it. If you absolutely need the memory to have an exact layout, use serialization instead.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    +1. For a standard layout class like OP has, the standard only guarantees there's no padding before the first member of the class (that is, a pointer to an instance of the class can be safely reinterpreted as a pointer to the first member of the class). – AndyG Oct 05 '22 at 19:59