简体   繁体   English

C#:指向空数组的不安全指针是否为空?

[英]C#: unsafe pointer to empty array is null?

In an unsafe block, I'm trying to get a pointer to a byte array. 在一个unsafe块中,我正在尝试获取一个指向byte数组的指针。 But I get different results depending on the declared size of the array: 但是根据声明的数组大小,我会得到不同的结果:

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

If I open the immediate window and type &bytes , I get the actual addresses of the byte arrays, including the case with the empty array. 如果我打开立即窗口并输入&bytes ,我会得到字节数组的实际地址,包括空数组的情况。

Why doesn't the fixed unmanaged pointer work the same? 为什么fixed非托管指针不一样?

UPDATE: 更新:

Here's the same code and what I get from the immediate window: 这是相同的代码以及我从即时窗口获得的代码:

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

The 8-byte difference between the address of the array object (&bytes) and the array pointer is explained by the object's header. 对象的标题解释了数组对象(&bytes)的地址与数组指针之间的8字节差异。

The array representation in memory is: 内存中的数组表示形式为:

     type id  size     elem 0   elem1    ...
----|--------|--------|--------|--------|...
    ^ 4Bytes   4Bytes ^
    |                 `--< pBytes
    `--< &bytes

The unsafe pointer actually points to the start of, well, actual data (ie what would be marshalled to an unmanaged context) 不安全的指针实际上指向实际数据的开始(即,将被编组到非托管上下文的内容)

Is there a way I could get, in code, the actual address of the empty array? 有没有办法在代码中获得空数组的实际地址?

FWIW, I actually need that to be able to get to the array's header, to modify the array's runtime-type on the fly. FWIW,我实际上需要能够到达数组的头,以便动态修改数组的运行时类型。

Why doesn't the fixed unmanaged pointer work the same? 为什么固定的非托管指针不一样?

That's a strange question. 这是一个奇怪的问题。 Why do you believe it should? 你为什么相信它应该?

The contract is: when you fix an array with n elements where n > 0 you get a pointer to a buffer from which you can read and write n elements. 合同是:当您修复一个包含n个元素的数组,其中n> 0时,您将获得一个指向缓冲区的指针,您可以从中读取和写入n个元素。

Now, When n is zero, null is a pointer to a buffer from which you can read and write zero elements , so as it turns out, that contract is actually met for the case where n is zero. 现在,当n为零时,null是一个指向缓冲区的指针,您可以从中读取和写入零元素 ,因此事实证明,对于n为零的情况,实际上符合该约定。 The C# language is not required to do so. 不需要C#语言。 The specification says 规范说

The behavior of the fixed statement is implementation-defined if the array expression is null or if the array has zero elements. 如果数组表达式为null或者数组具有零元素,则fixed语句的行为是实现定义的。

So the implementation would be entirely within its rights to, say, throw an exception in your program. 因此,实施将完全在其权利范围内,例如,在您的程序中抛出异常。 The meaning of your program is actually not defined at all by the C# language specification. 实际上,C#语言规范根本没有定义程序的含义。

You're trying to use fixed off-label in order to do something incredibly dangerous and wrong. 你试图使用fixed的标签来做一些非常危险和错误的事情。 Don't do that. 不要那样做。 You should use fixed on an array for one thing only: to obtain a pointer to a buffer of n elements that you can read and write. 你应该只在一个数组上使用fixed来获取一个东西:获取一个指向你可以读写的n个元素的缓冲区的指针。

Is there a way I could get, in code, the actual address of the empty array? 有没有办法在代码中获得空数组的实际地址?

Yes. 是。 Pin manually with a GCHandle . 使用GCHandle手动GCHandle

Pinning a managed object in order to obtain its address is almost always dangerous and wrong. 固定托管对象以获取其地址几乎总是危险和错误的。

I need that to be able to get to the array's header, to modify the array's runtime-type on the fly. 我需要能够到达数组的头,以便动态修改数组的运行时类型。

That's always dangerous and wrong. 这总是危险和错误的。 Don't do that under any circumstances. 在任何情况下都不要这样做。

The way to get the address is to make a GCHandle . 获取地址的方法是制作GCHandle

See GCHandle to get address(pointer) of .net object 请参阅GCHandle以获取.net对象的地址(指针)

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

See Eric Lippert's answer for why it works like this. 请参阅Eric Lippert的答案 ,了解其原理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM