StructLayoutAttribute.Pack Campo

Definição

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

public int Pack;

Valor do campo

Comentários

O Pack campo controla o alinhamento dos campos de um tipo na memória. Isso 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 de tipo são alinhados usando as seguintes regras:

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

Por exemplo, considere a estrutura a seguir, que consiste em dois Byte campos e um Int32 campo, quando ela é 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 êxito os exemplos de C#, você deve especificar a opção do /unsafe compilador.

Se você especificar o tamanho de empacotamento padrão, o tamanho da estrutura será de 8 bytes. Os dois bytes ocupam os dois primeiros bytes de memória, pois os bytes devem se alinhar em limites de um byte. Como o alinhamento padrão do tipo é de 4 bytes, que é o tamanho de seus campos maiores, i3, há dois bytes de preenchimento 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 como 2, o tamanho da estrutura será de 6 bytes. Como antes, os dois bytes ocupam os dois primeiros 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 será o mesmo que no caso padrão, em que o alinhamento do tipo foi definido pelo tamanho de 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 como 8, o tamanho da estrutura ainda será o mesmo que no caso padrão, porque o i3 campo se alinha em um limite de 4 bytes, que é menor que o limite de 8 bytes especificado pelo campo Pacote.

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 obter outro exemplo, considere a seguinte estrutura, que consiste em dois campos de bytes, um campo inteiro com sinal de 32 bits, uma matriz de bytes de elemento único e um valor decimal. Com o tamanho de empacotamento padrão, o tamanho da estrutura é de 28 bytes no .NET Framework e 32 bytes no .NET 5+. Os dois bytes ocupam os dois primeiros bytes de memória, seguidos por dois bytes de preenchimento, seguidos pelo inteiro. Em seguida, está a matriz de um byte, seguida por três bytes de preenchimento. Como um valor decimal consiste em vários campos, o alinhamento é baseado no maior de seus campos em vez do tamanho da Decimal estrutura como um todo. No .NET 5 e versões posteriores, a Decimal estrutura consiste em dois Int32 campos e um campo de 8 bytes, de modo que o Decimal campo, d5, se alinha em um limite de 8 bytes. No .NET Framework, a Decimal estrutura consiste em quatro Int32 campos, portanto, o Decimal campo d5 se alinha em um limite 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 como 2, o tamanho da estrutura será de 24 bytes. Em comparação com o alinhamento padrão, os dois bytes de preenchimento 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 após a4 foram substituídos por um byte de preenchimento, já que d5 agora se alinha em um limite de 2 bytes em vez de um limite 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 como 16, o tamanho da estrutura será o mesmo que no caso padrão, pois todos os requisitos de alinhamento nessa estrutura são menores que 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 usado quando as estruturas são exportadas durante operações de gravação de disco e rede. O campo também é usado com frequência 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, esse uso requer uma consideração cuidadosa das restrições de hardware reais e pode realmente prejudicar o desempenho.

Aplica-se a

Produto Versões
.NET Core 1.0, Core 1.1, Core 2.0, Core 2.1, Core 2.2, Core 3.0, Core 3.1, 5, 6, 7, 8, 9
.NET Framework 1.1, 2.0, 3.0, 3.5, 4.0, 4.5, 4.5.1, 4.5.2, 4.6, 4.6.1, 4.6.2, 4.7, 4.7.1, 4.7.2, 4.8, 4.8.1
.NET Standard 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 2.0, 2.1
UWP 10.0