Pergunta

Por que o operador sizeof retornar um tamanho maior para uma estrutura que o tamanho total de membros da estrutura?

Foi útil?

Solução

Isto é por causa de acolchoamento adicionado para satisfazer as restrições de alinhamento. impactos alinhamento de estrutura de dados tanto de desempenho e correção de programas:

    acesso
  • Mis-alinhado pode ser um erro de disco (muitas vezes SIGBUS).
  • Acesso Mis-alinhado pode ser um erro de software.
    • De qualquer corrigido no hardware, para um desempenho de degradação modesto.
    • Ou corrigido por emulação de software, para um desempenho de degradação grave.
    • Além disso, a atomicidade e outros simultaneidade-garantias pode ser quebrado, levando a erros sutis.

Aqui está um exemplo usando as configurações típicas para um processador x86 (todos utilizados 32 e 64 modos bit):

struct X
{
    short s; /* 2 bytes */
             /* 2 padding bytes */
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 3 padding bytes */
};

struct Y
{
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
    short s; /* 2 bytes */
};

struct Z
{
    int   i; /* 4 bytes */
    short s; /* 2 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
};

const int sizeX = sizeof(struct X); /* = 12 */
const int sizeY = sizeof(struct Y); /* = 8 */
const int sizeZ = sizeof(struct Z); /* = 8 */

Pode-se minimizar o tamanho das estruturas por triagem por membros de alinhamento (a classificação por tamanho de sufixos para que em tipos básicos) (como a estrutura Z no exemplo acima).

NOTA IMPORTANTE: Tanto o C e C ++ estado padrão que alinhamento estrutura é definida pela implementação. Por conseguinte, cada compilador podem escolher dados de alinhamento de forma diferente, resultando em diferentes e incompatíveis layouts de dados. Por esta razão, quando se lida com bibliotecas que serão utilizados por diferentes compiladores, é importante entender como os compiladores alinhar dados. Alguns compiladores têm configurações de linha de comando e / ou declarações #pragma especiais para alterar as configurações de alinhamento estrutura.

Outras dicas

embalagem e o alinhamento de bytes, tal como descrito no C FAQ aqui :

É para o alinhamento. Muitos processadores não pode acessar 2- e 4-byte quantidades (por exemplo ints e ints longo) se eles estão amontoados em todos os-que-way.

Suponha que você tenha esta estrutura:

struct {
    char a[3];
    short int b;
    long int c;
    char d[3];
};

Agora, você poderia pensar que ele deve ser possível para embalar este estrutura na memória como esta:

+-------+-------+-------+-------+
|           a           |   b   |
+-------+-------+-------+-------+
|   b   |           c           |
+-------+-------+-------+-------+
|   c   |           d           |
+-------+-------+-------+-------+

Mas é muito, muito mais fácil sobre o processador, se os arranjos do compilador assim:

+-------+-------+-------+
|           a           |
+-------+-------+-------+
|       b       |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           |
+-------+-------+-------+

Na versão embalada, observe como ele é, pelo menos, um pouco difícil para você e eu para ver como os campos B e C envolver em torno de? Em poucas palavras, é difícil para o processador, também. Portanto, a maioria dos compiladores pad vontade a estrutura (como se com campos extras, invisíveis) como este:

+-------+-------+-------+-------+
|           a           | pad1  |
+-------+-------+-------+-------+
|       b       |     pad2      |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           | pad3  |
+-------+-------+-------+-------+

Se você quer a estrutura de ter um certo tamanho com GCC, por exemplo, o uso __attribute__((packed)) .

No Windows, você pode definir o alinhamento para um byte quando se utiliza o Compier cl.exe com o / Zp opção .

Normalmente, é mais fácil para o CPU para acessar dados que é um múltiplo de 4 (ou 8), dependendo da plataforma e também sobre o compilador.

Por isso, é uma questão de alinhamento basicamente.

Você precisa ter boas razões para mudá-lo.

Isto pode ser devido ao alinhamento byte e preenchimento para que a estrutura sai para um número par de bytes (ou palavras) em sua plataforma. Por exemplo, em C no Linux, as seguintes 3 estruturas:

#include "stdio.h"


struct oneInt {
  int x;
};

struct twoInts {
  int x;
  int y;
};

struct someBits {
  int x:2;
  int y:6;
};


int main (int argc, char** argv) {
  printf("oneInt=%zu\n",sizeof(struct oneInt));
  printf("twoInts=%zu\n",sizeof(struct twoInts));
  printf("someBits=%zu\n",sizeof(struct someBits));
  return 0;
}

têm membros que há de tamanhos (em bytes) são 4 bytes (32 bits), 8 bytes (2x 32 bits) e 1 byte (2 + 6 bits), respectivamente. O programa acima (no Linux usando o gcc) imprime os tamanhos como 4, 8 e 4 -. Onde a última estrutura é tão acolchoado que é uma única palavra (4 x 8 bit bytes em minha plataforma de 32 bits)

oneInt=4
twoInts=8
someBits=4

Veja também:

para Microsoft Visual C:

http://msdn.microsoft .com / en-us / library / 2e70t5y1% 28v = vs.80% 29.aspx

e GCC compatibilidade reivindicação com o compilador da Microsoft:.

http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking -Pragmas.html

Além das respostas anteriores, por favor nota que, independentemente da embalagem, não há membros de ordem de garantia em C ++ . Compiladores pode (e certamente não) adicionar membros ponteiro mesa e bases estruturas virtuais para a estrutura. Mesmo a existência da tabela virtual não é assegurada pelo padrão (implementação mecanismo virtual não é especificado) e, portanto, pode-se concluir que tal garantia é simplesmente impossível.

Tenho certeza membro ordem é garantida em C , mas eu não contaria com isso, ao escrever um programa multi-plataforma ou cross-compiler .

O tamanho de uma estrutura é maior que a soma de suas partes por causa do que é chamado de embalagem. Um processador particular, tem um tamanho de dados preferida que funciona com. tamanho preferido da maioria dos processadores modernos se 32-bits (4 bytes). Acessando a memória quando os dados é sobre este tipo de fronteira é mais eficiente do que as coisas que pernalta que o tamanho limite.

Por exemplo. Considere a estrutura simples:

struct myStruct
{
   int a;
   char b;
   int c;
} data;

Se a máquina é uma máquina de 32 bits e de dados é alinhado com um limite de 32 bits, vemos um problema imediato (assumindo que não há estrutura de alinhamento). Neste exemplo, vamos supor que os dados de estrutura começa no endereço 1024 (0x400 - note que os menores de 2 bits são zero, para que os dados está alinhada a um limite de 32-bit). O acesso a data.a vai funcionar bem porque começa em um limite - 0x400. O acesso a data.b também irá funcionar bem, porque é no endereço 0x404 - um outro limite de 32-bit. Mas uma estrutura desalinhada colocaria data.c no endereço 0x405. Os 4 bytes de data.c são a 0x405, 0x406, 0x407, 0x408. Em uma máquina de 32 bits, o sistema iria ler data.c durante um ciclo de memória, mas só iria pegar 3 dos 4 bytes (o quarto byte é o próximo limite). Assim, o sistema teria que fazer um segundo acesso à memória para obter o quarto byte,

Agora, se em vez de colocar data.c no endereço 0x405, o compilador preenchido a estrutura por 3 bytes e colocar data.c no endereço 0x408, então o sistema seria só precisa de 1 ciclo para ler os dados, reduzindo o tempo de acesso a que elemento de dados por 50%. a eficiência da memória swaps de preenchimento para a eficiência de processamento. Dado que os computadores podem ter enormes quantidades de memória (muitos gigabytes), os compiladores sentir que o swap (velocidade sobre o tamanho) é razoável.

Infelizmente, este problema torna-se um assassino quando você tentar enviar estruturas através de uma rede ou até mesmo gravar os dados binários em um arquivo binário. O preenchimento inserido entre os elementos de uma estrutura ou classe pode perturbar os dados enviados para o arquivo ou rede. Para escrever código portátil (um que vai para vários compiladores diferentes), você provavelmente terá que acessar cada elemento da estrutura separadamente para assegurar o bom "embalagem".

Por outro lado, compiladores diferentes têm diferentes habilidades para gerenciar estrutura de dados de embalagem. Por exemplo, no Visual C / C ++ o compilador suporta o comando #pragma pack. Isso permitirá que você para ajustar os dados de embalagem e alinhamento.

Por exemplo:

#pragma pack 1
struct MyStruct
{
    int a;
    char b;
    int c;
    short d;
} myData;

I = sizeof(myData);

Eu agora deve ter o comprimento de 11. Sem o pragma, eu poderia ser qualquer coisa de 11 a 14 (e para alguns sistemas, tanto quanto 32), dependendo da embalagem padrão do compilador.

Pode fazê-lo se você tem implícita ou explicitamente definir o alinhamento da estrutura. A estrutura que está alinhado 4 será sempre um múltiplo de 4 bytes, mesmo se o tamanho de seus membros seria algo que não é um múltiplo de 4 bytes.

Também uma biblioteca pode ser compilado em x86 com ints de 32 bits e você pode estar comparando seus componentes em um processo de 64 bits que lhe daria um resultado diferente se você estivesse fazendo isso com a mão.

C99 N1256 projecto de norma

http: //www.open-std .org / JTC1 / SC22 / WG14 / www / docs / n1256.pdf

6.5.3.4 O operador sizeof :

3 Quando aplicado a um operando que tem estrutura ou tipo de união, o resultado é o número total de bytes de um objecto tal, incluindo interno e de fuga estofamento.

6.7.2.1 Estrutura e sindicais especificadores :

13 ... Pode haver sem nome preenchimento dentro de um objeto de estrutura, mas não em seu início.

e

15 Pode haver estofo não identificado no final de uma estrutura ou de união.

O novo C99 característica membro de matriz flexível (struct S {int is[];};) também pode afectar preenchimento:

16 Como um caso especial, o último elemento de uma estrutura com mais de um membro nomeado pode tem um tipo de matriz incompleta; isto é chamado um membro de matriz flexível. Na maioria das situações, o membro de matriz flexível é ignorado. Em particular, o tamanho da estrutura é como se o membro da matriz flexível foram omitidos excepto que podem ter estofo mais do que à direita a omissão implicaria.

Anexo J problemas de portabilidade reitera:

A seguir são especificadas: ...

