【问题标题】:How to get the pointer to the middle of an array in c#如何在c#中获取指向数组中间的指针
【发布时间】:2011-02-02 15:07:41
【问题描述】:

首先,关于我们环境的基本信息:我们使用 c# .net 4.0,在 Win7-x64 上,针对 32 位。

我们有一个预先分配的 -large- 数组。在函数中,我们希望返回一个指向该数组中任意点的指针,以便调用函数知道在哪里写入。例如:

class SomeClass {
    void function_that_uses_the_array() {
       Byte [] whereToWrite = getEmptyPtrLocation(1200);
       Array.Copy(sourceArray, whereToWrite, ...);
    }
}

class DataProvider {
    int MAX_SIZE = 1024*1024*64;
    Byte [] dataArray = new Byte[MAX_SIZE];
    int emptyPtr=0;
    Byte[] getEmptyPtrLocation(int requestedBytes) {
       int ref = emptyPtr;
       emptyPtr += requestedBytes;
       return dataArray[ref];
    }
}

本质上,我们希望预先分配一大块内存,并保留此内存块的任意长度部分,并让其他一些类/函数使用这部分内存。

在上面的例子中,getEmptyPtrLocation 函数不正确;它被声明为返回 Byte[],但试图返回单个字节值。

谢谢

【问题讨论】:

    标签: c# .net pointers bytearray memory-pool


    【解决方案1】:

    正如其他人所说,您不能在 C# 中执行此操作 - 通常您不应该执行类似的操作。使用系统 - 利用垃圾收集器等。

    话虽如此,如果你想做类似的事情并且你可以相信你的客户不会超出他们分配的插槽,你可以让你的方法返回ArraySegment<byte>而不是byte[]。那将代表“数组的一部分”。显然 .NET 中的大多数方法不使用 ArraySegment<T> - 但您可以可能编写扩展方法,使用它作为您想要使用的一些更常见操作的目标。 p>

    【讨论】:

    • 我听过几次垃圾收集器的说法,但到目前为止我们的测试表明预分配确实有帮助。它会非常依赖于应用程序的特性吗?在我们的应用程序中,我们需要分配/释放 2K-128K 的内存(每次大小不同),大约每秒 250 次。
    • @SomethingBetter:您是否考虑过预分配各种大小的完整数组,并允许调用者只获取不同大小的缓冲区,而不是一个大数组中的子数组?
    • 也许可以,但长度差异很大,会导致大量碎片/空白空间。实际上,我的一个基本问题是我无法将数据读入数组的任意位置,因为我们使用的是第 3 方库。我已将其作为另一个问题发布在这里:http://stackoverflow.com/questions/4895799/pinvoke-pointers-and-array-copy。我会很感激任何见解。 --谢谢
    • @SomethingBetter:对于这样的 API 来说,不将索引放入数组中(就像 Stream.Read 那样)是相对不寻常的。就您的碎片问题而言 - 如果您有“带”缓冲区,它不需要太大的影响。您可以在不浪费大量空间的情况下浪费一些空间。
    【解决方案2】:

    这是 C# 而不是 C++ - 你真的在与垃圾收集器作斗争。内存分配在 C# 中通常非常快,而且它不会受到内存碎片的影响,这是在 C++ 中预分配的其他主要原因之一。

    话虽如此,但您仍然想这样做,我将只使用数组索引和长度,因此您可以使用Array.Copy 在该位置写入。

    编辑:

    正如其他人指出的那样,ArraySegment<T> 更适合您的需求:它将消费者限制在数组的一部分中,并且不需要在更新原始数组中的内容之前分配单独的数组(如图所示由 ArraySegment)。

    【讨论】:

    • 同意这种观点(不应该这样做),不同意 Array.Copy 的使用。分配新内存,它不返回数组的子集。来自 MSDN:msdn.microsoft.com/en-us/library/k4yx47a1.aspx
    • @Chris:同意 - Array.Copy 将要求使用它的类在将其复制到目标缓冲区之前分配自己的缓冲区,他们也可以访问完整的数组 - 所以是的 @ 987654325@ 会更好。
    • @jon、@Chris、@BrokenGlass:感谢您提供有关 ArraySegment 的信息,我第一次听说这门课。虽然,它似乎不是很有用。事实证明,你不能用 [] 索引 ArraySegment,也不能迭代它。显然,您必须访问 ArraySegment.Array 字段,然后使用 [] 对其进行索引,索引 ID 是原始数组索引!。这是一个很好的link
    【解决方案3】:

    这是相当老的帖子,但我有一个简单的解决方案。我看到这个问题有很多答案,比如“你不应该”。如果有方法可以做某事,为什么不使用它呢?

    但除此之外,您可以使用以下内容

    int[] array = new int[1337];
    position = 69;
    void* pointer = (void*)Marshal.UnsafeAddrOfPinnedArrayElement(array, position);
    

    【讨论】:

      【解决方案4】:

      如果不进入非托管代码,你不能在 C# 中返回指向数组中间的指针,你所能做的就是返回要输入数据的数组索引。

      【讨论】:

        【解决方案5】:

        如果你必须这样做,你的选择包括返回一个 ArraySegment(正如 Jon Skeet 指出的那样),或者使用 Streams。您可以使用 this 构造函数返回作为数组子集的 MemoryStream。但是,您需要将所有内存读取和写入作为流操作处理,而不是作为偏移操作。

        【讨论】:

          猜你喜欢
          • 2016-04-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-12-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多