为什么字符串指针位置不同?

Why string pointer position is different?

为什么每次运行应用程序时字符串指针的位置都不同,当我使用 StringBuilder 但当我声明一个变量时相同?

void Main()
{
    string str_01 = "my string";
    string str_02 = GetString();
    unsafe 
    {
        fixed (char* pointerToStr_01 = str_01)
        {
            fixed (char* pointerToStr_02 = str_02)
            {
                Console.WriteLine((Int64)pointerToStr_01);
                Console.WriteLine((Int64)pointerToStr_02);
            }
        }
    }
}

private string GetString()
{
    StringBuilder sb = new StringBuilder();
    sb.Append("my string");

    return sb.ToString();
}

输出:

40907812
178488268

下次:

40907812
179023248

下次:

40907812
178448964

str_01 持有对常量字符串的引用。然而,StringBuilder 动态构建字符串实例,因此 returned 字符串实例与具有相同内容的常量字符串在引用上不是同一实例。 System.Object.ReferenceEquals() 将 return false.

由于 str_01 是对常量字符串的引用,它的数据可能存储在可执行文件的数据部分中,它始终在应用程序虚拟地址 space 中获得相同的地址。

编辑:

使用PE.Explorer或类似软件打开编译后的.exe文件,可以看到UTF-8编码的"my string"文本。它存在于文件的 .data 部分,包括首选虚拟地址,该部分应加载到进程虚拟内存中。

然而,我无法重现 str_01 在应用程序的多次运行中具有相同的地址,可能是因为我的 x64 Windows 8.1 执行 Address space layout randomization (ASLR)。因此,所有指针在应用程序的多次运行中都将不同,即使是那些直接指向加载的 PE 部分的指针也是如此。

仅仅因为两个字符串相等并不意味着它们指向相同的引用(我猜这意味着具有相同的指针),C# 不会自动实习所有字符串,因为性能方面的考虑等等。如果您希望两个字符串的指针相同,您可以使用 string.Intern.

实习 str_02

我不同意

的输出
 Console.WriteLine((Int64)pointerToStr_01);

对你来说总是一样的,因为我亲自测试过它,以使我的观点更清楚。

让我们看看两种情况:

  • 在字符串 str_01 = "my string" 的情况下,当您打印此变量的指针值时,它不会与以前相同,因为每次创建一个新的 String 对象(即字符串是不可变的)并且 "my string" 被分配给它。然后在 Fixed 语句中打印指针的值,该值在您再次执行程序时超出范围,并且不会记住以前的值。
  • 我认为,到目前为止,您可以自行解释 StringBuilder 的行为。

同时检查:

 string str_01 = GetString();
 private static string GetString()
    {
        var sb = new String(new char[] {'m','y',' ','s','t','r','i','n','g'});
        return sb;
    }

当我使用 fixed 时,它将分配内存
由于 str_01 是常量字符串,它会在执行时分配内存并每次都指向相同的位置

fixed (char* pointerToStr_01 = str_01)

但在

的情况下
fixed (char* pointerToStr_02 = str_02)

它动态分配内存,因此指向位置每次都不同

因此每次我们 运行

时字符串指针都不同