Share via


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 membros de classe e struct em seu valor de tamanho: bool e char em limites de 1 byte, short em limites de 2 bytes, int, long e float em limites de 4 bytes,long long, double e long double em limites de 8 bytes.

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

Endereços de alinhamento e memória

O alinhamento é uma propriedade de um endereço de memória, expresso como o módulo de endereço numérico de uma potência de 2. Por exemplo, o módulo 0x0001103F de endereço 4 é 3. Esse endereço e considerado como alinhado a 4n+3, onde 4 indica a potência de 2 escolhida. O alinhamento de um endereço depende da potência de 2 escolhida. O mesmo módulo 8 de endereço é 7. Um endereço é considerado como alinhado a X se seu alinhamento for Xn+0.

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

Tratamento do compilador de alinhamento de dados

Os compiladores tentam fazer alocações de dados de uma forma que impeça 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 de tipo long que são múltiplos de 4, definindo os 2 bits inferiores do endereço como zero.

O compilador também armazena estruturas de uma forma que alinham naturalmente cada elemento da estrutura. Considere as estruturas 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 naturalmente o alinhamento.

O exemplo de código a seguir mostra como o compilador coloca a estrutura preenchida 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];

As duas declarações retornam sizeof(struct x_) como 12 bytes.

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

  1. char _pad0[3] para alinhar o membro int b 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 preenchimento alinha os elementos de bar[3] uma maneira que permite acesso natural.

O exemplo de código a seguir mostra o layout da 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 e alignas

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

Exemplo

Você pode usar alignas em uma classe, struct ou união 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
}

Confira também

Alinhamento de estruturas de dados