修改字符串是否合法?

3

使用fixed语句,一个指针可以指向一个字符串。使用该指针,可以修改该字符串。但在C#文档中是否允许呢?

using System;

class Program
{
    static void Main()
    {
        string s = "hello";
        unsafe
        {
            fixed (char* p = s)
            {
                p[1] = 'u';
            }
        }
        Console.WriteLine("hello");
        Console.Write("hello" + "\n");
        Console.ReadKey();
    }
}

// hullo
// hello

上述程序修改了一个字符串常量。

1
如果编译通过,那就是合法的。无论它是否符合良好的实践,这是另一个讨论话题。但这是C#语言中的一种怪癖。 - DavidG
6
@DavidG,这在使用 unsafe 和相关功能时是绝对不正确的。这一点的重点在于,它允许您执行不明显安全的操作,但要求您声称它们是安全的。将其用于破坏语言不变式是_不安全的_使用方法。 - Cubic
2
C#标准倾向于不涉及这样的低级问题;如果您使用unsafe,则假定您知道自己在做什么。但是,如果您乱写内部字符串,则不能保证运行时稳定性。 (甚至可能根本不允许这样做 - 运行时可能会将它们保留在只读页面并故障。当前实现不会这样做。) - Jeroen Mostert
@Cubic 我从未说过这是安全的,只是合法的。 - DavidG
3个回答

4
根据语言规范
通过固定指针修改托管类型的对象可能导致未定义的行为。例如,由于字符串是不可变的,程序员有责任确保由指向固定字符串的指针引用的字符没有被修改。
(我强调
因此,在语言规范中明确考虑了这一点,你不应该这样做,但这是的责任,而不是编译器的责任。

感谢您给出简单而完整的答案。 - Minimus Heximus

1
“Legal”可能不是这里要用的词。“不正确”是我想说的。在C#中,字符串被定义为不可变的。通过任意改变一个字符串,你正在违反类不变量。运行时可以以任何方式对其做出反应,包括“表面上工作”、“崩溃”或“窃取你的信用卡信息购买塔可”。unsafe关键字的整个目的是引入代码的一个部分,在这里你说:“好吧,我知道你不能证明这是安全的,但相信我,我知道我在做什么,它完全是安全的。”
*:在这种特殊情况下,更有可能的风险是,在编译器和运行时之间的某个地方,多个阶段在内联和常量折叠访问字符串文字时是完全合理的,但对于其他稍微有些不同的代码,则不是这样,这意味着你可能会在运行时获得不一致的结果。底线是,不要这样做。

我其实不会太担心你在最后一段中提到的不一致性问题——编译器会忠实地将相同的字符串字面量统一为相同的“ldstr”操作码,而运行时则保证所有引用相同文本的“ldstr”操作码都将获得相同的字符串对象。正如规范所说,“当两个或更多按照字符串等式运算符[...]等效的字符串字面量出现在同一个程序中时,这些字符串字面量指代相同的字符串实例”。当然,这仍然不安全。 - Jeroen Mostert
我认为legallanguage-lawyer标签兼容。虽然我知道更改字面字符串不是一个好主意,但我提出这个问题是想看看文档是否明确允许这样做。因为C#文档明确允许定义指向字符串的指针。我的问题是它是否允许使用该指针修改字符串?它是否禁止这种用法?还是属于未定义行为? - Minimus Heximus
@nano字符串被定义为不可变的。您不需要单独的段落来解释改变不可变对象是不好的事实。 - Cubic

-2

法律是一个强有力的词,但是是的,你可以这样做。我还要补充一点,除非绝对必要,否则不要使用它。


有没有更温和一点的词建议? - Minimus Heximus
1
你有必要的情况的例子吗?编辑:我只能想到编写某种利用程序,所以也许那就是一个例子。 - Theraot

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