Как проверить количество байтов, потребляемых структурой?

Если я создаю относительно большую структуру, как я могу вычислить байты, которые он занимает в памяти?

Мы можем сделать это вручную, но если структура достаточно большая, то как мы это сделаем? Есть ли часть кода или приложение?

Ответ 1

Вы можете использовать оператор sizeof или sizeof.
Существуют некоторые различия между этими параметрами, см. Ссылки ссылки для более.

В любом случае хороший способ использовать эту функцию - иметь общий метод или метод расширения, подобный этим:

static class Test
{
  static void Main()
  {
    //This will return the memory usage size for type Int32:
    int size = SizeOf<Int32>();

    //This will return the memory usage size of the variable 'size':
    //Both lines are basically equal, the first one makes use of ex. methods
    size = size.GetSize();
    size = GetSize(size);
  }

  public static int SizeOf<T>()
  {
    return System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
  }

  public static int GetSize(this object obj)
  {
    return System.Runtime.InteropServices.Marshal.SizeOf(obj);
  }
}

Ответ 2

Структуры были очень сложными животными в компьютерной инженерии в течение очень долгого времени. Их макет памяти очень зависит от оборудования. Чтобы сделать их эффективными, их члены должны быть выровнены так, чтобы ЦП мог быстро считывать и записывать свои значения без необходимости мультиплексирования байтов в соответствии с шириной шины памяти. У каждого компилятора своя стратегия упаковки членов, часто направленная, например, директивой пакета #pragma в программе C или С++.

Это нормально, но проблема в сценариях взаимодействия. Где один фрагмент кода может делать разные предположения о структуре структуры, чем другой фрагмент, скомпилированный другим компилятором. Вы можете увидеть это снова в решении COM,.NET grandfather для взаимодействия программирования. У COM очень низкая поддержка обработки структур. Он не поддерживает их как собственный тип автоматизации, но имеет обходной путь через интерфейс IRecordInfo. Это позволяет программе обнаруживать макет памяти во время выполнения через явное объявление структуры в библиотеке типов. Что работает нормально, но довольно неэффективно.

Разработчики .NET сделали очень смелое и правильное решение для решения этой проблемы. Они сделали структуру макета структуры полностью неоткрытой. Нет документального способа получения смещения члена. И по расширению, никоим образом не обнаружить размер структуры. Все любимые ответы, использование Marshal.SizeOf() на самом деле не решение. Это возвращает размер структуры после ее маршалирования, размер, который вам нужно передать, скажем, Marshal.AllocCoTaskMem(), прежде чем вы вызовете Marshal.StructureToPtr. Это упорядочивает и выравнивает элементы структуры в соответствии с атрибутом [StructLayout], связанным со структурой. Обратите внимание, что этот атрибут не требуется для структур (например, для классов), runtime реализует значение по умолчанию, которое использует объявленный порядок для членов.

Одним из очень приятных побочных эффектов, которые невозможно обнаружить, является то, что CLR может играть с ним. При упаковке элементов структуры и их выравнивании макет может получить отверстия, которые не хранят данные. Вызывается прописные байты. Учитывая, что макет неизвестен, среда CLR может фактически использовать отступы. Он перемещает элемент, если он достаточно мал, чтобы соответствовать такой дыре. Теперь вы фактически получите структуру, размер которой меньше, чем обычно требуется, учитывая объявленный структурный макет. И, в частности, Marshal.SizeOf() вернет неправильное значение для размера структуры, оно вернет слишком большое значение.

Короче говоря, нет общего способа получения точной величины размера структуры программно. Лучше всего просто не задавать вопрос. Marshal.SizeOf() даст вам оценку, предполагая, что структура является blittable. Если по какой-то причине вам нужно точное значение, вы можете посмотреть на сгенерированный машинный код метода, который объявляет локальную переменную типа структуры и сравнивает ее с тем же методом без этой локальной переменной. Вы увидите разницу в настройке указателя стека, инструкцию "sub esp, xxx" в верхней части метода. Конечно, он будет зависящим от архитектуры, вы обычно получите большую структуру в 64-битном режиме.

Ответ 3

Вы можете использовать ключевое слово sizeof() для пользовательских структур, которые не содержат никаких полей или свойств, которые являются ссылочными типами, или используйте Marshal.SizeOf(Type) или Marshal.SizeOf(object), чтобы получить неуправляемый размер типа или структуры, который имеет последовательный или явный макет.

Ответ 4

