C# 返回在函数内部使用 stackalloc 创建的指针
C# Returning a pointer created with stackalloc inside a function
我有与 C++ 代码交互的 C# 代码,后者对字符串执行操作。
我在静态助手中有这段代码class:
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;
}
}
如您所见,它 returns 指向使用 stackalloc
关键字创建的字节的指针。
但是从 C# 规范 18.8:
All stack allocated memory blocks created during the execution of a function member are automatically discarded when that function member returns.
是不是说这个指针实际上是在方法returns后就失效了?
方法的当前用法:
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
的使用次数(包括 GetByteCount()
和 Encoding
的 GetBytes()
的其他重载中的 fixed
语句).
tl;博士
方法returns指针一失效?在传给DirectFunction()
的时候是不是无效?
如果是这样,使用最少的 fixed
语句来完成任务的最佳方法是什么?
stackalloc 导致在堆栈上分配内存。当函数 returns 时堆栈自动展开。 C# 通过不让您 return 指针来保护您免于创建挂起指针,因为当函数 returns.[= 展开堆栈后,内存不可能仍然有效。 10=]
如果您希望内存超出分配它的函数的范围,则不能在堆栈上分配它。你必须通过 new 在堆上分配。
Does it mean that the pointer is actually invalid as soon as the method returns?
是的,它在技术上是无效的 - 虽然它几乎肯定不会被检测到。这种情况是通过 unsafe
自己造成的。对该内存的任何操作现在都具有未定义的行为。您所做的任何事情,尤其是调用方法,都可能会随机覆盖该内存 - 或不 - 取决于相对堆栈帧的大小和深度。
这个场景具体是提议的未来ref
改变希望目标的场景之一,意思是:允许stackalloc
进入ref
(而不是指针),与编译器知道它是堆栈引用 ref
或类引用类型,因此不允许 ref
-return 该值。
最终,当您输入 unsafe
时,您输入的是 "I take full responsibility if this goes wrong"。在这种情况下,确实是错误的。
在离开方法之前使用指针是有效的,所以一个可行的方法可能是(假设你想要一个相当通用的目的 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);
}
}
另请注意,非常大的字符串可能会导致堆栈溢出。
我有与 C++ 代码交互的 C# 代码,后者对字符串执行操作。
我在静态助手中有这段代码class:
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;
}
}
如您所见,它 returns 指向使用 stackalloc
关键字创建的字节的指针。
但是从 C# 规范 18.8:
All stack allocated memory blocks created during the execution of a function member are automatically discarded when that function member returns.
是不是说这个指针实际上是在方法returns后就失效了?
方法的当前用法:
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
的使用次数(包括 GetByteCount()
和 Encoding
的 GetBytes()
的其他重载中的 fixed
语句).
tl;博士
方法returns指针一失效?在传给
DirectFunction()
的时候是不是无效?如果是这样,使用最少的
fixed
语句来完成任务的最佳方法是什么?
stackalloc 导致在堆栈上分配内存。当函数 returns 时堆栈自动展开。 C# 通过不让您 return 指针来保护您免于创建挂起指针,因为当函数 returns.[= 展开堆栈后,内存不可能仍然有效。 10=]
如果您希望内存超出分配它的函数的范围,则不能在堆栈上分配它。你必须通过 new 在堆上分配。
Does it mean that the pointer is actually invalid as soon as the method returns?
是的,它在技术上是无效的 - 虽然它几乎肯定不会被检测到。这种情况是通过 unsafe
自己造成的。对该内存的任何操作现在都具有未定义的行为。您所做的任何事情,尤其是调用方法,都可能会随机覆盖该内存 - 或不 - 取决于相对堆栈帧的大小和深度。
这个场景具体是提议的未来ref
改变希望目标的场景之一,意思是:允许stackalloc
进入ref
(而不是指针),与编译器知道它是堆栈引用 ref
或类引用类型,因此不允许 ref
-return 该值。
最终,当您输入 unsafe
时,您输入的是 "I take full responsibility if this goes wrong"。在这种情况下,确实是错误的。
在离开方法之前使用指针是有效的,所以一个可行的方法可能是(假设你想要一个相当通用的目的 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);
}
}
另请注意,非常大的字符串可能会导致堆栈溢出。