C#中损坏的字符串


70

我遇到了“ CorruptedString”(解决方案)。这是本书中的以下程序代码:

var s = "Hello";
string.Intern(s);
unsafe
{
  fixed (char* c = s)
    for (int i = 0; i < s.Length; i++)
      c[i] = 'a';
}
Console.WriteLine("Hello"); // Displays: "aaaaa"

为什么该程序显示“ aaaaa”?我了解此程序如下:

  1. CLR中实习生池储备“你好”(我的图像实习生池作为一组字符串)。
  2. string.Intern(s) 实际上不执行任何操作,因为CLR保留了“ Hello”字符串-它仅返回保留的“ Hello”字符串的地址(对象s具有相同的地址)
  3. 该程序通过指针更改“ Hello”字符串的内容
  4. ??? 内部处理池中应没有Hello字符串,并且应该是错误的!但是没关系;该程序运行成功。

据我了解,实习生池,就像某种字符串到字符串的字典。也许我错过了什么?


11
for(int i = 0; i <s.Length; i ++)c [i] ='a'; 似乎您要用“ a”的字符替换每个字符
Dieter B

它不像是字符串到字符串的字典。它更像是字符串的
哈希集

8
不安全的关键字为您提供了一条线索... :-)

23
您关闭了安全系统,然后将一堆垃圾写入您不拥有的内存中。此时一切都可能发生,因此“为什么X发生了?”的答案 是“ X发生与任何可能发生的事件一致”。当您关闭安全系统然后滥用特权时,您失去了在可预测的世界中生活的权利。
埃里克·利珀特

3
有趣的OT:这也适用于Java。
2016年

Answers:


65

首次使用“ Hello”时,它会插入到应用程序全局字符串存储中。基于您在unsafe模式下执行的事实(unsafe 此处有更多信息),您可以直接引用存储在最初分配给string值的位置中的数据s,因此通过

for (int i = 0; i < s.Length; i++)
      c[i] = 'a';

您正在编辑内存中的内容。下次访问内部字符串的存储区时,它将使用内存中的相同地址,保存刚刚更改的数据。没有则不可能unsafestring.Intern(s);在这里不起作用;如果您将其注释掉,则其行为相同。

然后

Console.WriteLine("Hello"); // Displays: "aaaaa"

.NET查看是否有为之获取地址的条目,"Hello"并且有:您刚刚更新为的条目"aaaaa"'a'字符数由的长度确定"Hello"


6
string.Intern(s)在此程序中实际上什么也没做。您可以注释此行,并且程序将相同(因为“ Hello”已保留)。不过,我同意你的看法
LmTinyToon

我猜想JIT用指定的计算哈希键通过其getter替换了文字的所有出现。到那时,指定值已更改
LmTinyToon '16

1
“ .net查找是否存在从“ Hello”获得的哈希密钥条目,并且存在。” - 什么?当两个哈希相同时,表示应该执行等于。我不知道它是如何工作的,但是我认为所有事情都是在编译时完成的?Console.WriteLine("Hello");-“ Hello”只是对已知字符串的引用。
启示录,2016年

1
此步骤由JIT执行。在编译时,您只知道文字,而对intern表没有任何提及。
LmTinyToon '16

6
嗯 准时的儿子。
C. Tewalt '16

5

即使@Jaroslav Kadlec的答案是正确和完整的,我也想添加一些有关代码行为的信息,以及为什么string.Intern(s);这种情况下没有用。

关于实习生池

实际上,.NET会自动对所有字符串文字执行字符串插入,这是通过使用一个特殊的表完成的,该表存储了对应用程序中所有唯一字符串的引用。

但是,请务必注意,只有明确声明的字符串才能在编译阶段进行检查

考虑以下代码:

var first = "Hello"; //Will be interned
var second = "World"; //Will be interned
var third = first + second; //Will not be interned

当然,在某些情况下,我们想在运行时内联一些字符串,这可以通过使用String.Intern进行检查来完成String.IsInterned

让我们回到OP的片段:

//...
var s = "Hello";
string.Intern(s);
//...

在这种情况下string.Intern(s);是没有用的,因为它已经在编译阶段进行了实习。


1
重要的是要提到使用string.Intern(s)。字符串可以不拘留(见msdn.microsoft.com/en-us/library/...
LmTinyToon
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.