【问题标题】:C# StructLayout/FieldOffset and indexing in arraysC# StructLayout/FieldOffset 和数组中的索引
【发布时间】:2010-10-24 19:18:33
【问题描述】:

我在正确使用 FieldOffset 和数组时遇到了一点问题。下面的代码是一个对我来说无法正常工作的示例:

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

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

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

例如,如果我将名为“data”的数组设置为序列化字节数组,然后尝试使用“idx16”字段将数据检索为short,则索引仍对齐为字节[]。这意味着 idx161 获取数据中的第二个字节,而不是第二个 16 位字(字节 2 和 3)。如果我做相反的我索引短裤而不是字节,这意味着偏移对齐是从源数据继承的。我的问题,有没有办法解决这个问题?我知道我可以通过乘以元素的大小来补偿索引值,但是还有其他方法吗?

Here 是我在 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);
}

非常感谢!

【问题讨论】:

  • 回复您的评论;当然,将索引除以大小可能比不安全的代码更可取;-p

标签: c# arrays indexing alignment field


【解决方案1】:

问题是(据我所见)您已经合并了数组的 references - 所以最后设置的数组将获胜。一旦有一个数组,它就会使用索引器(不是字节偏移量)——所以大小无关紧要。

“正确”(或不正确,视情况而定)执行此操作的方法可能是使用不安全的代码 - 获取指向数组的指针 - 类似于:

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

【讨论】:

  • 是的,这可行,我考虑过使用它,但如果有办法避免跳入不安全的代码,我更喜欢它。但是你的方法很好,如果一切都失败了,我会使用你的建议。
  • @Burre 重新解释强制转换托管引用,如果不安全代码的问题更多。如果你做低级的事情,没有理由避免不安全的代码,但避免完全扭曲 CLR 类型系统是邪恶的。我假设您的重新解释转换会导致未定义的行为,并且可能会在未来的 CLR 版本中中断。
  • @CodeInChaos 或者,实际上,在 WinRT 中
【解决方案2】:

您的 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;
}

【讨论】:

  • 有意将它们覆盖在位置 0 以使三个字段指向相同的内存区域。我期待使用 idx16 检索字节值会将字节对齐为短裤而不是字节,但这似乎不起作用,我想知道为什么。
猜你喜欢
  • 2018-01-04
  • 2020-08-12
  • 2018-04-27
  • 2012-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-28
  • 1970-01-01
相关资源
最近更新 更多