0

I am trying to understand the size of a struct based on it's members as described a brief tutorial on data alignment (see here. The article states that "All data is naturally aligned within the structure. If type is located at address 0 the following double x is located with an offset of 8 at address 8 and so on. As a consequence the size of this structure is 48 even though it contains only 37 bytes of meaningful data. And yes, if float m follows directly after type the offset of m will be four and the size of the structure reduced to 40." The sentence

And yes, if float m follows directly after type the offset of m will be four and the size of the structure reduced to 40.

does not make sense to me. Why would the struct size be reduced to 40 just because the order of the members is changed? Is it because double vy is offset by 40 bytes and then an additional 8 bytes are padded resulting in a struct size of 48? While if float m follows directly after type, the offset of double vy is only 32 bytes, and then an additional 8 bytes are added resulting in a struct size of 40?

enter image description here

I have written a small C program to double check the alignments of the struct members:

/*Outputs:
particle one alignment:
type 0
x 8
y 16
m 24
vx 32
vy 40
struct size --> 48
particle two alignment:
type 0
m 4
x 8
y 16
vx 24
vy 32
struct size --> 40
*/
#include <stdio.h>                                                                           
#include <stdlib.h>                                                                          
                                                                                             
typedef struct ParticleOne {                                                                 
    char type;                                                                               
    double x,y;                                                                              
    float m;         // after `y` member                                                                        
    double vx, vy;                                                                           
} ParticleOne;                                                                               
                                                                                             
typedef struct ParticleTwo {                                                                 
    char type;                                                                               
    float m;         // after `type` member                                                                        
    double x,y;                                                                              
    double vx, vy;                                                                           
} ParticleTwo;                                                                               
                                                                                             
int main() {                                                                                 
    ParticleOne *particle_one = malloc(sizeof(ParticleOne));                                 
    ParticleTwo *particle_two = malloc(sizeof(ParticleTwo));                                 
                                                                                             
    // Particle one alignment/offset information                                                                          
    void *p1_type = &particle_one->type;                                                     
    void *p1_x = (void*)&particle_one->x - p1_type;                                          
    void *p1_y = (void*)&particle_one->y - p1_type;                                          
    void *p1_m = (void*)&particle_one->m - p1_type;                                          
    void *p1_vx = (void*)&particle_one->vx - p1_type;
    void *p1_vy = (void*)&particle_one->vy - p1_type;

    printf("particle one alignment:\n");
    printf("type %d\n", p1_type - p1_type);
    printf("x %d\n", p1_x);
    printf("y %d\n", p1_y);
    printf("m %d\n", p1_m);
    printf("vx %d\n", p1_vx);
    printf("vy %d\n", p1_vy);
    printf("struct size --> %d\n", sizeof(ParticleOne));    

    // Particle two alignment/offset information
    void *p2_type = &particle_two->type;
    void *p2_x = (void*)&particle_two->x - p2_type;
    void *p2_y = (void*)&particle_two->y - p2_type;
    void *p2_m = (void*)&particle_two->m - p2_type;
    void *p2_vx = (void*)&particle_two->vx - p2_type;
    void *p2_vy = (void*)&particle_two->vy - p2_type;

    printf("particle two alignment:\n");
    printf("type %d\n", p2_type - p2_type);
    printf("m %d\n", p2_m);
    printf("x %d\n", p2_x);
    printf("y %d\n", p2_y);
    printf("vx %d\n", p2_vx);
    printf("vy %d\n", p2_vy);
    printf("struct size --> %d\n", sizeof(ParticleTwo));

    return 0;
}
Jared Frazier
  • 413
  • 1
  • 4
  • 10
  • Does this answer your question? [Why isn't sizeof for a struct equal to the sum of sizeof of each member?](https://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member) – Brian61354270 Mar 25 '23 at 16:17
  • General rule of thumb: sort largest to smallest when possible... add nuance if you have sub-structs that aren't powers of 2.... generally helps with speed too - especially if you use a struct packing compiler option – technosaurus Mar 28 '23 at 08:40

2 Answers2

4

With the original byte order, we need:

  • One byte for char type.
  • Seven bytes to make the following double aligned to a multiple of eight bytes.
  • 16 bytes, eight for each of x and y in double x,y.
  • Four bytes for float m.
  • Four bytes to make the following float aligned to a multiple of eight bytes.
  • 16 bytes, eight for each of vx and vy in double vx,vy.

That is 1+7+16+4+4+16 = 48 bytes.

With float moved, we need:

  • One byte for char type.
  • Three bytes to make the following float aligned to a multiple of four bytes.
  • Four bytes for float m.
  • 16 bytes, eight for each of x and y in double x,y.
  • 16 bytes, eight for each of vx and vy in double vx,vy.

That is 1 + 3 + 4 + 16 + 16 = 40 bytes.

Observe that no bytes were needed between the float and the double because the float ended at a multiple of eight bytes.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
0

the answer you're looking for is actually mentioned in the article you linked:

For example, an 8-byte floating-point datum is naturally aligned if the address used to identify it is aligned to eight (8)

If typeis the second member, the struct already occupies 1 byte, so the next position available for natural alignment is the next multiple of eight. You're essentially skipping 7 bytes for the sake of natural alignment, which can fit a whole float. Since float m only requires 4 bytes, it can be naturally aligned between typeand x.

RawkFist
  • 474
  • 5
  • 12