Ответ 1
Из статьи библиотеки MSDN для перечисления LayoutKind:
Точная позиция каждого члена объекта в неуправляемой памяти контролируется явно, в зависимости от значения поля StructLayoutAttribute.Pack. Каждый член должен использовать FieldOffsetAttribute, чтобы указать положение этого поля в типе.
Соответствующая фраза выделена, этого не происходит в этой программе, указатель по-прежнему очень сильно разыменовывает управляемую память.
И да, то, что вы видите, идентично тому, что происходит, когда структура содержит член типа DateTime, типа, к которому применено [StructLayout (LayoutKind.Auto)]. Код полевого маршаллера в CLR, который определяет макет, прилагает усилия для соблюдения LayoutKind.Sequential для управляемых структур. Но он быстро сдастся без визга, если столкнется с каким-либо участником, конфликтующим с этой целью. Для этого достаточно структуры, которая сама по себе не является последовательной. Вы можете видеть, что это делается в источнике SSCLI20, src/clr/vm/fieldmarshaler.cpp, ищите fDisqualifyFromManagedSequential
Который заставит это переключиться на автоматическое расположение, то же самое правило расположения, которое применялось к классам. Это переупорядочивает поля, чтобы минимизировать заполнение между членами. С чистым эффектом, что объем требуемой памяти меньше. После элемента "Bool" есть 7 байтов заполнения, неиспользуемое пространство для выравнивания элемента "Long" с адресом, кратным 8. Конечно, это расточительно, исправляя это, делая длинный элемент первым в макете,
Так что вместо явного макета с /* offset - size */annotated:
public int A; /* 0 - 4 */
public int B; /* 4 - 4 */
public bool Bool; /* 8 - 1 */
// padding /* 9 - 7 */
public long Long; /* 16 - 8 */
public Explicit C; /* 24 - 8 */
/* Total: 32 */
Это приходит с:
public long Long; /* 0 - 8 */
public int A; /* 8 - 4 */
public int B; /* 12 - 4 */
public bool Bool; /* 16 - 1 */
// padding /* 17 - 3 */
public Explicit C; /* 20 - 8 */
/* Total: 28 */
С легким сохранением 4 байта памяти. 64-битная компоновка требует дополнительного заполнения, чтобы гарантировать, что long все еще выровнен, когда он хранится в массиве. Все это в значительной степени недокументировано и может быть изменено, поэтому никогда не принимайте зависимость от структуры управляемой памяти. Только Marshal.StructureToPtr() может дать вам гарантию.