Alineación

Una de las características de bajo nivel de C++ es la capacidad para especificar la alineación precisa de los objetos en la memoria para sacar el máximo partido de una arquitectura de hardware específica. De forma predeterminada, el compilador alinea los miembros de clase y estructura en su valor de tamaño: bool y char en límites de 1 byte, short en límites de 2 bytes, int, long y float en límites de 4 bytes y long long, double y long double en límites de 8 bytes.

En la mayoría de los escenarios no tendrá que preocuparse jamás por la alineación, puesto que la alineación predeterminada ya es óptima. No obstante, en algunos casos se pueden conseguir importantes mejoras de rendimiento o ahorros de memoria al especificar una alineación personalizada para sus estructuras de datos. Antes de Visual Studio 2015 podía usar las palabras clave __alignof y __declspec(align) específicas de Microsoft para especificar una alineación mayor que el valor predeterminado. Desde Visual Studio 2015 se deben usar las palabras clave estándar de C++11 alignof y alignas para aumentar al máximo la portabilidad de su código. Las nuevas palabras clave se comportan de la misma manera que las extensiones específicas de Microsoft. La documentación de esas extensiones también se aplica a las nuevas palabras clave. Para obtener más información, vea alignof Operador, alignas Especificador y alineación. El estándar de C++ no especifica el comportamiento de empaquetado para alinear en límites inferiores al valor predeterminado del compilador para la plataforma de destino, por lo que seguirá teniendo que usar el #pragma pack de Microsoft en ese caso.

Use la clase aligned_storage para la asignación de memoria de estructuras de datos con alineaciones personalizadas. La clase aligned_union consiste en especificar la alineación de uniones con constructores o destructores notriviales.

Alineación y direcciones de memoria

La alineación es una propiedad de una dirección de memoria, expresada como el módulo de la dirección numérica a una potencia de 2. Por ejemplo, la dirección 0x0001103F módulo 4 es 3. Esa dirección está alineada con 4n+3, donde 4 indica la potencia de 2 elegida. La alineación de una dirección depende de la potencia de 2 elegida. El mismo módulo de dirección 8 es 7. Se dice que una dirección está alineada con X si su alineación es Xn+0.

Las CPU ejecutan instrucciones que funcionan con datos almacenados en memoria. Los datos se identifican mediante sus direcciones en memoria. Un solo dato también tiene un tamaño. Un dato está alineado naturalmente si su dirección está alineada a su tamaño. De lo contrario, se llama desalineado. Por ejemplo, un dato de punto flotante de 8 bytes se encuentra alineado naturalmente si la dirección que se utiliza para identificarlo está alineada a 8.

Control del compilador de la alineación de datos

Los compiladores intentan realizar asignaciones de datos de forma que impidan la desalineación de datos.

Para los tipos de datos simples, el compilador asigna direcciones que son múltiplos del tamaño en bytes del tipo de datos. Por ejemplo, el compilador asigna direcciones a variables de tipo long que son múltiplos de 4, estableciendo los 2 bits inferiores de la dirección en cero.

El compilador también incluye estructuras de una manera que alinea de forma natural cada elemento de la estructura. Considere la estructura struct x_ en el siguiente ejemplo de código:

struct x_
{
   char a;     // 1 byte
   int b;      // 4 bytes
   short c;    // 2 bytes
   char d;     // 1 byte
} bar[3];

El compilador rellena esta estructura para aplicar la alineación de forma natural.

En el siguiente ejemplo de código se muestra cómo el compilador coloca la estructura rellenada en memoria:

// Shows the actual memory layout
struct x_
{
   char a;            // 1 byte
   char _pad0[3];     // padding to put 'b' on 4-byte boundary
   int b;            // 4 bytes
   short c;          // 2 bytes
   char d;           // 1 byte
   char _pad1[1];    // padding to make sizeof(x_) multiple of 4
} bar[3];

Ambas declaraciones devuelven sizeof(struct x_) como 12 bytes.

La segunda declaración incluye dos elementos de relleno:

  1. char _pad0[3] para alinear el miembro int b en un límite de 4 bytes.
  2. char _pad1[1] para alinear los elementos de matriz de la estructura struct _x bar[3]; en un límite de 4 bytes.

El relleno alinea los elementos de bar[3] de tal forma que permite el acceso natural.

En el código de ejemplo siguiente se muestra el diseño de matriz bar[3]:

adr offset   element
------   -------
0x0000   char a;         // bar[0]
0x0001   char pad0[3];
0x0004   int b;
0x0008   short c;
0x000a   char d;
0x000b   char _pad1[1];

0x000c   char a;         // bar[1]
0x000d   char _pad0[3];
0x0010   int b;
0x0014   short c;
0x0016   char d;
0x0017   char _pad1[1];

0x0018   char a;         // bar[2]
0x0019   char _pad0[3];
0x001c   int b;
0x0020   short c;
0x0022   char d;
0x0023   char _pad1[1];

alignof y alignas

El alignas especificador es una manera estándar portátil de C++ para especificar la alineación personalizada de variables y tipos definidos por el usuario. El operador alignof también es una forma estándar y portátil de obtener la alineación de una variable o un tipo especificado.

Ejemplo

Puede usar alignas en una clase, struct o union, o en miembros individuales. Cuando se encuentran varios alignas especificadores, el compilador elige el que tiene el valor más grande.

// alignas_alignof.cpp
// compile with: cl /EHsc alignas_alignof.cpp
#include <iostream>

struct alignas(16) Bar
{
    int i;       // 4 bytes
    int n;      // 4 bytes
    alignas(4) char arr[3];
    short s;          // 2 bytes
};

int main()
{
    std::cout << alignof(Bar) << std::endl; // output: 16
}

Consulte también

Alineación de estructuras de datos