Alinhamento

Um dos recursos de baixo nível do C++ é a capacidade de especificar o alinhamento preciso de objetos na memória para tirar o máximo proveito de uma arquitetura de hardware específica. Por padrão, o compilador alinha os membros de classe e struct em seu valor de tamanho: bool e em limites de 1 byte, short em limites de 2 bytes, int, long, e float em limites de 4 bytes, e long long, doublee long double em limites de 8 byteschar.

Na maioria dos cenários, você nunca precisa se preocupar com o alinhamento porque o alinhamento padrão já é o ideal. Em alguns casos, no entanto, você pode obter melhorias significativas de desempenho ou economia de memória especificando um alinhamento personalizado para suas estruturas de dados. Antes do Visual Studio 2015, você podia usar as palavras-chave __alignof específicas da Microsoft e __declspec(align) especificar um alinhamento maior do que o padrão. A partir do Visual Studio 2015, você deve usar as palavras-chave alignof padrão C++11 e alignas para portabilidade máxima de código. As novas palavras-chave se comportam da mesma forma sob o capô que as extensões específicas da Microsoft. A documentação para essas extensões também se aplica às novas palavras-chave. Para obter mais informações, consulte alignof Operador, alignas especificador e alinhar. O padrão C++ não especifica o comportamento de empacotamento para alinhamento em limites menores do que o padrão do compilador para a plataforma de destino, portanto, você ainda precisa usar a Microsoft #pragma pack nesse caso.

Use a classe aligned_storage para alocação de memória de estruturas de dados com alinhamentos personalizados. A classe aligned_union é para especificar o alinhamento para uniões com construtores ou destruidores não triviais.

Alinhamento e endereços de memória

Alinhamento é uma propriedade de um endereço de memória, expressa como o módulo de endereço numérico uma potência de 2. Por exemplo, o endereço 0x0001103F módulo 4 é 3. Diz-se que esse endereço está alinhado com 4n+3, onde 4 indica a potência escolhida de 2. O alinhamento de um endereço depende do poder escolhido de 2. O mesmo endereço modulo 8 é 7. Diz-se que um endereço está alinhado a X se o seu alinhamento for Xn+0.

As CPUs executam instruções que operam em dados armazenados na memória. Os dados são identificados pelos seus endereços na memória. Um único dado também tem um tamanho. Chamamos um dado naturalmente alinhado se seu endereço estiver alinhado ao seu tamanho. Chama-se desalinhado de outra forma. Por exemplo, um dado de ponto flutuante de 8 bytes é naturalmente alinhado se o endereço usado para identificá-lo tiver um alinhamento de 8 bytes.

Tratamento do compilador do alinhamento de dados

Os compiladores tentam fazer alocações de dados de uma forma que evite o desalinhamento de dados.

Para tipos de dados simples, o compilador atribui endereços que são múltiplos do tamanho em bytes do tipo de dados. Por exemplo, o compilador atribui endereços a variáveis do tipo long que são múltiplos de 4, definindo os 2 bits inferiores do endereço como zero.

O compilador também preenche estruturas de uma forma que alinha naturalmente cada elemento da estrutura. Considere a estrutura struct x_ no exemplo de código a seguir:

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

O compilador preenche essa estrutura para impor o alinhamento naturalmente.

O exemplo de código a seguir mostra como o compilador coloca a estrutura acolchoada na memória:

// 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 as declarações retornam sizeof(struct x_) como 12 bytes.

A segunda declaração inclui dois elementos de acolchoamento:

  1. char _pad0[3] para alinhar o int b membro em um limite de 4 bytes.
  2. char _pad1[1] para alinhar os elementos de matriz da estrutura struct _x bar[3]; em um limite de 4 bytes.

O acolchoamento alinha os elementos de bar[3] uma forma que permite o acesso natural.

O exemplo de código a seguir mostra o layout da bar[3] matriz:

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 e alignas

O alignas especificador é uma maneira portátil padrão de C++ para especificar o alinhamento personalizado de variáveis e tipos definidos pelo usuário. O alignof operador é igualmente uma forma padrão e portátil de obter o alinhamento de um tipo ou variável especificados.

Exemplo

Você pode usar alignas em uma classe, struct ou sindicato, ou em membros individuais. Quando vários alignas especificadores são encontrados, o compilador escolhe aquele com o maior valor.

// 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
}

Ver também

Alinhamento da estrutura de dados