Как я могу симулировать объединение C ++ в C #? - PullRequest
9 голосов
/ 22 июля 2009

У меня небольшой вопрос о структурах с установленным атрибутом LayoutKind.Explicit. Я объявил struct, как вы можете видеть, с fieldTotal с 64 битами: fieldFirst первые 32 байта и fieldSecond последние 32 байта. После установки fieldfirst и fieldSecond на Int32.MaxValue я бы ожидал, что fieldTotal будет Int64.MaxValue, чего на самом деле не происходит. Почему это? Я знаю, что C # на самом деле не поддерживает объединения C ++, возможно, он будет хорошо читать только значения при взаимодействии, но когда мы пытаемся установить значения самостоятельно, он просто не справится с этим очень хорошо?

[StructLayout(LayoutKind.Explicit)]
struct STRUCT {
    [FieldOffset(0)]
    public Int64 fieldTotal;

    [FieldOffset(0)]
    public Int32 fieldFirst;

    [FieldOffset(32)]
    public Int32 fieldSecond;
}

        STRUCT str = new STRUCT();
        str.fieldFirst = Int32.MaxValue;
        str.fieldSecond = Int32.MaxValue;
        Console.WriteLine(str.fieldTotal); // <----- I'd expect both these values
        Console.WriteLine(Int64.MaxValue); // <----- to be the same.
        Console.ReadKey();

Ответы [ 3 ]

10 голосов
/ 22 июля 2009

Причина в том, что FieldOffsetAttribute принимает количество байтов в качестве параметра, а не количество бит. Это работает как ожидалось:

[StructLayout(LayoutKind.Explicit)]
struct STRUCT
{
    [FieldOffset(0)]
    public Int64 fieldTotal;

    [FieldOffset(0)]
    public Int32 fieldFirst;

    [FieldOffset(4)]
    public Int32 fieldSecond;
}
6 голосов
/ 22 июля 2009

Глядя на шестнадцатеричные значения, если Int32.MaxValue и Int64.MaxValue должны предоставить ответ.

Ключ является наиболее значимым битом. Для положительного целого числа старший значащий бит устанавливается только для отрицательного числа. Таким образом, максимальное значение Int32 равно 0, за которым следует целый ряд 1 с. Порядок не важен, просто будет хотя бы один бит 0. То же самое относится и к Int64.MaxValue.

Теперь рассмотрим, как должен работать профсоюз. Это будет по существу раскладывать биты значений рядом друг с другом. Итак, теперь у вас есть набор битов длиной 64, который содержит два 0-битных значения. По одному на каждый из экземпляров Int32.MaxValue. Это никогда не может быть равно Int64.MaxValue, поскольку оно может содержать только один бит 0.

Как ни странно, вы, вероятно, получите искомое поведение, если для fieldSecond будет установлено значение Int32.MinValue.

РЕДАКТИРОВАТЬ Пропущено, что вам нужно сделать это FieldOffset (4).

4 голосов
/ 22 июля 2009

Бен М предоставил один из наиболее важных элементов - ваше определение настроено неправильно.

При этом, это не сработает - даже в C ++ с объединением. Указанные вами значения не будут (и не должны быть) одинаковыми, поскольку вы используете целые числа со знаком (а не без знака). Со знаком int (Int32) у вас будет 0 бит, а затем 1 бит. Когда вы сделаете объединение, вы получите 0 бит, за которым следует группа из 1 бита, затем еще один 0 бит, затем группа из 1 бита ... Второй 0 бит - это то, что вас портит. *

Если вы используете UInt32 / UInt64, это будет работать свойство, так как дополнительный знак не существует.

...