다음을 통해 공유


StructLayoutAttribute.Pack 필드

정의

메모리에 있는 클래스 또는 구조체의 데이터 필드 맞춤을 제어합니다.

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

필드 값

설명

Pack 필드는 메모리에서 형식의 필드 맞춤을 제어합니다. 속성에 LayoutKind.Sequential 영향을 줍니다. 값은 현재 플랫폼의 기본 압축 크기를 나타냅니다. 값 Pack 은 0, 1, 2, 4, 8, 16, 32, 64 또는 128이어야 합니다. 기본값은 0입니다.

형식 인스턴스의 필드는 다음 규칙을 사용하여 정렬됩니다.

  • 형식의 맞춤은 가장 큰 요소의 크기(예: 1, 2, 4 또는 8바이트) 또는 지정된 압축 크기 중 더 작은 크기입니다.
  • 각 필드는 크기가 작은 필드나 형식의 맞춤에 맞춰야 합니다. 형식의 기본 맞춤은 다른 모든 필드 길이보다 크거나 같은 가장 큰 요소의 크기이므로 일반적으로 필드가 크기에 따라 정렬됨을 의미합니다. 예를 들어 형식에서 가장 큰 필드가 64비트(8 바이트) 정수이거나 Pack 필드가 8 Byte 로 설정된 경우에도 필드는 1 바이트 경계 Int16 에 맞춰지고 필드는 2 바이트 경계에 정렬되고 Int32 필드는 4 바이트 경계에 정렬됩니다.
  • 맞춤 요구 사항을 충족하기 위해 필드 사이에 안쪽 여백이 추가됩니다.

예를 들어 필드의 다양한 값 Pack 과 함께 사용되는 경우 두 Byte 개의 필드와 하나의 Int32 필드로 구성된 다음 구조를 고려해 보세요.

using System;

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

중요

C# 예제를 성공적으로 컴파일하려면 컴파일러 스위치를 /unsafe 지정해야 합니다.

기본 압축 크기를 지정하는 경우 구조체의 크기는 8바이트입니다. 바이트는 1바이트 경계에 맞춰야 하므로 두 바이트는 메모리의 처음 두 바이트를 차지합니다. 형식의 기본 맞춤은 가장 큰 필드 i3의 크기인 4바이트이므로 2바이트의 안쪽 여백과 정수 필드가 있습니다.

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

가 2로 설정된 경우 Pack 구조체의 크기는 6바이트입니다. 이전과 마찬가지로 두 바이트는 처음 두 바이트의 메모리를 차지합니다. 이제 필드가 2바이트 경계에 맞춰지므로 두 번째 바이트와 정수 사이에 안쪽 여백이 없습니다.

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

가 4로 설정된 경우 Pack 구조체의 크기는 기본 사례와 동일합니다. 여기서 형식의 맞춤은 가장 큰 필드의 크기( i34)로 정의되었습니다.

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

가 8로 설정된 경우 Pack 필드가 Pack 필드에 지정된 8 바이트 경계보다 작은 4 바이트 경계에 맞춰지므로 구조체의 크기는 기본 사례 i3 와 동일합니다.

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

또 다른 예를 들어 두 바이트 필드, 32비트 부속 정수 필드 1개, 단일 요소 바이트 배열 1개 및 10진수 값으로 구성된 다음 구조를 고려해 보세요. 기본 압축 크기를 사용하면 구조체의 크기는 .NET Framework에서 28바이트, .NET 5 이상에서는 32바이트입니다. 두 바이트는 처음 2바이트의 메모리를 차지하고 그 뒤에 2바이트의 안쪽 여백과 정수가 잇습니다. 다음은 1바이트 배열과 3바이트의 안쪽 여백입니다. 10진수 값은 여러 필드로 구성되므로 맞춤은 구조체 전체의 Decimal 크기가 아닌 가장 큰 필드를 기반으로 합니다. .NET 5 이상 버전에서 구조체는 Decimal 2개의 Int32 필드와 1개의 8 바이트 필드로 구성되므로 Decimal 필드 d5는 8 바이트 경계에 맞춥니다. .NET Framework에서 구조체는 Decimal 4개의 Int32 필드로 구성되므로 Decimal 필드 d5는 4 바이트 경계에 맞춥니다.

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

가 2로 설정된 경우 Pack 구조체의 크기는 24바이트입니다. 기본 맞춤과 비교하여 형식의 맞춤이 이제 2가 아닌 4이므로 두 바이트와 정수 사이의 패딩 2바이트가 제거되었습니다. 그리고 이후 a4 패딩의 세 바이트는 안쪽 여백의 1 바이트로 대체되었습니다, d5 이후 지금은 4 바이트 경계가 아닌 2 바이트 경계에 정렬.

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

가 16으로 설정된 경우 Pack 이 구조체의 모든 맞춤 요구 사항이 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

Pack 필드는 디스크 및 네트워크 쓰기 작업 중에 구조를 내보낼 때 자주 사용됩니다. 이 필드는 플랫폼 호출 및 interop 작업 중에도 자주 사용됩니다.

경우에 따라 필드는 더 엄격한 압축 크기를 생성하여 메모리 요구 사항을 줄이는 데 사용됩니다. 그러나 이 사용은 실제 하드웨어 제약 조건을 신중하게 고려해야 하며 실제로 성능이 저하할 수 있습니다.

적용 대상