Considerar:
struct mystruct_A
{
char a;
int b;
char c;
} x;
struct mystruct_B
{
int b;
char a;
} y;
Os tamanhos das estruturas são 12 e 8, respectivamente.
Essas estruturas são acolchoadas ou embaladas?
Quando ocorre o preenchimento ou a embalagem?
padding
torna as coisas maiores.packing
torna as coisas menores. Totalmente diferente.Respostas:
O preenchimento alinha os membros da estrutura aos limites de endereço "naturais" - digamos, os
int
membros teriam compensações, que estãomod(4) == 0
na plataforma de 32 bits. O preenchimento está ativado por padrão. Ele insere as seguintes "lacunas" em sua primeira estrutura:A embalagem , por outro lado, impede que o compilador faça preenchimento - isso deve ser explicitamente solicitado - no GCC, é
__attribute__((__packed__))
o seguinte:produziria estrutura de tamanho
6
em uma arquitetura de 32 bits.Uma observação: o acesso à memória desalinhada é mais lento em arquiteturas que permitem (como x86 e amd64) e é explicitamente proibido em arquiteturas de alinhamento estrito como SPARC.
fonte
( As respostas acima explicaram o motivo com bastante clareza, mas não parecem totalmente claras sobre o tamanho do preenchimento, portanto, adicionarei uma resposta de acordo com o que aprendi com The Lost Art of Structure Packing , que evoluiu para não se limitar a
C
, mas também aplicável aGo
,Rust
. )Alinhamento de memória (para struct)
Regras:
por exemplo, no sistema de 64 bits,
int
deve começar no endereço divisível por 4 elong
por 8,short
por 2.char
echar[]
são especiais, podem ser qualquer endereço de memória; portanto, eles não precisam de preenchimento antes deles.struct
, além da necessidade de alinhamento de cada membro individual, o tamanho da estrutura inteira será alinhado com um tamanho divisível pelo tamanho do maior membro individual, preenchendo o final.por exemplo, se o maior membro do struct for
long
divisível por 8,int
depois por 4 eshort
depois por 2.Ordem do membro:
stu_c
estu_d
do exemplo abaixo têm os mesmos membros, mas em ordem diferente e resultam em tamanho diferente para as 2 estruturas.Endereço na memória (para struct)
Regras:
endereço estrutural começa a partir de
(n * 16)
bytes. ( Você pode ver no exemplo abaixo, todos os endereços hexadecimais impressos das estruturas terminam com0
. )Motivo : o maior membro individual possível da estrutura é de 16 bytes (
long double
).char
membro, seu endereço poderá começar em qualquer endereço.Espaço vazio :
exemplo,
test_struct_address()
abaixo, a variávelx
reside entre estruturasg
e adjacentesh
.Não importa se
x
é declarado,h
o endereço do endereço não será alterado,x
apenas reutilize o espaço vazio que foig
desperdiçado.Caso semelhante para
y
.Exemplo
( para sistema de 64 bits )
memory_align.c :
Resultado da execução -
test_struct_padding()
:Resultado da execução -
test_struct_address()
:Assim, o início do endereço para cada variável é g: d0 x: dc h: e0 y: e8
fonte
<The Lost Art of C Structure Packing>
explica muito bem as regras, mesmo que seja um pouco mais longo do que esta resposta. O livro está disponível gratuitamente on-line: catb.org/esr/structure-packingSei que essa pergunta é antiga e a maioria das respostas aqui explica muito bem o preenchimento, mas, ao tentar entendê-lo, achei que ter uma imagem "visual" do que está acontecendo ajudou.
O processador lê a memória em "pedaços" de tamanho definido (palavra). Digamos que a palavra do processador tenha 8 bytes. Ele olhará para a memória como uma grande linha de blocos de construção de 8 bytes. Sempre que precisar obter algumas informações da memória, atingirá um desses blocos e as obterá.
Como parece na imagem acima, não importa onde esteja um caractere (1 byte de comprimento), pois ele estará dentro de um desses blocos, exigindo que a CPU processe apenas 1 palavra.
Quando lidamos com dados maiores que um byte, como 4 bytes int ou 8 bytes duplos, a maneira como eles estão alinhados na memória faz a diferença em quantas palavras terão que ser processadas pela CPU. Se os blocos de 4 bytes estiverem alinhados de uma maneira, eles sempre se encaixam no interior de um bloco (o endereço de memória é múltiplo de 4) apenas uma palavra terá que ser processada. Caso contrário, um pedaço de 4 bytes pode ter parte de si mesmo em um bloco e parte de outro, exigindo que o processador processe 2 palavras para ler esses dados.
O mesmo se aplica a um duplo de 8 bytes, mas agora ele deve estar em um endereço de memória múltiplo de 8 para garantir que ele esteja sempre dentro de um bloco.
Isso considera um processador de texto de 8 bytes, mas o conceito se aplica a outros tamanhos de palavras.
O preenchimento funciona preenchendo as lacunas entre esses dados para garantir que eles estejam alinhados com esses blocos, melhorando assim o desempenho durante a leitura da memória.
No entanto, como indicado em outras respostas, às vezes o espaço importa mais do que o desempenho propriamente dito. Talvez você esteja processando muitos dados em um computador que não possui muita RAM (o espaço de troca pode ser usado, mas é MUITO mais lento). Você pode organizar as variáveis no programa até que o menor preenchimento seja feito (como foi bastante exemplificado em algumas outras respostas), mas se isso não for suficiente, você poderá desabilitar explicitamente o preenchimento, que é o que é o empacotamento .
fonte
O empacotamento da estrutura suprime o preenchimento da estrutura, o preenchimento usado quando o alinhamento é mais importante, o empacotamento usado quando o espaço é mais importante.
Alguns compiladores fornecem
#pragma
para suprimir o preenchimento ou torná-lo compactado em n número de bytes. Alguns fornecem palavras-chave para fazer isso. Geralmente, o pragma usado para modificar o preenchimento da estrutura estará no formato abaixo (depende do compilador):Por exemplo, o ARM fornece a
__packed
palavra-chave para suprimir o preenchimento da estrutura. Consulte o manual do compilador para saber mais sobre isso.Portanto, uma estrutura compactada é uma estrutura sem preenchimento.
Estruturas geralmente compactadas serão usadas
economizar espaço
formatar uma estrutura de dados para transmitir pela rede usando algum protocolo (essa não é uma boa prática, é claro, porque você precisa
lidar com endianness)
fonte
Preenchimento e embalagem são apenas dois aspectos da mesma coisa:
Na
mystruct_A
suposição de um alinhamento padrão de 4, cada membro é alinhado em um múltiplo de 4 bytes. Como o tamanho dechar
é 1, o preenchimento paraa
ec
é de 4 a 1 = 3 bytes, enquanto não é necessário preenchimento para oint b
qual já existem 4 bytes. Funciona da mesma maneira paramystruct_B
.fonte
O empacotamento da estrutura é feito somente quando você instrui explicitamente o compilador para compactar a estrutura. Preenchimento é o que você está vendo. Seu sistema de 32 bits está preenchendo cada campo para alinhar as palavras. Se você tivesse dito ao seu compilador para compactar as estruturas, elas teriam 6 e 5 bytes, respectivamente. Não faça isso embora. Não é portátil e faz com que os compiladores gerem código muito mais lento (e às vezes até com bugs).
fonte
Não há mas sobre isso! Quem quiser entender o assunto deve fazer o seguinte,
fonte
Regras para preenchimento:
Por que regra 2: considere a seguinte estrutura,
Se criarmos uma matriz (de 2 estruturas) dessa estrutura, nenhum preenchimento será necessário no final:
Portanto, tamanho de struct = 8 bytes
Suponha que devemos criar outra estrutura como abaixo:
Se criarmos uma matriz dessa estrutura, existem 2 possibilidades, do número de bytes de preenchimento necessário no final.
A. Se adicionarmos 3 bytes no final e o alinharmos por int e não por muito tempo:
B. Se adicionarmos 7 bytes no final e alinhá-lo por muito tempo:
O endereço inicial da segunda matriz é um múltiplo de 8 (ou seja, 24). O tamanho da estrutura = 24 bytes
Portanto, alinhando o endereço inicial da próxima matriz da estrutura a um múltiplo do maior membro (ou seja, se criarmos uma matriz dessa estrutura, o primeiro endereço da segunda matriz deve começar em um endereço que seja múltiplo do maior membro da estrutura.Aqui está 24 (3 * 8)), podemos calcular o número de bytes de preenchimento necessários no final.
fonte
O alinhamento da estrutura de dados é a maneira como os dados são organizados e acessados na memória do computador. Consiste em duas questões separadas, mas relacionadas: alinhamento de dados e preenchimento da estrutura de dados . Quando um computador moderno lê ou grava em um endereço de memória, ele faz isso em blocos do tamanho de palavras (por exemplo, blocos de 4 bytes em um sistema de 32 bits) ou maior. Alinhamento de dados significa colocar os dados em um endereço de memória igual a alguns múltiplos do tamanho da palavra, o que aumenta o desempenho do sistema devido à maneira como a CPU lida com a memória. Para alinhar os dados, pode ser necessário inserir alguns bytes sem sentido entre o final da última estrutura de dados e o início da próxima, que é o preenchimento da estrutura de dados.
fonte