StructLayoutAttribute.Pack Campo

Definição

Controla o alinhamento dos campos de dados de uma classe ou estrutura na memória.

public: int Pack;
public int Pack;
val mutable Pack : int
Public Pack As Integer 

Valor de Campo

Observações

O Pack campo controla o alinhamento dos campos de um tipo na memória. Afeta a LayoutKind.Sequential propriedade. O valor indica o tamanho de empacotamento padrão para a plataforma atual. O valor de Pack deve ser 0, 1, 2, 4, 8, 16, 32, 64 ou 128. O valor padrão é 0.

Os campos de uma instância tipo são alinhados usando as seguintes regras:

  • O alinhamento de um tipo é o tamanho do seu maior elemento (por exemplo, 1, 2, 4 ou 8 bytes) ou o tamanho de empacotamento especificado, o que for menor.
  • Cada campo deve alinhar-se com campos do seu próprio tamanho ou com o alinhamento do tipo, o que for menor. Como o alinhamento padrão do tipo é o tamanho do seu maior elemento, que é maior ou igual a todos os outros comprimentos de campo, isto geralmente significa que os campos estão alinhados pelo seu tamanho. Por exemplo, mesmo que o maior campo de um tipo seja um inteiro de 64 bits (8 bytes) ou que o campo Pack esteja definido como 8, Byte os campos alinham-se em limites de 1 byte, Int16 os campos alinham-se em limites de 2 bytes e Int32 os campos alinham-se em limites de 4 bytes.
  • É adicionado preenchimento entre campos para satisfazer os requisitos de alinhamento.

Por exemplo, considere a seguinte estrutura, que consiste em dois Byte campos e um Int32 campo, quando é usada com vários valores para o Pack campo.

using System;

struct ExampleStruct
{
    public byte b1;
    public byte b2;
    public int i3;
}

Importante

Para compilar com sucesso os exemplos de C#, deve especificar o /unsafe switch do compilador.

Se especificar o tamanho padrão de empacotamento, o tamanho da estrutura é de 8 bytes. Os dois bytes ocupam os primeiros dois bytes de memória, porque os bytes têm de se alinhar em fronteiras de um byte. Como o alinhamento padrão do tipo é 4 bytes, que é o tamanho dos seus maiores campos, i3, há dois bytes de enchimento seguidos pelo campo inteiro.

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack = 0)]
struct ExampleStruct1
{
    public byte b1;
    public byte b2;
    public int i3;
}

public class Example1
{
    public unsafe static void Main()
    {
        ExampleStruct1 ex = new();
        byte* addr = (byte*)&ex;
        Console.WriteLine("Size:      {0}", sizeof(ExampleStruct1));
        Console.WriteLine("b1 Offset: {0}", &ex.b1 - addr);
        Console.WriteLine("b2 Offset: {0}", &ex.b2 - addr);
        Console.WriteLine("i3 Offset: {0}", (byte*)&ex.i3 - addr);
    }
}
// The example displays the following output:
//       Size:      8
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 4

Se Pack for definido para 2, o tamanho da estrutura é de 6 bytes. Como antes, os dois bytes ocupam os primeiros dois bytes de memória. Como os campos agora se alinham em limites de 2 bytes, não há preenchimento entre o segundo byte e o inteiro.

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack = 2)]
struct ExampleStruct2
{
    public byte b1;
    public byte b2;
    public int i3;
}

public class Example2
{
    public unsafe static void Main()
    {
        ExampleStruct2 ex = new();
        byte* addr = (byte*)&ex;
        Console.WriteLine("Size:      {0}", sizeof(ExampleStruct2));
        Console.WriteLine("b1 Offset: {0}", &ex.b1 - addr);
        Console.WriteLine("b2 Offset: {0}", &ex.b2 - addr);
        Console.WriteLine("i3 Offset: {0}", (byte*)&ex.i3 - addr);
    }
}
// The example displays the following output:
//       Size:      6
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 2

Se Pack for definido como 4, o tamanho da estrutura é o mesmo que no caso padrão, onde o alinhamento do tipo foi definido pelo tamanho do seu maior campo, i3, que é 4.

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct ExampleStruct3
{
    public byte b1;
    public byte b2;
    public int i3;
}

public class Example3
{
    public unsafe static void Main()
    {
        ExampleStruct3 ex = new();
        byte* addr = (byte*)&ex;
        Console.WriteLine("Size:      {0}", sizeof(ExampleStruct3));
        Console.WriteLine("b1 Offset: {0}", &ex.b1 - addr);
        Console.WriteLine("b2 Offset: {0}", &ex.b2 - addr);
        Console.WriteLine("i3 Offset: {0}", (byte*)&ex.i3 - addr);
    }
}
// The example displays the following output:
//       Size:      8
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 4

Se Pack for definido para 8, o tamanho da estrutura mantém-se igual ao caso padrão, porque o i3 campo alinha-se numa fronteira de 4 bytes, que é menor do que a fronteira de 8 bytes especificada pelo campo Pack.

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack = 8)]
struct ExampleStruct4
{
    public byte b1;
    public byte b2;
    public int i3;
}

public class Example4
{
    public unsafe static void Main()
    {
        ExampleStruct4 ex = new();
        byte* addr = (byte*)&ex;
        Console.WriteLine("Size:      {0}", sizeof(ExampleStruct4));
        Console.WriteLine("b1 Offset: {0}", &ex.b1 - addr);
        Console.WriteLine("b2 Offset: {0}", &ex.b2 - addr);
        Console.WriteLine("i3 Offset: {0}", (byte*)&ex.i3 - addr);
    }
}
// The example displays the following output:
//       Size:      8
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 4