Я написал небольшую небольшую библиотеку в CIL (.NET), чтобы показать некоторые опрятные функциональные возможности, недоступные на С#. Я вырвал инструкцию sizeof.

Он значительно отличается от оператора sizeof в С#. В принципе, он получает размер структуры (или ссылочный тип, который действует смешно с некоторыми оптимизациями), включая дополнение и все. Итак, если вам нужно создать массив T, вы можете использовать sizeof для определения расстояния между каждым элементом массива в байтах. Это также полностью проверяемый и управляемый код. Обратите внимание, что в Mono произошла ошибка (pre-3.0?), Которая привела бы к неправильному сообщению sizeof ссылочных типов, что расширилось бы к структурам, содержащим ссылочные типы.

В любом случае, вы можете загрузить лицензионную библиотеку BSD (и CIL) из BitBucket. Вы также можете увидеть код примера и еще несколько деталей в моем блоге.

Ответ 5

Вы хотите использовать System.Runtime.InteropServices.Marshal.SizeOf():

struct s
{
    public Int64 i;
}

public static void Main()
{
    s s1;
    s1.i = 10;          
    var s = System.Runtime.InteropServices.Marshal.SizeOf(s1);
}

Ответ 6

Вы можете использовать System.Runtime.InteropServices.Marshal.SizeOf(), чтобы получить размер в байтах.

Ответ 7

В .NET Core команда sizeof CIL была открыта через недавно добавленный класс Unsafe. Добавьте ссылку на пакет System.Runtime.CompilerServices.Unsafe и просто выполните следующее:

int size = Unsafe.SizeOf<MyStruct>();

Он также работает для ссылочных типов (будет возвращен 4 или 8 в зависимости от архитектуры вашего компьютера).

Ответ 8

0. Для примера кода:

using System;
using System.Diagnostics;
using System.Reflection.Emit;
using System.Runtime.InteropServices;

1. Демонстрационная структура

[Serializable, StructLayout(LayoutKind.Sequential, Pack = 128)]
public struct T
{
    public int a;
    public byte b;
    public int c;
    public String d;
    public short e;
};

2. Вычитание управляемых указателей:

/// Return byte-offset between managed addresses of struct instances 'hi' and 'lo'
public static long IL<T1,T2>.RefOffs(ref T1 hi, ref T2 lo) { ... }
public static class IL<T1, T2>
{
    public delegate long _ref_offs(ref T1 hi, ref T2 lo);

    public static readonly _ref_offs RefOffs;

    static IL()
    {
        var dm = new DynamicMethod(
            Guid.NewGuid().ToString(),
            typeof(long),
            new[] { typeof(T1).MakeByRefType(), typeof(T2).MakeByRefType() },
            typeof(Object),
            true);

        var il = dm.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(OpCodes.Sub);
        il.Emit(OpCodes.Conv_I8);
        il.Emit(OpCodes.Ret);
        RefOffs = (_ref_offs)dm.CreateDelegate(typeof(_ref_offs));
    }
};

3. Отображение управляемой внутренней структуры структуры:

static class demonstration
{
    /// Helper thunk that enables automatic type-inference from argument types
    static long RefOffs<T1,T2>(ref T1 hi, ref T2 lo) => IL<T1,T2>.RefOffs(ref hi, ref lo);

    public static void Test()
    {
        var t = default(T);
        var rgt = new T[2];

        Debug.Print("Marshal.Sizeof<T>: {0,2}", Marshal.SizeOf<T>());
        Debug.Print("&rgt[1] - &rgt[0]: {0,2}", RefOffs(ref rgt[1], ref rgt[0]));

        Debug.Print("int      &t.a      {0,2}", RefOffs(ref t.a, ref t));
        Debug.Print("byte     &t.b      {0,2}", RefOffs(ref t.b, ref t));
        Debug.Print("int      &t.c      {0,2}", RefOffs(ref t.c, ref t));
        Debug.Print("String   &t.d      {0,2}", RefOffs(ref t.d, ref t));
        Debug.Print("short    &t.e      {0,2}", RefOffs(ref t.e, ref t));
    }
};

4. Результаты и обсуждение

Параметр StructLayout(..., Pack) может быть добавлен в объявление struct T с любым из следующих значений: {0, 1, 2, 4, 8, 16, 32, 64, 128}. Значение по умолчанию, когда Pack не указано, или эквивалентно с Pack=0 -sets, на упаковке, равной IntPtr.Size (4 на x86, 8 на x64).

