.NET 不安全的字符串操作

3

我使用下面这段不安全的代码来修改字符串:

public static unsafe void RemoveLastOne(ref string Str1)
    {
        if (Str1.Length < 1)
            return;

        int len = Str1.Length - 1;
        fixed (char* pCh1 = Str1)
        {
            int* pChi1 = (int*)pCh1;
            pCh1[len] = '\0';
            pChi1[-1] = len;
        }
    }

但是,一段时间后,我的C#程序崩溃并引发异常:

FatalExecutionEngineError: "运行时遇到致命错误。 错误的地址为0x6e9a80d9,在0xcfc线程上。错误代码为0xc0000005。此错误可能是CLR或用户代码的不安全或不可验证部分中的错误。此错误的常见来源包括COM-Interop或PInvoke的用户封送处理错误,这可能会破坏堆栈。"

如果我将函数“RemoveLastOne”更改为“Str1 = Str1.Remove(Str1.Length - 1);”,程序就可以正常工作。

为什么会出现异常?我该如何正确实现在C#中不安全的字符串更改?


1
在托管环境中为什么要使用非托管代码?如果您想要执行删除等操作,则确实没有必要,例如可以使用Replace()方法。 - MethodMan
1
只是确认一下,你的意思是要将一个 int 存储在 -1 索引的 char 数组中吗?(char 是 2 字节,int 是 4 字节) - Kieren Johnstone
pChi1[-1] 包含字符串的长度,为 int 类型(4 字节)。 - Ilya Georgievsky
2个回答

7
在.Net中,String值旨在是不可变的。但是在这个函数中,你却对一个不可变的值进行了多次可见的突变(包括内容和长度),更不用说在原始数据之前写入数据。我一点也不惊讶这最终会导致CLR崩溃,因为它在几个地方特殊处理String值,在指针之前写入数据就是非常危险的。
我真的看不出你为什么要在这里进行不安全的操作。安全代码很简单,不会造成这些难以跟踪的错误。

我只是试图加快我的解析器程序。在这个例子中,我只是简化了我的问题。谢谢。 - Ilya Georgievsky
1
这篇文章怎么样 - http://www.codeproject.com/KB/dotnet/strings.aspx ('SetLength'函数)。或者在旧版的.NET中它可以工作,但在新版中不行? - Ilya Georgievsky
2
@IlyaGeorgievsky 这是一篇不错的文章,但它依赖于String的特定实现细节,这些细节并不保证一定存在。此外,它也是在近10年前编写的,而事情自那时以来已经发生了显著变化。你有对你的解析器进行过性能分析吗?如果你发现这个函数会给你带来问题,我会非常惊讶。 - JaredPar
当然,我正在对其进行性能分析。在解析器中,经常使用一些字符串操作。我将一些搜索函数(如StartsWith、EndsWith)更改为不安全的方式,这提高了一些性能。但是更改字符串失败了。 - Ilya Georgievsky

2

不安全的字符串处理本质上是错误的。 .NET字符串不应该被编辑,很可能框架中的代码都建立在这样的假设之上,即字符串将永远不会改变。任何依赖于String.GetHashCode()的东西就会立刻出现问题,但可能存在幕后的优化或合理性检查。可能是这种情况导致了CLR错误。

如果您在进行性能分析后发现.NET的不可变字符串实现无法满足您的需求,则最简单的可变替代方案是List<char>,它可以让您修改其长度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接