Share via


StructLayoutAttribute.Pack Campo

Definición

Controla la alineación de los campos de datos de una clase o una estructura en la memoria.

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

Valor de campo

Comentarios

El Pack campo controla la alineación de los campos de un tipo en memoria. Afecta a la LayoutKind.Sequential propiedad . El valor indica el tamaño de empaquetado predeterminado para la plataforma actual. El valor de Pack debe ser 0, 1, 2, 4, 8, 16, 32, 64 o 128. El valor predeterminado es 0.

Los campos de una instancia de tipo se alinean mediante las reglas siguientes:

  • La alineación de un tipo es el tamaño de su elemento más grande (por ejemplo, 1, 2, 4 o 8 bytes) o el tamaño de empaquetado especificado, que sea menor.
  • Cada campo debe alinearse con los campos de su propio tamaño o la alineación del tipo, lo que sea menor. Dado que la alineación predeterminada del tipo es el tamaño de su elemento más grande, que es mayor o igual que todas las demás longitudes de campo, normalmente significa que los campos están alineados por su tamaño. Por ejemplo, incluso si el campo más grande de un tipo es un entero de 64 bits (8 bytes) o el campo Pack se establece en 8, Byte los campos se alinean en límites de 1 byte, Int16 los campos se alinean en límites de 2 bytes y Int32 los campos se alinean en límites de 4 bytes.
  • El relleno se agrega entre los campos para satisfacer los requisitos de alineación.

Por ejemplo, considere la siguiente estructura, que consta de dos Byte campos y un Int32 campo, cuando se usa con varios valores para el Pack campo.

using System;

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

Importante

Para compilar correctamente los ejemplos de C#, debe especificar el modificador del /unsafe compilador.

Si especifica el tamaño de empaquetado predeterminado, el tamaño de la estructura es de 8 bytes. Los dos bytes ocupan los dos primeros bytes de memoria, ya que los bytes deben alinearse en límites de un byte. Dado que la alineación predeterminada del tipo es de 4 bytes, que es el tamaño de sus campos más grandes, , i3hay dos bytes de relleno seguidos del campo entero.

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

Si Pack se establece en 2, el tamaño de la estructura es de 6 bytes. Como antes, los dos bytes ocupan los dos primeros bytes de memoria. Dado que los campos ahora se alinean en los límites de 2 bytes, no hay relleno entre el segundo byte y el entero.

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

Si Pack se establece en 4, el tamaño de la estructura es el mismo que en el caso predeterminado, donde la alineación del tipo se definió mediante el tamaño de su campo más grande, i3, que es 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

Si Pack se establece en 8, el tamaño de la estructura sigue siendo el mismo que en el caso predeterminado, ya que el i3 campo se alinea en un límite de 4 bytes, que es menor que el límite de 8 bytes especificado por el 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 tomar otro ejemplo, considere la siguiente estructura, que consta de dos campos de bytes, un campo entero con signo de 32 bits, una matriz de bytes de un solo elemento y un valor decimal. Con el tamaño de empaquetado predeterminado, el tamaño de la estructura es de 28 bytes en .NET Framework y 32 bytes en .NET 5+. Los dos bytes ocupan los dos primeros bytes de memoria, seguidos de dos bytes de relleno, seguidos del entero. A continuación, se muestra la matriz de un byte, seguida de tres bytes de relleno. Dado que un valor decimal consta de varios campos, la alineación se basa en el mayor de sus campos en lugar del tamaño de la Decimal estructura en su conjunto. En .NET 5 y versiones posteriores, la Decimal estructura consta de dos Int32 campos y un campo de 8 bytes, por lo que el Decimal campo, d5, se alinea en un límite de 8 bytes. En .NET Framework, la Decimal estructura consta de cuatro Int32 campos, por lo que el Decimal campo, d5, se alinea en un límite 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

Si Pack se establece en 2, el tamaño de la estructura es de 24 bytes. En comparación con la alineación predeterminada, los dos bytes de relleno entre los dos bytes y el entero se han quitado porque la alineación del tipo es ahora 4 en lugar de 2. Y los tres bytes de relleno después a4 de haber sido reemplazados por un byte de relleno, ya que d5 ahora se alinea en un límite de 2 bytes en lugar de un límite 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

Si Pack se establece en 16, el tamaño de la estructura es el mismo que en el caso predeterminado, porque todos los requisitos de alineación de esta estructura son 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

El Pack campo se usa con frecuencia cuando las estructuras se exportan durante las operaciones de escritura de disco y red. El campo también se usa con frecuencia durante las operaciones de interoperabilidad e invocación de plataforma.

En ocasiones, el campo se usa para reducir los requisitos de memoria mediante la generación de un tamaño de empaquetado más estricto. Sin embargo, este uso requiere una consideración cuidadosa de las restricciones de hardware reales y puede degradar realmente el rendimiento.

Se aplica a