Pergunta

Eu estou tendo um pouco de um problema usando FieldOffset corretamente com matrizes. O código abaixo é um exemplo onde ele não funciona corretamente para mim:

[StructLayout(LayoutKind.Explicit)]
public struct IndexStruct {
    [FieldOffset(0)]
    public byte[] data;

    [FieldOffset(0)]
    public short[] idx16;

    [FieldOffset(0)]
    public int[] idx32;
}

Se eu, por exemplo, define a matriz chamada "dados" para um array de bytes serializada e depois tentar recuperar dados como calções usando o campo "idx16" a indexação ainda está alinhada como um byte []. O que significa que idx16 1 obtém o segundo byte de dados, não o segundo 16bit palavra (byte 2 e 3). Se fazer o inverso calções índice i em vez de bytes, o que significa que o alinhamento do deslocamento é herdado a partir dos dados da fonte. A minha pergunta, existe uma maneira de contornar isso? Eu sei que eu posso compensar o valor do índice multiplicando com o tamanho do elemento, mas existe outra maneira?

Aqui é uma resposta que eu encontrei aqui no StackOverflow, mas ao tentar esse código descobriu-se que ele não estava funcionando corretamente. Tentou fazê-lo usando um teste de unidade no VS com o seguinte código sem qualquer sucesso:

[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);
}

Muito obrigado antecipadamente!

Foi útil?

Solução

O problema é (pelo que posso ver) que você unioned referências das matrizes - assim qualquer variedade fica definido última vai ganhar. Uma vez que existe uma variedade, é usando o indexador (não byte offset) -. Modo que o tamanho não importa

A maneira de fazer isso "corretamente" (ou mal, conforme o caso pode ser) seria provavelmente com código inseguro - levando o ponteiro para a matriz - algo como:

    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
        }
    }

É claro, eu não estou certo que eu recomendo - mas que parece conseguir o que deseja

Gostaria também de saber se você pode soltar a struct completamente e usar apenas o acesso inseguro a um byte[] diretamente:

    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
        }
    }

Outras dicas

Seus define FieldOffset onde cada um dos seus elementos de dados estão dentro do struct ..

Ao definir-los todos para 0, você está dizendo ao compilador que todos eles na posição 0.

A segunda coisa que eu vejo é que você está criando uma matriz de bytes, shorts e ints.

ver: MSDN StructLayoutAttribute

[StructLayout(LayoutKind.Explicit)]
public struct IndexStruct {
        [FieldOffset(0)]
        public byte[16] data;

        [FieldOffset(16)]
        public short idx16;

        [FieldOffset(18)]
        public int idx32;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top