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

twe*_*ypi 6 c# c++ pointers

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

void SetName(char* name, int idx)
Run Code Online (Sandbox Code Playgroud)

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

void SetName(char*name, int ptr)
{
   ((TypeName*)ptr)->name = name;
}
Run Code Online (Sandbox Code Playgroud)

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

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

Mar*_*k H 7

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

[DllImport(...)]
extern void SetName(string name, int id);
Run Code Online (Sandbox Code Playgroud)

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

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

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


Han*_*ant 2

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

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

这是故意对这个 C++ 函数撒谎的:

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

注意C++代码中new的用法,你必须复制字符串。传递的name参数指向 pinvoke 编组器分配的缓冲区,并且仅在函数体的持续时间内有效。你原来的代码无法工作。如果您打算返回指向 vector<> 元素的指针,那么要非常小心。添加元素时,向量会重新分配其内部数组。这样返回的指针将变得无效,并且当您稍后使用它时,您将损坏堆。C# List<> 也会发生完全相同的情况,但没有悬空指针的风险。

  • 顺便说一句,在 C++ 中使用“new”是完全错误的。它应该只是“vect[index] = name;”。我什至不确定假装指针实际上是引用是否安全。 (2认同)