假设有一个字节数组
byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
像这样
[StructLayout(LayoutKind.Explicit)]
struct ByteToUlongConverter
{
[FieldOffset(0)]
public byte[] bytes;
[FieldOffset(0)]
public ulong[] ulongs;
public ByteToUlongConverter(byte[] bytes)
{
this.ulongs = null;
this.bytes = bytes;
}
}
结构体。
接下来,有了这个结构,我做
ByteToUlongConverter conv = new ByteToUlongConverter(bytes);
foreach (ulong ul in conv.ulongs)
Console.WriteLine($"{ul:X16}");
这输出类似
0807060504030201
793C086C00000000
0000000000000000
0000000000000000
0000000000000000
0000000004710B40
00000001793BFDD8
793BED0C00000000
那些。我分配了8个字节,读了64个(你甚至不仅可以读,还可以写东西)。
这些额外的字节是谁的?这是什么记忆?那些。据我所知,我的阵列位于堆中的某个地方,而这是附近的东西。.Net 中的堆是如何组织的,所有进程都一样还是每个进程都有自己的堆?那些。另一个没有特权的进程可以尝试以这种方式读取我进程中的某些内容吗?
没错,就是字节数组后面的堆内存。.NET 中的堆是每个应用程序域一个,或者相对来说,一个应用程序一个。垃圾收集器完全与堆一起工作,如果由于相邻的应用程序,您还必须停止应用程序以释放内存,那就不是很好了。
但是请注意,进程和应用程序域是不同的概念。一个进程是 Windows 内核的一种结构,在一个进程中可以有许多应用程序域,因为它们(大概)是受管理的——也就是说,它们不能只是进入彼此的内存。但是,由于这是一个 Windows 进程,因此理论上,如果它们使用非托管代码,它们肯定会破坏彼此的内存。
因此,.NET 不允许您只运行非托管代码(请参阅托管/非托管代码互操作性概述中的安全部分)。由于硬件限制,另一个进程肯定无法进入您的内存,除非您非常明确地允许,否则另一个应用程序域也不能。
顺便说一下,堆也不是普通的块序列——为了提高垃圾收集的性能,它分为两部分:小对象和大对象。
现在专门介绍您的代码。您在同一地址有两个数组。在.NET中,数组的大小是存储在最开始的,分配了8个字节,也就是一个类型的数字
System.Int64。原始数组的长度为 8。当您尝试像处理 的数组一样处理此数组时ulong,长度保持不变,即 8,但元素本身变大 8 倍。所以你可以读取 8×8 个字节而不是 8 个。但有时会因为数组经过操作系统分配的段边界而出现异常,这将是一个纯粹的硬件异常。如果您尝试向
ulong数组写入内容,则行为将不可预测。堆中的每个块前面都有一个服务标头,因此从第 9 个字节开始就可以找到这样的标头,因此应用程序可能会在下一次垃圾回收时崩溃。该数组已分配到托管堆上。输出的第一行实际上显示了原始字节数组。
因此,在剩余的行中 - 托管堆的随机片段。
PS不要使用类似的转换黑客,使用更好
BitConverter