  • O valor de preenchimento de bytes ao armazenar os valores em estruturas ou uniões (6.2.6.1)

C ++ 11 N3337 projecto de norma

http://www.open -std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

5.3.3 Sizeof :

2 Quando aplicado a uma classe, o resultado é o número de bytes num objecto dessa classe, incluindo qualquer preenchimento requeridos para colocação de objetos desse tipo em uma matriz.

9.2 Os membros da classe :

Um apontador para um objecto struct-disposição padrão, adequadamente convertido utilizando um reinterpret_cast, pontos à sua membro inicial (ou se este membro é um campo de bits, em seguida, para a unidade na qual reside) e vice-versa. [ Nota: Não pode, portanto, ser estofamento sem nome dentro de um objeto struct layout padrão, mas não no seu início, conforme necessário para conseguir o alinhamento adequado. - nota final]

Eu só sei o suficiente C ++ para entender a nota: -)

Além das outras respostas, uma struct pode (mas geralmente não) têm funções virtuais, caso em que o tamanho da estrutura também irá incluir o espaço para o vtbl.

folhas de linguagem compilador C alguma liberdade sobre a localização dos elementos estruturais na memória:

    buracos
  • memória pode aparecer entre quaisquer dois componentes, e após o último componente. Foi devido ao fato de que certos tipos de objetos no computador de destino pode ser limitado pelas fronteiras de abordar
  • "buracos de memória" tamanho incluído no resultado do operador sizeof. O sizeof só não incluem o tamanho da matriz flexível, que está disponível em C / C ++
  • Algumas implementações da linguagem permitem-lhe controlar o layout de memória de estruturas através das opções de Pragma e compilador

