在C#中保留C ++指针是否安全?

时间:2011-08-14 13:19:16

标签: c# c++ pointers

我目前正在研究一些使用invoke的C#/ C ++代码。在C ++方面,有一个std :: vector,每个指针都由C#代码中的索引标识,例如函数声明如下所示:

void SetName(char* name, int idx)

但是现在我在想,因为我正在使用指针,我无法向C#发送指针地址本身然后在代码中我可以做这样的事情:

void SetName(char*name, int ptr)
{
   ((TypeName*)ptr)->name = name;
}

显然,这是我所得到的快速版本(并且可能无法编译)。

指针地址是否可以保证在C ++中保持不变,这样我可以安全地将其地址存储在C#中,或者由于某些原因这会太不稳定或危险?

4 个答案:

答案 0 :(得分:7)

在C#中,您不需要在这里使用指针,只需使用普通的C#字符串。

[DllImport(...)]
extern void SetName(string name, int id);

这是有效的,因为p / invoke中字符串的默认行为是使用MarshalAs(UnmanagedType.LPStr),它转换为C风格的char *。如果需要某种其他编组方式(例如[MarshalAs(UnmanagedType.LPWStr)]),则可以显式标记C#声明中的每个参数,对于每个字符串使用2个字节的arg。

使用指针的唯一原因是,如果需要保留对调用函数后指向的数据的访问权限。即使这样,您也可以在大多数时间使用out个参数。

你可以基本上调用任何东西而不需要指针(因此不需要不安全的代码,这需要在某些环境中执行特权)。

答案 1 :(得分:2)

是的,没问题。本机内存分配永远不会移动,因此将指针存储在C#端的IntPtr中就可以了。您需要某种返回此指针的pinvoked函数,然后

[DllImport("something.dll", CharSet = CharSet.Ansi)]
void SetName(IntPtr vector, string name, int index);

有关此C ++函数的故意在于:

void SetName(std::vector<std::string>* vect, const char* name, int index) {
    std::string copy = name;
    (*vect)[index] = copy;
}

请注意在C ++代码中使用 new ,您必须复制字符串。传递的名称参数指向由pinvoke marshaller分配的缓冲区,并且仅在函数体的持续时间内有效。您的原始代码无法使用。如果您打算返回指向vector&lt;&gt;的指针元素然后非常小心。添加元素时,向量会重新分配其内部数组。这样一个返回的指针将变为无效,并且您将在以后使用它时损坏堆。 C#List&lt;&gt;完全相同的事情发生了但没有悬挂指针的风险。

答案 2 :(得分:1)

我认为它是稳定的,直到你命令C ++代码并完全清楚他做了什么,而其他使用相同代码的开发人员也知道这个危险。

所以我认为,它不是非常安全的架构方式,我会尽可能地避免它。

问候。

答案 3 :(得分:1)

C#GC会移动东西,但C ++堆不会移动任何东西 - 指向已分配对象的指针保证delete之前保持有效。这种情况的最佳架构只是将指针发送到C#作为IntPtr,然后在C ++中将其恢复。

这肯定是一个非常好的,比令人难以置信的BAD,HORRIFIC整数演员更好的想法。