Según la RAE Sinergia es : «Acción de dos o más causas cuyo efecto es superior a la suma de los efectos individuales.». Vamos que la suma del todo es mayor que el de las partes. Recientemente me he topado (otra vez) con este efecto programando, cómo somos viejos conocidos os lo voy a presentar. Se llama padding y suele venir de serie en las estructuras y clases de C o C++.

Veamos un ejemplo:

typedef struct
{
    float f1;
    float f2;
} Struct1;
typedef struct
{
    char  c;
    float  f;
} Struct2;

Tenemos dos estructuras Struct1 y Struct2, una tiene dos floats y la otra un float y un char. La primera, debería ocupar 8 bytes (2×4 bytes) y la segunda 5 bytes (4+1 bytes). Pero el caso es que el compilador elige que no sea así, comprobémoslo con este pequeño programa en C:

#include <stdio.h>
int main(int argc, char **argv)
{
    Struct1 test1[10];
    Struct2 test2[10];
    printf("\n-- Test with Struct1 (2 floats) -----------------\n");
    printf("Size of the structure Struct1 = %u bytes (%u)\n",
        sizeof(Struct1), sizeof(float)+sizeof(float) );
    printf("Struct1[0] = %p\n", test1);
    printf("Struct1[1] = %p\n", test1 + 1);
    printf("Distance = %u\n", (unsigned int)(test1+1) - (unsigned int)(test1));
    printf("\n-- Test width Strruct2 ( 1 float + 1 char)-------\n");
    printf("Size of the structure Struct2 = %u bytes (%u)\n",
        sizeof(Struct2), sizeof(float)+sizeof(char));
    printf("Struct2[0] = %p\n", test2);
    printf("Struct2[1] = %p\n", test2 + 1);
    printf("Distance = %u\n", (unsigned int)(test2+1) - (unsigned int)(test2));
    printf("\n-- Final Test -----------------------------------\n");
    printf(" (sizeof(Struct1) == sizeof(Struct2)) --> %s\n",
        ( sizeof(Struct1) == sizeof(Struct2) ) ? "true" : "false");
    return 0;
}

El resultado de ejecutarlo es el siguiente:

pplux@Matrix:~/tmp$ gcc test.c -Wall
pplux@Matrix:~/tmp$ ./a.out

-- Test with Struct1 (2 floats) -----------------
Size of the structure Struct1 = 8 bytes (8)
Struct1[0] = 0xbf9f9478
Struct1[1] = 0xbf9f9480
Distance = 8

-- Test width Strruct2 ( 1 float + 1 char)-------
Size of the structure Struct2 = 8 bytes (5)
Struct2[0] = 0xbf9f9428
Struct2[1] = 0xbf9f9430
Distance = 8

-- Final Test -----------------------------------
 (sizeof(Struct1) == sizeof(Struct2)) --> true

El compilador decide que las dos estructuras tengan el mismo tamaño, aunque en realidad la segunda desperdicia 3 bytes. Esto lo hace para que los accesos a memoria estén alineados, y lo mejor de todo, en cada arquitectura puede ocurrir una cosa diferente.

Más aún, si medimos dentro de Struct2 dónde empieza el char y dónde empieza el float, tal vez nos llevemos una sorpresa. Podríamos pensar que «c» ocupará el primer byte, y que acontinuación vienen 4 bytes que forman el float y que los otros 3 bytes que el compilador mete de relleno, lo hace por el padding.

Si ejecutamos este pequeño código, podremos medir a qué distancia está cada elemento desde el principio del struct:

    printf("Distance of c %u\n", (unsigned int)(&test2[0].c) - (unsigned int)(test2));
    printf("Distance of f %u\n", (unsigned int)(&test2[0].f) - (unsigned int)(test2));

El resultado es que efectivamente c empieza en el byte 0, pero f lo hace en el 4. Es decir, los 3 bytes de padding están entre el campo «c» y el campo «f», y no al final de la estructura a modo de relleno. El compilador, muy listo, introduce huecos deliberadamente dentro del struct para asegurar que el acceso a cada uno de sus campos, es un acceso alineado.

Lo se, lo se, esto en el fondo es una gran-chorrada&#153, no importa tener bytes fantasmas por ahí, ni nos preocupa por lo general. Pero basta que algún día te declares un struct con tus campos (que seguramente no serán ni dos floats ni un char) que no tengas en cuenta el padding y hagas un cast burro, o un memcpy en plan hack-eficiente. En ese momento las cosas pueden empezar a torcerse 😉