我正在研究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(是的,各个Bits有意义)。 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#中有更好的替代方案吗?记住各个位有意义。
答案 0 :(得分:1)
以这种方式从数组中获取指针是否有效?
是的,这是有效的。 pin_ptr&lt;&gt; helper类在引擎盖下调用GCHandle.Alloc(),要求GCHandleType.Pinned。所以指针是稳定的,可以传递给非托管代码,而不用担心垃圾收集器会移动数组并使指针无效。
然而,问题中缺少一个非常重要的细节。 pin_ptr&lt;&gt;的原因当调用GCHandle.Free()方法时,存在而不仅仅是让你直接使用GCHandle 完全。你没有明确地这样做,pin_ptr&lt;&gt;它适合你,它使用标准的C ++ RAII模式。换句话说,Free()方法是自动调用,它发生在变量超出范围时。它让C ++编译器发出析构函数调用,然后调用Free()。
当C函数存储传递的 dataPtr 并稍后使用它时,这将非常非常错误。后来成为问题,数组将不再被固定,现在可以存在于任意地址。主要数据损坏,很难诊断。 getData()函数强烈暗示事实是这样的。这不好。
你需要解决这个问题,使用GCHandle :: Alloc()自己永久地固定阵列对于垃圾收集器来说非常痛苦,垃圾收集器是一条不会让步的岩石,对效率有长期影响该计划。相反,您应该将托管数组复制到您使用malloc()或Marshal :: AllocHGlobal()分配的稳定内存。这是无管理的内存,它永远不会移动。 Marshal :: Copy()是一种复制它的简单方法。