Para dar outro exemplo, considere a seguinte estrutura, que consiste em dois campos de bytes, um campo inteiro com sinal de 32 bits, um array de bytes de elemento único e um valor decimal. Com o tamanho padrão de empacotamento, o tamanho da estrutura é de 28 bytes no .NET Framework e 32 bytes no .NET 5+. Os dois bytes ocupam os primeiros dois bytes de memória, seguidos por dois bytes de enchimento, seguidos pelo inteiro. Segue-se o array de um byte, seguido de três bytes de preenchimento (padding). Como um valor decimal consiste em vários campos, o alinhamento baseia-se no maior dos seus corpos em vez do tamanho da Decimal estrutura como um todo. Nas versões .NET 5 e posteriores, a estrutura Decimal consiste em dois campos Int32 e um campo de 8 bytes, pelo que o campo Decimal, d5, alinha-se numa fronteira de 8 bytes. No .NET Framework, a estrutura Decimal consiste em quatro campos Int32, pelo que o campo Decimal, d5, alinha-se numa fronteira de 4 bytes.

using System;

unsafe struct ExampleStruct5
{

    public byte b1;
    public byte b2;
    public int i3;
    public fixed byte a4[1];
    public decimal d5;
}

public class Example5
{
    public unsafe static void Main()
    {
        ExampleStruct5 ex = new();
        byte* addr = (byte*)&ex;
        Console.WriteLine("Size:      {0}", sizeof(ExampleStruct5));
        Console.WriteLine("b1 Offset: {0}", &ex.b1 - addr);
        Console.WriteLine("b2 Offset: {0}", &ex.b2 - addr);
        Console.WriteLine("i3 Offset: {0}", (byte*)&ex.i3 - addr);
        Console.WriteLine("a4 Offset: {0}", ex.a4 - addr);
        Console.WriteLine("d5 Offset: {0}", (byte*)&ex.d5 - addr);
    }
}
// The example displays the following output:
//
// .NET 5+:
//       Size:      32
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 4
//       a4 Offset: 8
//       d5 Offset: 16
//
// .NET Framework:
//       Size:      28
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 4
//       a4 Offset: 8
//       d5 Offset: 12

Se Pack for definido para 2, o tamanho da estrutura é de 24 bytes. Em comparação com o alinhamento padrão, os dois bytes de enchimento entre os dois bytes e o inteiro foram removidos porque o alinhamento do tipo é agora 4 em vez de 2. E os três bytes de preenchimento seguintes a4 foram substituídos por um byte de enchimento (padding), uma vez d5 que agora se alinham numa fronteira de 2 bytes em vez de uma fronteira de 4 bytes.

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack = 2)]
unsafe struct ExampleStruct6
{

    public byte b1;
    public byte b2;
    public int i3;
    public fixed byte a4[1];
    public decimal d5;
}

public class Example6
{
    public unsafe static void Main()
    {
        ExampleStruct6 ex = new();
        byte* addr = (byte*)&ex;
        Console.WriteLine("Size:      {0}", sizeof(ExampleStruct6));
        Console.WriteLine("b1 Offset: {0}", &ex.b1 - addr);
        Console.WriteLine("b2 Offset: {0}", &ex.b2 - addr);
        Console.WriteLine("i3 Offset: {0}", (byte*)&ex.i3 - addr);
        Console.WriteLine("a4 Offset: {0}", ex.a4 - addr);
        Console.WriteLine("d5 Offset: {0}", (byte*)&ex.d5 - addr);
    }
}
// The example displays the following output:
//       Size:      24
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 2
//       a4 Offset: 6
//       d5 Offset: 8

Se Pack for definido para 16, o tamanho da estrutura é o mesmo que no caso padrão, porque todos os requisitos de alinhamento nesta estrutura são inferiores a 16.

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack = 16)]
unsafe struct ExampleStruct7
{

    public byte b1;
    public byte b2;
    public int i3;
    public fixed byte a4[1];
    public decimal d5;
}

public class Example7
{
    public unsafe static void Main()
    {
        ExampleStruct7 ex = new();
        byte* addr = (byte*)&ex;
        Console.WriteLine("Size:      {0}", sizeof(ExampleStruct7));
        Console.WriteLine("b1 Offset: {0}", &ex.b1 - addr);
        Console.WriteLine("b2 Offset: {0}", &ex.b2 - addr);
        Console.WriteLine("i3 Offset: {0}", (byte*)&ex.i3 - addr);
        Console.WriteLine("a4 Offset: {0}", ex.a4 - addr);
        Console.WriteLine("d5 Offset: {0}", (byte*)&ex.d5 - addr);
    }
}
// The example displays the following output:
//
// .NET 5+:
//       Size:      32
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 4
//       a4 Offset: 8
//       d5 Offset: 16
//
// .NET Framework:
//       Size:      28
//       b1 Offset: 0
//       b2 Offset: 1
//       i3 Offset: 4
//       a4 Offset: 8
//       d5 Offset: 12

O Pack campo é frequentemente utilizado quando estruturas são exportadas durante operações de escrita em disco e rede. O campo é também frequentemente utilizado durante operações de invocação e interoperabilidade de plataforma.

Ocasionalmente, o campo é usado para reduzir os requisitos de memória, produzindo um tamanho de empacotamento mais apertado. No entanto, esta utilização exige uma consideração cuidadosa das restrições reais de hardware e pode, na verdade, degradar o desempenho.

Aplica-se a