为什么修改这个字符串同时也修改另一个单独字符串的内容



在我的静态main函数中,我有以下代码:

string str1 = "aaaaaaaaa";
pointerTest();
Console.WriteLine( "str1 is: " + str1 );

声明为unsafe的静态pointerTest-方法包含以下内容:

string str2 = "aaaaaaaaa";
fixed( char* ptr = str2 )
{
for( int i = 0; i < str2.Length / 3; ++i )
ptr[i] = 'z';
}
Console.WriteLine( "str2 is: " + str2 );

请注意,str1str2是如何独立声明的,但它们具有相同的内容。

该程序的预期输出为:

str2 is: zzzaaaaaa
str1 is: aaaaaaaaa

当我运行程序时,实际输出显示如下:

str2 is: zzzaaaaaa
str1 is: zzzaaaaaa

当我将str2str1更改为不具有完全相同的内容时(例如,在str2的末尾再添加一个"a"(,程序的操作与预期的一样
如果发现.Net Core 3.1Mono中都存在这种行为(不确定确切的版本,我使用了Repl.It(

我的问题是为什么会发生这种行为,以及如何解决它。

我的理论是,这是因为编译器优化,特别是一个称为字符串内部的过程:

编译器认识到没有必要单独分配str2,因为内存中已经有完全相同的字符序列,与str1的初始化一起分配。因此,它没有重新分配,而是将str2作为str1已经指向的位置的引用。

字符串在C#中被认为是不可变的,所以在正常情况下,不应该以任何方式、形状或形式修改它们的内容。

由于此代码使用unsafe关键字和指针逻辑,因此不能保证不会导致未定义的行为,从而导致溢出结果。

唯一的办法是绕过这个";"问题";是遵守C#的规范,并将字符串视为不可变的。

最新更新