问题描述
我有与C ++代码交互的C#代码,该代码执行字符串操作.
I have C# code that interacts with C++ code, which performs operations with strings.
我在静态帮助器类中有这段代码:
I have this piece of code in a static helper 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;
}
}
如您所见,它返回一个指针,该指针指向用stackalloc
关键字创建的字节.
但是,从C#规范18.8开始:
As you can see, it returns a pointer to the bytes created with the stackalloc
keyword.
However from the C# Specifications 18.8:
这是否意味着该方法返回后指针实际上实际上是无效的?
Does it mean that the pointer is actually invalid as soon as the method 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
方法?
And use fixed
again on the array returned, to pass the pointer to the DirectFunction
method?
我正在尝试尽量减少使用fixed
的次数(包括GetByteCount()
和Encoding
的GetBytes()
其他重载中的fixed
语句).
I'm trying to minimise the number of fixed
usages (including the fixed
statements in other overloads of GetByteCount()
and GetBytes()
of Encoding
).
tl; dr
-
方法返回后指针是否立即无效?在传递给
DirectFunction()
时是否无效?
如果是这样,使用最少的fixed
语句来完成任务的最佳方法是什么?
If so, what is the best way to use the fewest fixed
statements to achieve the task?
推荐答案
是的,从技术上讲,它是无效的-尽管几乎可以肯定不会检测到它.此情况是通过unsafe
自行造成的.现在对该内存执行的任何操作都具有未定义的行为.您所做的任何事情,特别是调用方法,都可能会随机覆盖该内存-或不-取决于相对的堆栈帧大小和深度.
Yes, it is technically invalid - although it almost certainly won't be detected. This scenario is self-inflicted via unsafe
. Any action on that memory now has undefined behavior. Anything you do, but in particular calling methods, may randomly overwrite that memory - or not - depending on the relative stack-frame sizes and depth.
这种情况特别是拟议的未来ref
希望改变目标的一种情况,这意味着:允许stackalloc
进入ref
(而不是指针),并且编译器知道它是一个堆栈-引用ref
或类似ref的类型,因此不允许ref
返回该值.
This scenario is specifically one of the ones that the proposed future ref
changes hope to target, meaning: allowing stackalloc
into ref
(rather than a pointer), with the compiler knowing that it is a stack-referring ref
or ref-like type, and thus disallowing ref
-return of that value.
最终,当您键入unsafe
时,您说的是如果出现错误,我将承担全部责任".在这种情况下,确实是错误的.
Ultimately, the moment you type unsafe
you're saying "I take full responsibility if this goes wrong". In this case, it is indeed wrong.
在离开该方法之前使用指针 是有效的,因此一种可行的方法可能是(假设您想要一个相当通用的API)以允许调用方传递一个委托或接口,该委托或接口指定调用者希望您使用指针做的内容,即
It is valid to use the pointer before leaving the method, so one viable approach might be (assuming yo want a fairly general purpose API) to allow the caller to pass in a delegate or interface that specifies what the caller wants you to do with the pointer, i.e.
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);
}
}
还请注意,非常大的字符串可能会导致堆栈溢出.
Note also that very large strings may cause you to stack-overflow.
这篇关于C#返回在函数内部使用stackalloc创建的指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!