A linguagem C fornece alguma garantia para o programador dos elementos de layout na estrutura:

  • compiladores necessários para atribuir uma sequência de componentes crescentes endereços de memória
  • endereço do primeiro coincide componentes com o endereço inicial da estrutura
  • campos de bits sem nome pode ser incluído na estrutura para os alinhamentos de endereços necessários de elementos adjacentes

Problemas relacionados com o alinhamento elementos:

  • Diferentes computadores alinhar as bordas dos objetos de formas diferentes
  • Diferentes restrições sobre a largura do campo de bits
  • Computadores divergem sobre como armazenar os bytes em uma palavra (Intel 80x86 e Motorola 68000)

Como funciona o alinhamento:

  • O volume ocupado pela estrutura é calculado como o tamanho do elemento único alinhadas de um array de tais estruturas. A estrutura deve extremidade de modo que não o primeiro elemento da estrutura seguinte a seguir faz a requisitos de alinhamento violar

P.S Informações mais detalhadas estão disponíveis aqui: "Samuel P.Harbison, Guy L.Steele C referência, (5.6.2 - 5.6.7)"

A idéia é que a velocidade e cache de considerações, operandos devem ser lidos a partir de endereços alinhados ao seu tamanho natural. Para que isso aconteça, os membros da estrutura almofadas compilador então o seguinte membro ou após struct será alinhado.

struct pixel {
    unsigned char red;   // 0
    unsigned char green; // 1
    unsigned int alpha;  // 4 (gotta skip to an aligned offset)
    unsigned char blue;  // 8 (then skip 9 10 11)
};

// next offset: 12

A arquitetura x86 tem sido sempre capaz de buscar endereços desalinhados. No entanto, é mais lento e quando o desalinhamento se sobreponha a duas linhas de cache diferentes, em seguida, ele remove duas linhas de cache quando um acesso alinhado só iria despejar um.

Algumas arquiteturas realmente tem que armadilha no desalinhada leituras e gravações, e as primeiras versões da arquitetura ARM (aquele que evoluiu para todas as CPUs móvel de hoje) ... bem, eles realmente só retornou dados ruins sobre para aqueles. (Eles ignoraram os bits de baixa ordem.)

Finalmente, nota que as linhas de cache pode ser arbitrariamente grande, eo compilador não tenta adivinhar quem ou fazer uma troca de espaço-vs-velocidade. Em vez disso, as decisões de alinhamento fazem parte da ABI e representam o alinhamento mínimo que acabará por preencher uniformemente até uma linha de cache.

TL; DR:. alinhamento é importante

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top