That's a practice inherited from C. But your first question is unrelated.
Why 8 bytes
The day where memory was physically organized in bytes has gone with the 8bit microprocessors. Processor with larger "words" don't move bytes as their native unit. Accessing individual bytes, in fact, requires to the processor a masking and a shift. To avid this "penality" individual struct fields are "aligned" on a "processor word boundary". For 32 bit processor this is 4 bytes.
Your struct is 8 bytes long because 4 serves the
int x
, and other 4 serves
char y
(1 is the char, the other 3 are padding).
What's the zero size
C is not "strongly typed" as C++ is, and has no "support for dynamic structures".
The zero sized array is essentially a way to allow expression like
C.z[3]
allowing you to access the 4
rd (3+1) int after the struct beginning address.
In your case (where C is on-stack) this will write something else (causing undefined behavior)
But with dynamic memory, the idea is something like
xyz* ptr = (xyx*)malloc(sizeof(xyz)+4*sieof(int));
new(ptr) xyz;
new(&ptr->z)int[4];
ptr->x = 1;
ptr->y = 'a';
ptr->z[0] = 10;
ptr->z[1] = 11;
ptr->z[2] = 12;
ptr->z[3] = 13;
for(i=0;i<4;++i) p->z[i].~int();
ptr->~xyx();
free(ptr);
Today this is anymore
valid C++ code for a variety of reasons (
malloc
allocates memory without contruction, so in-place construction and explicit in-place destruction are required, in case xyz contains somting other than plain data, like object requiring construction), but to access low level stuff where the memory has to be layed-out in a given way, can still be in use.
More modern C++ code will probably define your struct like
#include <vector>
struct xyz
{
int x;
char y;
std::vector<int> z;
explicit xyz(size_t vectsize)
:x(), y(), z(vectsize)
{}
};
int main()
{
xyz C(4);
C.x = 1;
C.y = 'a';
c.z[0] = 10;
c.z[1] = 20;
c.z[2] = 30;
c.z[3] = 40;
return 0;
}
But the resultingmemory layout will be completely different (std::vector contains just "pointers" to a dynamically allocated memory)