在unsafe
块中,我正在尝试获取指向byte
数组的指针。但是根据声明的数组大小,我会得到不同的结果:
unsafe {
byte[] bytes;
bytes = new byte[1];
fixed(void* pBytes = bytes)
{
((int)pBytes).Dump(); //prints e.g. 41797644
}
bytes = new byte[0];
fixed(void* pBytes = bytes)
{
((int)pBytes).Dump(); //prints 0 ?!
}
}
如果我打开即时窗口并输入&bytes
,我会得到字节数组的实际地址,包括空数组的情况。
为什么fixed
非托管指针不起作用?
更新:
这是相同的代码以及我从即时窗口得到的内容:
unsafe {
byte[] bytes;
bytes = new byte[1];
fixed(void* pBytes = bytes)
{
// bytes =>
// {byte[1]}
// [0]: 0
//
// &bytes
// 0x0601c34c //the address of the variable
// bytes: 0x027dc804 //the address of the array
//
// pBytes
// 0x027dc80c // notice pBytes == (&bytes + 8)
// *pBytes: 0
}
bytes = new byte[0];
fixed(void* pBytes = bytes)
{
// bytes =>
// {byte[0]}
//
// &bytes
// 0x0601c34c //same address of the variable, ofc
// bytes: 0x02aa7ad4 //different address of (new) array
//
// pBytes
// 0x00000000 // BOINK
// *pBytes: Cannot dereference 'pBytes'.
// The pointer is not valid.
}
}
数组对象(& bytes)的地址与数组指针之间的8字节差异由对象的标头解释。
内存中的数组表示形式为:
type id size elem 0 elem1 ...
----|--------|--------|--------|--------|...
^ 4Bytes 4Bytes ^
| `--< pBytes
`--< &bytes
不安全指针实际指向实际数据的开头(即将被编组到非托管上下文的内容)
有没有办法在代码中获得空数组的实际地址?
FWIW,我实际上需要能够到达数组的头部,以便动态修改数组的运行时类型。
答案 0 :(得分:12)
为什么固定的非托管指针不一样?
这是一个奇怪的问题。你为什么相信它应该?
合同是:当您修复一个包含n个元素的数组时,其中n&gt; 0你得到一个指向缓冲区的指针,你可以从中读取和写入n个元素。
现在,当n为零时,null是指向缓冲区的指针,您可以从中读取和写入零元素,因此事实证明,在n的情况下实际符合该合同是零。 不要求使用C#语言。规范说明
如果数组表达式为null或者数组的元素为零,则fixed语句的行为是实现定义的。
因此,实施将完全在其权利范围内,例如,在您的程序中抛出异常。实际上,C#语言规范根本没有定义程序的含义。
你正在尝试使用fixed
标签来做一些非常危险和错误的事情。不要那样做。你应该只在一个数组上使用fixed
来获取一个东西:获取一个指向你可以读写的n个元素的缓冲区的指针。
有没有办法在代码中获得空数组的实际地址?
是。使用GCHandle
手动固定。
固定托管对象以获取其地址几乎总是危险和错误的。
我需要能够到达数组的头,以便动态修改数组的运行时类型。
这总是危险和错误的。在任何情况下都不要这样做。
答案 1 :(得分:1)
获取地址的方法是制作GCHandle
。
请参阅GCHandle to get address(pointer) of .net object
GCHandle handle;
IntPtr ptr;
byte[] bytes;
bytes = new byte[1];
handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
ptr = handle.AddrOfPinnedObject();
ptr.ToInt32().Dump(); // Prints 239580124
handle.Free();
unsafe {
fixed(void* pBytes = bytes)
{
((int)pBytes).Dump(); //prints 239580124
}
}
bytes = new byte[0];
handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
ptr = handle.AddrOfPinnedObject();
ptr.ToInt32().Dump(); // Prints 239609660
handle.Free();
unsafe {
fixed(void* pBytes = bytes)
{
((int)pBytes).Dump(); //prints 0
}
}
请参阅Eric Lippert's answer了解其原因。