При запуске этого кода:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace StructLayoutTest
{
class Program
{
unsafe static void Main()
{
Console.WriteLine(IntPtr.Size);
Console.WriteLine();
Sequential s = new Sequential();
s.A = 2;
s.B = 3;
s.Bool = true;
s.Long = 6;
s.C.Int32a = 4;
s.C.Int32b = 5;
int* ptr = (int*)&s;
Console.WriteLine(ptr[0]);
Console.WriteLine(ptr[1]);
Console.WriteLine(ptr[2]);
Console.WriteLine(ptr[3]);
Console.WriteLine(ptr[4]);
Console.WriteLine(ptr[5]);
Console.WriteLine(ptr[6]);
Console.WriteLine(ptr[7]); //NB!
Console.WriteLine("Press any key");
Console.ReadKey();
}
[StructLayout(LayoutKind.Explicit)]
struct Explicit
{
[FieldOffset(0)]
public int Int32a;
[FieldOffset(4)]
public int Int32b;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct Sequential
{
public int A;
public int B;
public bool Bool;
public long Long;
public Explicit C;
}
}
}
Я ожидаю, что этот вывод ОБА на x86 и x64:
4 или 8 (в зависимости от x86 или x64)
2
3
1
6
0
4
5
мусора
Что я получаю вместо этого на x86:
4
6
0
2
3
1
4
5
мусора
Что я получаю вместо этого на x64:
8
6
0
2
3
1
0
4
5
Больше:
- проблема исчезает, когда я удаляю атрибуты LayoutKind.Explicit и FieldOffset.
- Проблема удаляется, когда я удаляю поле Bool.
- Проблема удаляется, когда я удаляю длинное поле.
- Обратите внимание: на x64 кажется, что параметр атрибута Pack = 4 тоже игнорируется?
Это относится к .Net3.5, а также .Net4.0
Мой вопрос: что мне не хватает? Или это ошибка?
Я нашел аналогичный вопрос:
Почему LayoutKind.Sequential работает по-другому, если структура содержит поле DateTime?
Но в моем случае макет изменяется даже тогда, когда изменяется атрибут подструктуры, без каких-либо изменений в типах данных. Так что это не похоже на оптимизацию. Кроме того, я хотел бы отметить, что другой вопрос по-прежнему остается без ответа.
В этом другом вопросе упоминается, что макет соблюдается при использовании Marshalling. Я не тестировал это сам, но мне интересно, почему макет не соблюдается для небезопасного кода, так как все соответствующие атрибуты, похоже, на месте? В документации упоминается где-то, что эти атрибуты игнорируются, если не сделано Marshalling? Зачем?
Учитывая это, могу ли я даже ожидать LayoutKind.Explicit для надежной работы для небезопасного кода?
Кроме того, в документации упоминается мотив сохранения структур с ожидаемым макетом:
Чтобы уменьшить проблемы, связанные с компоновкой, связанные с параметром Auto, компиляторы С#, Visual Basic и С++ задают последовательный макет для типов значений.
Но этот мотив, по-видимому, не относится к небезопасному коду?