Hay que tener en cuenta que el alineamiento en memoria depende de la arquitectura, y ademas la implementación por motivos de eficiencia aplica sus rellenos (pading) a las estructuras. Asumiendo que el alineamiento es de 4 bytes tipico en x86-64 y (en mi caso) eliminando este y el pading "forzando" al compilador GCC con la clausula __packed__.
Te muestro unas rutinas en C (GCC) sencillas que indican los valores que solicitas.
#include <stdio.h>
union VALOR
{
char a[10];
double b;
};
struct DATO
{
int a;
double b;
union VALOR c[10];
float d;
char e;
}__attribute__((__packed__));
struct DATO alfa;
int main(void)
{
int begin = (int) &alfa; /* 1 */
int item = (int) &alfa.c[1].a[1]; /* 2 */
printf("%p --> %p\n", (void*) begin, (void*) item);
printf("Offset == %d bytes\n", item - begin);
return 0;
}
/*
1 -> direccion de inicio del objeto tipo DATO
2 -> dirección de memoria del segundo elemento de a, correspondiente al segundo elemento de c
*/Prueba a eliminar el compactado de la estructura y coteja los resultados.
Por otra parte hacer la suma con los tamaños de cada tipo es facil, el único plus que tienes que tener en cuenta es con el tamaño de una union, este es igual al registro, campo y/o tipo más grande que contuviera, y el de una array automático es (formula valida como instrucción): sizeof(nombre_array) / sizeof(T), donde T es el tipo (char, int, double, TADS, etc) del array.
Un saludo.