Подтвердить что ты не робот

Макет типа значения .NET в памяти

У меня есть следующие типы значений .NET:

[StructLayout(LayoutKind.Sequential)]
public struct Date
{
    public UInt16 V;
}

[StructLayout(LayoutKind.Sequential)]
public struct StringPair
{
    public String A;
    public String B;
    public String C;
    public Date D;
    public double V;
}

У меня есть код, передающий указатель на тип значения неуправляемому коду, а также смещения, обнаруженные при вызове System.Runtime.InteropServices.Marshal.OffsetOf. Неуправляемый код заполняет дату и двойные значения.

Сдвиги, которые сообщаются для структуры StringPair, являются именно тем, что я ожидал бы: 0, 8, 16, 24, 32

У меня есть следующий код в тестовой функции:

FieldInfo[] fields = typeof(StringPair).GetFields(BindingFlags.Instance|BindingFlags.Public);

for ( int i = 0; i < fields.Length; i++ )
{
    int offset = System.Runtime.InteropServices.Marshal.OffsetOf(typeof(StringPair), fields[i].Name).ToInt32();

    Console.WriteLine(String.Format(" >> field {0} @ offset {1}", fields[i].Name, offset));
}

который печатает именно эти смещения.

 >> field A @ offset 0
 >> field B @ offset 8
 >> field C @ offset 16
 >> field D @ offset 24
 >> field V @ offset 32

Затем у меня есть тестовый код:           foreach (пара StringPair попарно)           {               Дата d = pair.D;               double v = pair.V;               ...

С абитуриентом, связанным с ним в отладчике:

               Date d = pair.D;
0000035d  lea         rax,[rbp+20h] 
00000361  add         rax,20h 
00000367  mov         ax,word ptr [rax] 
0000036a  mov         word ptr [rbp+000000A8h],ax 
00000371  movzx       eax,word ptr [rbp+000000A8h] 
00000378  mov         word ptr [rbp+48h],ax 

                double v = pair.V;
0000037c  movsd       xmm0,mmword ptr [rbp+38h] 
00000381  movsd       mmword ptr [rbp+50h],xmm0 

Загружает поле D со смещением 32 (0x20) и поле V со смещением 24 (0x38-0x20). JIT изменил порядок вокруг. Отладчик Visual Studio также показывает этот инвертированный порядок.

Почему!? Я тянул свои волосы, пытаясь понять, где моя логика идет не так. Если я поменяю порядок D и V в структуре, тогда все будет работать, но этот код должен иметь дело с архитектурой плагина, где другие разработчики определили структуру, и от них нельзя ожидать, чтобы они запоминали тайные правила макета.

4b9b3361

Ответ 1

Информация, которую вы получаете из класса маршала, имеет значение только в том случае, если тип фактически получает маршалин. Макет внутренней памяти управляемой структуры не может быть обнаружен с помощью каких-либо документированных средств, кроме, возможно, выглядящего на ассемблере.

Это означает, что CLR может свободно реорганизовать компоновку структуры и оптимизировать упаковку. При замене полей D и V ваша структура меньше из-за требований к выравниванию двойника. Он сохраняет 6 байтов на вашей 64-разрядной машине.

Не уверен, почему это будет проблемой для вас, этого не должно быть. Рассмотрим Marshal.StructureToPtr(), чтобы получить структуру, расположенную так, как вы хотите.

Ответ 2

Если вам нужен явный макет... использовать явный макет...

[StructLayout(LayoutKind.Explicit)]
public struct StringPair
{
    [FieldOffset(0)] public String A;
    [FieldOffset(8)] public String B;
    [FieldOffset(16)] public String C;
    [FieldOffset(24)] public Date D;
    [FieldOffset(32)] public double V;
}

Ответ 3

Две вещи:

  • StructLayout(Sequential) не гарантирует упаковку. Вы можете использовать Pack=1, в противном случае 32 и 64-битные платформы могут отличаться.

  • и строка - это ссылка, а не указатель. Если длина строки всегда исправлена, вы можете использовать фиксированные массивы char:

    public struct MyArray // This code must appear in an unsafe block
    {
        public fixed char pathName[128];
    }