Результаты выполнения указанной выше программы показывают, что значение Pack влияет только на размер маршалинга, указанный Marshal.SizeOf, а не фактический размер одного изображения памяти T, предположительно являющегося смещением байта между физически смежные экземпляры. Контрольный код измеряет это с помощью диагностического управляемого массива new T[2], назначенного для rgt.

========= x86 ==========   ========= x64 ==========

-------- Pack=1 --------   -------- Pack=1 --------
Marshal.Sizeof(): 15   Marshal.Sizeof(): 19
&rgt[1] - &rgt[0]: 16   &rgt[1] - &rgt[0]: 24

-------- Pack=2 --------   -------- Pack=2 --------
Marshal.Sizeof(): 16   Marshal.Sizeof(): 20
&rgt[1] - &rgt[0]: 16   &rgt[1] - &rgt[0]: 24

--- Pack=4/0/default ---   -------- Pack=4 --------
Marshal.Sizeof(): 20   Marshal.Sizeof(): 24
&rgt[1] - &rgt[0]: 16   &rgt[1] - &rgt[0]: 24

-------- Pack=8 --------   --- Pack=8/0/default ---
Marshal.Sizeof(): 20   Marshal.Sizeof(): 32
&rgt[1] - &rgt[0]: 16   &rgt[1] - &rgt[0]: 24

-- Pack=16/32/64/128 ---   -- Pack=16/32/64/128 ---
Marshal.Sizeof(): 20   Marshal.Sizeof(): 32
&rgt[1] - &rgt[0]: 16   &rgt[1] - &rgt[0]: 24

Как уже отмечалось, мы обнаруживаем, что для каждой архитектуры (x86, x64) управляемый макет поля согласован независимо от Pack установка. Вот фактические смещения поля управления, опять же для 32- и 64-битного режима, как указано в приведенном выше коде:

┌─offs─┐
field type size x86 x64
===== ====== ==== === ===
a int 4 4 8
b byte 1 14 18
c int 4 8 12
d String 4/8 0 0
e short 2 12 16

Самое важное, что следует заметить в этой таблице, это то, что (как упомянутый Гансом), указанные смещения поля не являются монотонными относительно их порядка объявления, Поля экземпляров ValueType всегда переупорядочиваются так, чтобы все поля с типичным типом были первыми. Мы можем видеть, что поле String d имеет смещение 0.

Дальнейшее переупорядочение оптимизирует порядок полей, чтобы разделить внутренний избыток, который в противном случае был бы потрачен впустую. Мы можем видеть это с полем byte b, которое было перенесено из второго объявленного поля, вплоть до последнего.

Естественно, путем сортировки строк предыдущей таблицы можно выявить истинный внутренний управляемый макет .NET ValueType. Обратите внимание, что мы можем получить этот макет, несмотря на пример struct T, содержащий управляемую ссылку (String d) и, таким образом, не-blittable:

============= x86 ============   ============= x64 ============
field type size offs end   field type size offs end
===== ====== ==== ==== ===   ===== ====== ==== ==== ===
d String 4 0 … 4   d String 8 0 … 8
a int 4 4 … 8   a int 4 8 … 12
c int 4 8 … 12   c int 4 12 … 16
e short 2 12 … 14   e short 2 16 … 18
b byte 1 14 … 15   b byte 1 18 … 19

internal padding: 1 15 … 16   internal padding: 5 19 … 24

  x64 managed total size: 24

Раньше мы определяли размер одного управляемого экземпляра структуры, вычисляя разницу смещения байта между соседними экземплярами. Учитывая это, последние строки предыдущей таблицы тривиально показывают дополнение, которое CLR внутренне применяется к концу примера struct T. Помните еще раз, конечно, что это внутреннее заполнение фиксируется CLR и полностью вне нашего контроля.

5. Coda
Для полноты эта последняя таблица показывает количество дополнений, которые будут синтезироваться "на лету" во время маршалинга. Обратите внимание, что в некоторых случаях это отладка Marshal отрицательна по сравнению с внутренним управляемым размером. Например, хотя внутренний управляемый размер T в x64 равен 24 байтам, структура, испущенная маршалингом, может быть 19 или 20 байтов с Pack=1 или Pack=2 соответственно.

pack size offs end   pack size offs end
============= ==== ==== ===   ============= ==== ==== ===
1 0 15 … 15   1 0 19 … 19
2 1 15 … 16   2 1 19 … 20
4/8/16/32/64… 5 15 … 20   4/8/16/32/64… 5 19 … 24

Ответ 9

A struct должен иметь максимальный размер 16 байт. Если ваш struct больше, чем тогда, вы должны использовать class.