Какой размер и выравнивание С# фиксированного массива bool в структуре?

При выполнении P/Invoke важно сопоставить макет данных.

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

Например:

struct MyStruct 
{
    public bool f;
}

дает размер 4. Хотя мы можем сообщить компилятору, что он должен быть 1 байт bool для соответствия типу С++ bool:

struct MyStruct
{
    [MarshalAs(UnmanagedType.I1)]
    public bool f;
}

дает размер 1.

Они имеют смысл. Но когда я тестировал фиксированный массив bool, я был в замешательстве.

unsafe struct MyStruct
{
    public fixed bool fs[1];
}

дает размер 4 байта. и

unsafe struct MyStruct
{
    public fixed bool fs[4];
}

по-прежнему задает размер 4 байта. но

unsafe struct MyStruct
{
    public fixed bool fs[5];
}

дает размер 8.

Похоже, что в фиксированном массиве bool размер элемента bool по-прежнему равен 1 байт, но выравнивание равно 4 байтам. Это не соответствует массиву bool С++, размер которого равен 1 байту и выравниванию.

Может кто-нибудь объяснить мне об этом?

Обновление: я, наконец, узнаю, причина в том, что тип bool в структуре, тогда эта структура никогда не будет blattable! Поэтому не ожидайте, что структура, которая имеет тип bool внутри, будет такой же, как в C.

С уважением, Сян.

Ответ 1

Bool довольно особенный, он возвращается к решению Денниса Ричи, чтобы не дать C-языку тип bool. Это привело к тому, что многие разработчики хаоса, языка и операционной системы добавили его сами и сделали несовместимый выбор.

Он был добавлен в Winapi как BOOL typedef. Это маршалинг по умолчанию, если вы не навязываете другой тип. Typedef-ed как int, чтобы поддерживать его совместимость с C, занимает 4 байта, как вы узнали. И выровняется по 4, как вы узнали, как и любой int.

Он был добавлен в С++. Без спецификации размера большинство реализаций компилятора С++ выбрали один байт для хранения. В частности, компилятор Microsoft С++, наиболее вероятная реализация, с которой вы будете взаимодействовать.

Он был добавлен в COM Automation как VARIANT_BOOL. Первоначально он был нацелен на новую модель расширения для Visual Basic, чтобы избавиться от ограничений VBX, и она стала чрезвычайно популярной, и практически любая среда исполнения на Windows теперь поддерживает ее. В то время VB сильно пострадал от чувствительности 16-разрядной операционной системы, VARIANT_BOOL занимает 2 байта.

Все три среды среды выполнения, вероятно, являются мишенями для взаимодействия в программе на С#. Очевидно, что дизайнеры CLR имели очень трудный выбор, чтобы выбрать между 1, 2 и 4 байтами. Невозможно выиграть, в то время как CLR делает попытку догадки в COM-взаимодействии, он не может знать, пытаетесь ли вы взаимодействовать с api или C-программой на основе C. Поэтому они сделали единственный логический выбор: ни один из них.

Структура или тип класса, который содержит bool, никогда не будет смягчаться. Даже когда вы применяете [MarshalAs (UnmanagedType.U1)], тот, который сделает его совместимым с типом CLR. Не так уверен, что это было хорошее решение, но это было то, что они сделали, поэтому нам придется иметь дело с этим.

Получение blittable структуры очень желательно, это позволяет избежать копирования. Он позволяет встроенному коду напрямую обращаться к управляемой куче и стеку. Довольно опасный и много сломанной декларации пинкока испортил кучу GC без обычной выгоды от небезопасного уведомления по ключевым словам. Но невозможно биться за скорость.

Вы получаете blittable struct, не используя bool. Вместо этого используйте byte. Вы все равно можете вернуть bool, обернув элемент структуры с помощью свойства. Не используйте автоматически реализованное свойство, вы должны заботиться о позиции байта. Таким образом:

struct MyStruct 
{
    private byte _f;
    public bool f {
        get { return _f != 0; }
        set { _f = value ? 1 : 0; }
    }
}

Собственный код не обращает внимания на свойство. Не волнуйтесь о служебных затратах времени исполнения для getter и setter, оптимизатор джиттера заставляет их исчезнуть, и каждый из них превращается в одну команду процессора.

Ответ 2

Должен работать:

[StructLayout(LayoutKind.Sequential)]
unsafe struct MyStruct
{
   public fixed bool fs[5];
}