我有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
次使用的次数(包括fixed
和GetByteCount()
GetBytes()
的其他重载中的Encoding
次声明)。
TL;博士
方法返回后指针是否无效?在传递给DirectFunction()
如果是这样,使用最少的fixed
语句来完成任务的最佳方法是什么?
答案 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);
}
}
另请注意,非常大的字符串可能会导致堆栈溢出。