gpt4 book ai didi

c# - 为什么看起来这个字符串是按值内联存储在显式布局类或结构中的?

转载 作者:行者123 更新时间:2023-11-30 18:45:24 25 4
gpt4 key购买 nike

我一直在对 System.Runtime.CompilerServices.Unsafe MSIL 包进行一些极其不安全且略微无用的操作,它允许您使用指针做很多在 C# 中做不到的事情。我创建了一个返回 ref byte 的扩展方法,该字节是对象开始处方法表指针的开始,它允许您在固定语句中使用任何对象,采用指向对象开始的字节指针:

public static unsafe ref byte GetPinnableReference(this object obj)
{
return ref *(byte*)*(void**)Unsafe.AsPointer(ref obj);
}

然后我决定使用以下代码对其进行测试:

[StructLayout(LayoutKind.Explicit, Pack = 0)]
public class Foo
{
[FieldOffset(0)]
public string Name = "THIS IS A STRING";
}

[StructLayout(LayoutKind.Explicit, Pack = 0)]
public struct Bar
{
[FieldOffset(0)]
public string Name;
}

然后在方法中

        var foo = new Foo();
//var foo = new Bar { Name = "THIS IS A STRING" };

fixed (byte* objPtr = foo)
{
char* stringPtr = (char*)(objPtr + (foo is Foo ? : 12));

for (var i = 0; i < foo.Name.Length; i++)
{
Console.Write(*(stringPtr + i /* Char offset */));
}

Console.WriteLine();
}

Console.ReadKey();

真正奇怪的是它成功打印了“THIS IS A STRING”?代码是这样工作的:

  1. 获取指向对象最开始的字节指针objPtr
  2. 加 16 得到实际数据
  3. 再添加 16 以通过字符串 header 到达字符串的实际数据
  4. 加 4 以跳过字符串的前 4 个字节,即 int _stringLength(作为 Length 属性向我们公开)
  5. 将结果解释为字符指针

编辑:重要的一点 - 当将 foo 切换为键入 Bar 时,我只添加了 12 个字节而不是 36 个字节 (36 = 16 + 16 + 4)。为什么它在结构中只有 8 个字节的 header 而不是类中的 32 个?该结构具有较小的 header (我相信没有 syncblk)是有道理的,但是为什么字符串仍然没有 16 字节的 header ?我希望偏移量为 8 + 16 + 4 (28) 而不仅仅是 8 + 4 (12)然而,这种假设存在很大的缺陷。它假定字符串以内联方式存储在 class/struct 中。但是,字符串是引用类型,据我所知,只有对它们的引用存储在对象内部。特别是,我认为引用类型只能放在堆上——因为这个结构是一个局部变量,我认为它在堆栈上。如果不是,代码肯定看起来更像这样来获取 stringPtr

byte** stringRefptr = objPtr + 16;
char* stringPtr = (char*)(*stringRefPtr + 20);

将字符串引用作为 byte**,然后使用它来获取字符。如果字符串内部是 char[](我不确定它是否是)

,这仍然没有意义

那么,当字符串是引用类型时,为什么它会工作并打印字符串,即使它错误地假设字符串是内联存储的?

注意:需要 .NET Core 2.0+ 和 System.Runtime.CompilerServices.Unsafe nuGet 包,以及 C# 7.3+。

最佳答案

因为字符串确实是内联存储的。您假设的问题是字符串不是普通对象,而是作为特殊情况由 CLR 处理(可能出于性能原因)。

至于对象,由于字符串是唯一的成员,这自然是分配内存的最有效方式。尝试在您的字符串成员之后添加更多成员,您的代码将会中断。

这里有一些关于字符串如何存储在 CLR 中的引用资料

https://mattwarren.org/2016/05/31/Strings-and-the-CLR-a-Special-Relationship/

https://codeblog.jonskeet.uk/2011/04/05/of-memory-and-strings/

编辑:我没有检查,但我相信你在偏移量背后的推理是错误的。 36 = 24(对象的大小)+ 8(字符串标题?)+ 4(整数的大小),而对于结构,24 个字节变为 0,因为它没有标题。

关于c# - 为什么看起来这个字符串是按值内联存储在显式布局类或结构中的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54060690/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com