1

我正在开发 C API 的 C++ CLI 包装器。C API 中的一个函数期望数据形式为:

void setData(byte* dataPtr, int offset, int length);
void getData(byte* buffer, int offset, int length);

对于 C++ CLI,建议我们使用 System.Collections.BitArray(是的,各个位有意义)。一个 BitArray 可以从一个字节数组构造并复制到一个:

  array<System::Byte>^ bytes = gcnew array<System::Byte>(40);
  System::Collections::BitArray^ ba = gcnew System::Collections::BitArray(bytes);
  int length = ((ba->Length - 1)/8) +1;
  array<System::Byte>^ newBytes = gcnew array<System::Byte>(length);
  ba->CopyTo(newBytes, 0);
  pin_ptr<unsigned char> rawDataPtr = &buffer[0];

我关心的是最后一行。以这种方式从数组中获取指针是否有效?C# 中是否有更好的选择来处理任意位?请记住,各个位是有意义的。

4

1 回答 1

1

以这种方式从数组中获取指针是否有效?

是的,这是有效的。pin_ptr<> 助手类在后台调用 GCHandle.Alloc(),请求 GCHandleType.Pinned。所以指针是稳定的,可以传递给非托管代码,而不必担心垃圾收集器会移动数组并使指针无效。

然而,这个问题缺少一个非常重要的细节。pin_ptr<> 存在而不是让您直接使用 GCHandle 的原因正是在何时调用 GCHandle.Free() 方法。您无需明确执行此操作, pin_ptr<> 为您执行此操作,它使用标准 C++ RAII 模式。换句话说,Free() 方法被自动调用,它发生在变量超出范围时。这让 C++ 编译器发出析构函数调用,然后调用 Free()。

当 C 函数存储传递的dataPtr并在以后使用它时,这将非常非常错误。后来的问题是,该数组将不再固定,现在可以存在于任意地址。主要数据损坏,很难诊断。getData() 函数强烈表明事实就是如此。不是很好。

你需要解决这个问题,自己使用 GCHandle::Alloc() 来永久固定数组对垃圾收集器来说是非常痛苦的,这是一块不会让步的石头,对程序的效率有长期的影响. 相反,您应该将托管数组复制到使用 malloc() 或 Marshal::AllocHGlobal() 分配的稳定内存中。那是非托管内存,它永远不会移动。Marshal::Copy() 是一种简单的复制方法。

于 2013-11-11T23:45:58.083 回答