C#返回在函数内使用stackalloc创建的指针

时间:2017-05-06 07:06:14

标签: c# pointers unsafe stackalloc

我有C#代码与C ++代码交互,C ++代码使用字符串执行操作。

我在静态助手类中有这段代码:

internal static unsafe byte* GetConstNullTerminated(string text, Encoding encoding)
{
    int charCount = text.Length;
    fixed (char* chars = text)
    {
        int byteCount = encoding.GetByteCount(chars, charCount);
        byte* bytes = stackalloc byte[byteCount + 1];
        encoding.GetBytes(chars, charCount, bytes, byteCount);
        *(bytes + byteCount) = 0;
        return bytes;
    }
}

如您所见,它返回一个指向使用stackalloc关键字创建的字节的指针 但是从C#规范18.8:

  

当函数成员返回时,将自动丢弃在执行函数成员期间创建的所有堆栈分配的内存块。

这意味着一旦方法返回指针实际上是无效的吗?

该方法的当前用法:

byte* bytes = StringHelper.GetConstNullTerminated(value ?? string.Empty, Encoding);
DirectFunction(NativeMethods.SCI_SETTEXT, UIntPtr.Zero, (IntPtr) bytes);

代码是否应更改为

...
int byteCount = encoding.GetByteCount(chars, charCount);
byte[] byteArray = new byte[byteCount + 1];
fixed (byte* bytes = byteArray)
{
    encoding.GetBytes(chars, charCount, bytes, byteCount);
    *(bytes + byteCount) = 0;
}
return byteArray;

在返回的数组上再次使用fixed,将指针传递给DirectFunction方法?

我尝试尽量减少fixed次使用的次数(包括fixedGetByteCount() GetBytes()的其他重载中的Encoding次声明)。

TL;博士

  1. 方法返回后指针是否无效?在传递给DirectFunction()

  2. 时是否无效
  3. 如果是这样,使用最少的fixed语句来完成任务的最佳方法是什么?

2 个答案:

答案 0 :(得分:1)

stackalloc导致在堆栈上分配内存。函数返回时,堆栈会自动展开。 C#通过不让你返回指针来保护你不会创建挂起指针,因为在函数返回时,当堆栈被展开后,内存仍然无法生效。

如果您希望内存超出分配它的函数的范围,则无法在堆栈上分配它。你必须通过new分配在堆上。

答案 1 :(得分:1)

  

这意味着一旦方法返回指针实际上是无效的吗?

是的,它在技术上是无效的 - 虽然它几乎肯定不会被发现。此方案是通过unsafe自行造成的。对该内存的任何操作现在都有未定义的行为。你做的任何事情,特别是调用方法,都可能随机覆盖那个内存 - 或不是 - 取决于相对的堆栈框架大小和深度。

此场景特别是建议的未来ref更改希望定位的场景之一,意思是:允许stackalloc进入ref(而不是指针),编译器知道它是一个堆栈引用ref或类似ref的类型,因此不允许ref - 返回该值。

最终,当您输入unsafe时,您说“如果出现问题,我将承担全部责任”。在这种情况下,确实是错误的。

在离开方法之前使用指针是有效的,所以一种可行的方法可能是(假设你想要一个相当通用的API)来允许调用者传入一个委托或接口,用于指定调用者希望你用指针的内容,即

StringHelper.GetConstNullTerminated(value ?? string.Empty, Encoding,
    ptr => DirectFunction(NativeMethods.SCI_SETTEXT, UIntPtr.Zero, (IntPtr) ptr));

使用:

unsafe delegate void PointerAction(byte* ptr);
internal static unsafe void GetConstNullTerminated(string text, Encoding encoding,
    PointerAction action)
{
    int charCount = text.Length;
    fixed (char* chars = text)
    {
        int byteCount = encoding.GetByteCount(chars, charCount);
        byte* bytes = stackalloc byte[byteCount + 1];
        encoding.GetBytes(chars, charCount, bytes, byteCount);
        *(bytes + byteCount) = 0;
        action(bytes);
    }
}

另请注意,非常大的字符串可能会导致堆栈溢出。