C#:如何访问C+上的托管2D阵列+-客户端? 我如何在C++端访问托管2D数组?< /P> 我知道我们应该使用 PixPtR ,以便从C++端访问托管数组。1D阵列很简单,但对于2D阵列,我不知道如何正确使用pin_ptr
我的代码如下所示,其中将从C#端调用C#:如何访问C+上的托管2D阵列+-客户端? 我如何在C++端访问托管2D数组?< /P> 我知道我们应该使用 PixPtR ,以便从C++端访问托管数组。1D阵列很简单,但对于2D阵列,我不知道如何正确使用pin_ptr,c#,.net,arrays,c++-cli,C#,.net,Arrays,C++ Cli,我的代码如下所示,其中将从C#端调用foo(): nativeFunc(双**ptr); foo(数组^A) { const int len=A->GetLength(0); 双**ptr=(双**)alloca(sizeof(双*)*len); 对于(int i=0;iGetLength(0); m_dimInner=managedArray->GetLength(1); m_handle=GCHandle::Alloc(managedArray,GCHandleType::pinted);
foo()
:
nativeFunc(双**ptr);
foo(数组^A)
{
const int len=A->GetLength(0);
双**ptr=(双**)alloca(sizeof(双*)*len);
对于(int i=0;i
问题是我的pin_ptr
会过早超出范围,因为它位于循环体内部,因此我认为上面的代码不安全。但我怎样才能避免这种情况呢?显然,它不允许创建一个既不是托管的也不是非托管的pin_ptr
数组。它也不能添加到std::vector
,也不能成为类成员。所以我被困在这里了
谢谢你的建议……好的,在进一步挖掘之后,我发现
GCHandle::Alloc(x,GCHandleType::pinted)
可以更灵活地替代这里的pin\u ptr
。但是,我们似乎只能从整体上确定托管阵列。它确实不似乎可以通过这种方式固定单个子阵列(内部阵列),就像pin_ptr
一样。此外,通过“try-and-error”方法,我发现通过GCHandle句柄,我可以通过hdl.AddrOfPinnedObject().ToPointer()
获得一个非托管指针,并且这个指针指向一个连续的内存块,该内存块以“展平”(序列化)形式包含整个2D数组。从这里,我可以通过使用适当的基指针和跨步重建非托管二维数组。但这被认为是一种“安全”的方法吗?它总是有效的吗?或者它甚至是特定于实现的吗
所以我拼凑了一个解决方案如下:
class ArrayPinHandlerRAII
{
public:
ArrayPinHandlerRAII(array<double,2> ^managedArray)
{
m_dimOuter = managedArray->GetLength(0);
m_dimInner = managedArray->GetLength(1);
m_handle = GCHandle::Alloc(managedArray, GCHandleType::Pinned);
m_ptr = new double*[m_dimOuter];
double *basePointer = reinterpret_cast<double*>
(m_handle.AddrOfPinnedObject().ToPointer());
for(size_t d = 0; d < m_dimOuter; d++)
{
m_ptr[d] = basePointer;
basePointer += m_dimInner;
}
}
~ArrayPinHandlerRAII(void)
{
delete [] m_ptr;
m_handle.Free();
}
inline double **data(void)
{
return m_ptr;
}
inline const size_t &dimOuter(void) const
{
return m_dimOuter;
}
inline const size_t &dimInner(void) const
{
return m_dimInner;
}
private:
GCHandle m_handle;
double **m_ptr;
size_t m_dimOuter;
size_t m_dimInner;
};
类arraypinhanderraii
{
公众:
ArrayPinHandlerarii(数组^managedArray)
{
m_dimOuter=managedArray->GetLength(0);
m_dimInner=managedArray->GetLength(1);
m_handle=GCHandle::Alloc(managedArray,GCHandleType::pinted);
m_ptr=新双*[m_dimOuter];
double*basePointer=reinterpret\u cast
(m_handle.AddrOfPinnedObject().ToPointer());
对于(大小d=0;d
有什么意见吗?;-)好的,MSDN中的一个示例包含以下重要信息: 固定在托管对象中定义的子对象具有固定整个对象的效果例如,如果数组的任何元素被固定,则整个数组也被固定。声明固定数组的语言没有扩展。若要固定数组,请将固定指针声明为其元素类型,并固定其中一个元素 因此,代码实际上可以简化为:
void nativeFunc(double **ptr);
void foo(array<double,2> ^A)
{
int dimOuter = managedArray->GetLength(0);
int dimInner = managedArray->GetLength(1);
pin_ptr<double> pinned = &A[i,0]; //This pins the *entire* array!
double **ptr = (double**) alloca(sizeof(double*) * dimOuter);
double *basePtr = pinned;
for(int i = 0; i < dimOuter; i++)
{
ptr[i] = basePtr;
basePtr += dimInner;
}
nativeFunc(ptr);
}
void nativeFunc(双**ptr);
void foo(数组^A)
{
int-dimOuter=managedArray->GetLength(0);
int dimInner=managedArray->GetLength(1);
pin_ptr pinted=&A[i,0];//这将固定*整个*数组!
双**ptr=(双**)alloca(双*)尺寸*dimOuter;
双*basePtr=固定;
对于(int i=0;i
对于2D,您需要两个For循环(第二个循环替换ptr[i]=pinted;
)。您现在指向的是来自长时间非托管内存(ptr
)的特定于作用域的数据(pind\u ptr pinted
),为什么我需要两个循环?对于1D数组,我根本不需要循环,因为pin_ptr
给我一个指向数组数据的非托管指针。对于2D数组(即数组的数组),我需要循环,因为我需要迭代“外部”数组并接收指向每个“内部”数组的非托管指针。最大的问题是,pin\ptr
只在数据超出范围之前固定数据,这是在我的循环的每次迭代之后如果不清楚,我的目标是获得指向数组数据的非托管double**
指针,这是“本机”函数所需要的。我不想复制数据!只要pin_ptr
在范围内,pin_ptr
中的数据就有效。在上面的代码中,作用域在下一行(}
)结束,之后不允许读/写指针。因此:1)您完全可以在pin_ptr
处于活动状态时编辑数据。2) 要在处理pin_ptr
后使用数据,您需要将该数据复制到自己的内存中(使用第二个for循环;或memcpy
),我很清楚pin_ptr
中的数据只要pin_ptr
在范围内就有效。我想避免复制数据。这就是重点。无论如何,我在以下信息之一中发现:“固定在托管对象中定义的子对象具有固定整个对象的效果。例如,如果数组的任何元素被固定,那么整个数组也被固定。”因此pin\u ptr
可以简单地位于循环之外,以完全避免“超出范围”的问题!看看我的另一个答案
void nativeFunc(double **ptr);
void foo(array<double,2> ^A)
{
int dimOuter = managedArray->GetLength(0);
int dimInner = managedArray->GetLength(1);
pin_ptr<double> pinned = &A[i,0]; //This pins the *entire* array!
double **ptr = (double**) alloca(sizeof(double*) * dimOuter);
double *basePtr = pinned;
for(int i = 0; i < dimOuter; i++)
{
ptr[i] = basePtr;
basePtr += dimInner;
}
nativeFunc(ptr);
}