【问题标题】:Converting int[] to byte: How to look at int[] as it was byte[]?将 int[] 转换为 byte:如何将 int[] 视为 byte[]?
【发布时间】:2012-02-05 01:49:56
【问题描述】:

解释一下:我有整数数组作为输入。我需要将它转换为字节数组,其中 1 int = 4 字节(大端)。在 C++ 中,我可以轻松地转换它,然后像访问字节数组一样访问该字段,而无需复制或计算数据 - 只需直接访问即可。这在 C# 中可能吗?而在 C# 2.0 中呢?

【问题讨论】:

  • 您的应用程序是否将在原生大端的平台上运行?如果没有,那么您至少需要复制字节来进行转换。

标签: c# types int byte


【解决方案1】:

是的,使用不安全的代码:

int[] arr =...
fixed(int* ptr = arr) {
    byte* ptr2 = (byte*)ptr;
    // now access ptr2[n]
}

如果编译器报错,添加(void*):

    byte* ptr2 = (byte*)(void*)ptr;

【讨论】:

  • 刚刚测试过,编译器没有报错。 (至少使用 C#4)
【解决方案2】:

您可以创建一个 byte[] 大小为 int[] 长度的 4 倍。 然后,您遍历整数数组并从以下位置获取字节数组:

BitConverter.GetBytes(int32);

接下来,您使用 Buffer.BlockCopy 将此函数中的 4 个字节复制到正确的偏移量 (i * 4)。

BitConverter Buffer.BlockCopy

【讨论】:

    【解决方案3】:

    看看BitConverter 类。您可以遍历int 的数组,并调用BitConverter.GetBytes(Int32) 为每个数组获取byte[4]。

    【讨论】:

    • 不复制就无法访问数据。实际上,在这种情况下,“访问”和“复制”本质上是同义词。即使只是将 i 作为右值(访问变量的最小可能方式)复制其值。
    • @David 不,ref i 不会复制值:p
    • 每次读取的新分配非常昂贵,不幸的是BitConverter 不提供重用现有数组的重载。
    • @MarcGravell 那么它到底对这个值做了什么?您可以对寄存器以外的位置中的值执行的唯一操作就是复制它。 (在你访问它之前的值在多少个地方?在你访问它的时候它在多少个地方?如果第二个数字大于第一个,那就是副本。)
    • @David 另一种选择是获取地址(因此ref) - 这实际上是一个严肃的问题,就像struct 一样,您可以使用ref 传递地址到一个方法,然后在方法内部访问成员和调用方法,而无需复制值。这种方法在 XNA 中很常见,其中structs 很常见以避免 GC。
    【解决方案4】:

    如果您编写不安全的代码,您可以在内存中修复数组,获取指向其开头的指针,然后强制转换该指针。

    unsafe
    {
      fixed(int* pi=arr)
      {
        byte* pb=(byte*)pi;
        ...
      }
    }
    

    .net 中的数组以元素数量为前缀,因此您无法安全地在指向相同数据的int[] 和byte[] 之间进行转换。您可以在uint[] 和int[] 之间进行转换(至少就.net 而言,C# 本身对此功能的支持有点不一致)。

    还有一个基于联合的技巧来重新解释强制转换引用,但我强烈建议不要使用它。


    以本地字节序从字节数组中获取单个整数的常用方法是BitConverter,但相对较慢。手动代码通常更快。当然也不支持逆向转换。

    一种手动​​转换假设 little-endian 的方法(在我的 2.6GHz i3 上每秒管理大约 4 亿次读取):

    byte GetByte(int[] arr, int index)
    {
      uint elem=(uint)arr[index>>2];
      return (byte)(elem>>( (index&3)* 8));
    }
    

    如果您想使用托管代码,我建议手动编写使用位移来访问单个字节的代码,如果您想要最后一点性能,则建议使用指针。

    您还需要注意字节顺序问题。其中一些方法仅支持原生字节序。

    【讨论】:

    • 位移非常清楚。特别是,有些平台甚至不允许不安全。
    【解决方案5】:

    类型安全托管代码中最简单的方法是使用:

    byte[] result = new byte[intArray.Length * sizeof(int)];
    Buffer.BlockCopy(intArray, 0, result, 0, result.Length);
    

    这并不完全符合我认为您的问题所问的问题,因为在小端架构(如 x86 或 ARM)上,结果数组最终将成为小端,但我很确定 C++ 也是如此也是。

    如果您可以使用unsafe{},您还有其他选择:

    unsafe{
      fixed(byte* result = (byte*)(void*)intArray){
        // Do stuff with result.
      }
    }
    

    【讨论】:

    • 我认为BlockCopy 使用本地字节序,而不是小字节序。当然,CLR 通常在小端架构上运行,因此在实践中差异并不重要。
    • @CodeInChaos,是的——非常正确。我的措辞有点草率。我真的想将他可能的平台选择导致的小端与 OP 对大端结果的要求进行对比。我已经修正了措辞,让事情变得更清楚。
    猜你喜欢
    • 1970-01-01
    • 2011-06-24
    • 1970-01-01
    • 2020-02-03
    • 2010-11-08
    • 2017-03-18
    • 2019-08-08
    • 2014-05-04
    • 2014-03-05
    相关资源
    最近更新 更多