【问题标题】:Is it safe to keep C++ pointers in C#?在 C# 中保留 C++ 指针是否安全?
【发布时间】:2011-10-26 18:53:40
【问题描述】:

我目前正在编写一些使用调用的 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# 中,还是由于某种原因这会太不稳定或太危险?

【问题讨论】:

  • 澄清一下:你知道上面的代码只有在 unsafe 块内时才能在 C# 中编译?

标签: c# c++ pointers


【解决方案1】:

在 C# 中,您不需要在此处使用指针,只需使用纯 C# 字符串即可。

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

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

使用指针的唯一原因是如果您需要在调用函数后保留对指向的数据的访问权限。即便如此,您大部分时间都可以使用out 参数。

您可以在根本不需要指针的情况下 p/invoke 任何东西(因此不需要不安全的代码,这在某些环境中需要特权执行)。

【讨论】:

    【解决方案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;
    }
    

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

    【讨论】:

    • 顺便说一句,在 C++ 中使用 new 是完全错误的。它应该只是vect[index] = name;。而且我什至不确定假装指针真的是引用是否安全。
    • 已修复。演示字符串的复制是相当重要的。是的,它是安全的。
    • 这并不是我真正定义的更好。更像是,您将编译器会告诉您错误的问题交换为只有在堆损坏时才会发现的问题,因为原始指针非常不安全,并且您意外删除了它或泄漏了它。任何能够阅读基本 C++ 的人都会存储 std::vector&lt;std::string&gt; 并执行 vect[index] = name; 并知道它构成了一个副本。至少使用智能指针或其他东西。请。以这样的糟糕代码为例,就是在教别人错误的东西。
    • 叹气,请编辑代码 sn-p 以便您满意演示复制。
    • 这里。对你来说非常明确。
    【解决方案3】:

    我认为在您使用 C++ 代码并完全了解他的工作之前它是稳定的,并且使用相同代码的其他开发人员也知道这种危险。

    所以在我看来,这不是很安全的架构方式,我会尽可能避免它。

    问候。

    【讨论】:

      【解决方案4】:

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

      这肯定是一个非常棒的主意,比你必须去那里的非常糟糕、可怕的整数转换要好得多。

      【讨论】:

      • C# GC 不会移动字符串。
      • @Grozz - 确实如此,并不是每个字符串都被保留。
      猜你喜欢
      • 1970-01-01
      • 2012-12-07
      • 1970-01-01
      • 1970-01-01
      • 2016-09-11
      • 2011-06-27
      • 2011-06-02
      • 2012-01-27
      相关资源
      最近更新 更多