【发布时间】:2012-02-05 01:49:56
【问题描述】:
解释一下:我有整数数组作为输入。我需要将它转换为字节数组,其中 1 int = 4 字节(大端)。在 C++ 中,我可以轻松地转换它,然后像访问字节数组一样访问该字段,而无需复制或计算数据 - 只需直接访问即可。这在 C# 中可能吗?而在 C# 2.0 中呢?
【问题讨论】:
-
您的应用程序是否将在原生大端的平台上运行?如果没有,那么您至少需要复制字节来进行转换。
解释一下:我有整数数组作为输入。我需要将它转换为字节数组,其中 1 int = 4 字节(大端)。在 C++ 中,我可以轻松地转换它,然后像访问字节数组一样访问该字段,而无需复制或计算数据 - 只需直接访问即可。这在 C# 中可能吗?而在 C# 2.0 中呢?
【问题讨论】:
是的,使用不安全的代码:
int[] arr =...
fixed(int* ptr = arr) {
byte* ptr2 = (byte*)ptr;
// now access ptr2[n]
}
如果编译器报错,添加(void*):
byte* ptr2 = (byte*)(void*)ptr;
【讨论】:
您可以创建一个 byte[] 大小为 int[] 长度的 4 倍。 然后,您遍历整数数组并从以下位置获取字节数组:
BitConverter.GetBytes(int32);
接下来,您使用 Buffer.BlockCopy 将此函数中的 4 个字节复制到正确的偏移量 (i * 4)。
【讨论】:
看看BitConverter 类。您可以遍历int 的数组,并调用BitConverter.GetBytes(Int32) 为每个数组获取byte[4]。
【讨论】:
i 作为右值(访问变量的最小可能方式)复制其值。
ref i 不会复制值:p
BitConverter 不提供重用现有数组的重载。
ref) - 这实际上是一个严肃的问题,就像struct 一样,您可以使用ref 传递地址到一个方法,然后在方法内部访问成员和调用方法,而无需复制值。这种方法在 XNA 中很常见,其中structs 很常见以避免 GC。
如果您编写不安全的代码,您可以在内存中修复数组,获取指向其开头的指针,然后强制转换该指针。
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));
}
如果您想使用托管代码,我建议手动编写使用位移来访问单个字节的代码,如果您想要最后一点性能,则建议使用指针。
您还需要注意字节顺序问题。其中一些方法仅支持原生字节序。
【讨论】:
类型安全托管代码中最简单的方法是使用:
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 通常在小端架构上运行,因此在实践中差异并不重要。