C # StructLayout / FieldOffset и индексация в массивах
Вопрос
У меня возникла небольшая проблема с использованием FieldOffset правильно с массивами. Приведенный ниже код является примером, где он не работает правильно для меня:
[StructLayout(LayoutKind.Explicit)]
public struct IndexStruct {
[FieldOffset(0)]
public byte[] data;
[FieldOffset(0)]
public short[] idx16;
[FieldOffset(0)]
public int[] idx32;
}
Если я, например, задаю массив с именем " data " в сериализованный байтовый массив, а затем попытайтесь извлечь данные в виде шорт, используя " idx16 " поле индексирования по-прежнему выравнивается как байт []. Это означает, что idx16 1 извлекает второй байт данных, а не второе 16-битное слово (байт 2 и 3). Если я делаю обратные индексные шорты вместо байтов, это означает, что выравнивание смещения наследуется от исходных данных. Мой вопрос, есть ли способ обойти это? Я знаю, что могу компенсировать значение индекса путем умножения на размер элемента, но есть ли другой способ?
Здесь - это ответ, который я нашел здесь в StackOverflow, но при попытке этого кода оказалось, что он не работает должным образом. Попробовал, используя модульный тест в VS со следующим кодом, но безуспешно:
[TestMethod()]
public void SumTest() {
float[] fArr = {2.0f, 0.5f, 0.0f, 1.0f};
MemoryStream ms = new MemoryStream();
for (int i = 0; i < fArr.Length; i++) {
ms.Write(BitConverter.GetBytes(fArr[i]), 0, sizeof(float));
}
byte[] buff = ms.ToArray();
double expected = 3.5f;
double actual = Sum(buff);
Assert.AreEqual(expected, actual);
}
Заранее большое спасибо!
Решение
Проблема в том (насколько я вижу), что вы объединили ссылки массивов - так что какой бы массив не был установлен последним, победит. Когда есть массив, он использует индексатор (а не смещение в байтах) - поэтому размер не имеет значения.
Способ сделать это "правильно" (или неправильно, в зависимости от обстоятельств), вероятно, будет с небезопасным кодом - с указателем на массив - что-то вроде:
IndexStruct s = new IndexStruct();
s.data = new byte[] { 1, 0, 0, 0, 1, 1 };
unsafe
{
fixed (short* data = s.idx16)
{
Console.WriteLine(data[0]); // should be 1 (little-endian)
Console.WriteLine(data[1]); // should be 0
Console.WriteLine(data[2]); // should be 257
}
}
Конечно, я не уверен, что рекомендую это - но, похоже, это то, чего вы хотите?
Мне также интересно, можете ли вы полностью удалить struct
и просто использовать небезопасный доступ к byte []
напрямую:
byte[] raw = new byte[] { 1, 0, 0, 0, 1, 1 };
unsafe
{
fixed (byte* addr = raw)
{
short* s = (short*)addr;
Console.WriteLine(s[0]); // should be 1
Console.WriteLine(s[1]); // should be 0
Console.WriteLine(s[2]); // should be 257
}
}
Другие советы
Ваш FieldOffset определяет, где каждый из ваших элементов данных находится внутри структуры.
Устанавливая их все в 0, вы сообщаете компилятору, что они все в позиции 0.
Второе, что я вижу, это то, что вы создаете массив байтов, шортов и целых чисел.
см .: MSDN StructLayoutAttribute р>
[StructLayout(LayoutKind.Explicit)]
public struct IndexStruct {
[FieldOffset(0)]
public byte[16] data;
[FieldOffset(16)]
public short idx16;
[FieldOffset(18)]
public int idx32;
}