在 C# 中返回固定指针 ref

问题描述 投票:0回答:2

我想知道下面代码中的

RepeatedIDValue64_8.Get
方法在强制固定指针后返回某个内存段的引用是否安全?我们的程序出现了崩溃,我想知道是否与GC后的内存碎片整理有关?

public struct IDValue64 : IStructMessage<IDValue64>
{
    public static StructMessageParser<IDValue64> Parser = new StructMessageParser<IDValue64>();
    public const int SizeOf = 16;
    public long Id;
    public long Value;

    public override string ToString()
    {
        MessageFormat format = new MessageFormat();
        format.WriteLine("{");
        format.Indent();
        format.WriteLine("Id : " + Id.ToString() + ",");
        format.WriteLine("Value : " + Value.ToString() + " }");
        format.Outdent();
        return format.ToString();
    }

}


unsafe public struct RepeatedIDValue64_8 
{
    const int BuffSize = 128;
    public const int MaxCount = 8;
    public int Count;
    public fixed byte Buffer[BuffSize];
    public int GetCount()
    {
        return Count;
    }
    public int GetMaxCount()
    {
        return MaxCount;
    }
    public ref IDValue64 Get(int index)
    {
        if (index < 0 || index >= Count)
        {
            string message = string.Format("class RepeatedIDValue64_8 index{0} count{1}", index, Count);
            throw new ArgumentOutOfRangeException(message);
        }
        fixed (byte* pb = Buffer)
        {
            IDValue64* pbs = (IDValue64*)pb;
            return ref pbs[index]; // is safe?
        }
    }

    public override string ToString()
    {
        StringBuilder sb = new StringBuilder();
        sb.Append("{\n");
        for (int i = 0; i < Count; i++)
        {
            sb.Append("[" + i.ToString() + "]:");
            sb.Append(Get(i).ToString());    // core pos
        }
        sb.Append('}');
        return sb.ToString();
    }
}

dotnet版本是8.0

这是 coredump clrstack clrstack

c# .net fixed
2个回答
0
投票

首先,旁白,

public fixed byte Buffer[BuffSize];

不会使

Buffer
固定在内存中,而是将数组布局在结构体的内存中,而不是在其自己的内存块中。

但是,以固定的说法来说:

fixed (byte* pb = Buffer)
{
    IDValue64* pbs = (IDValue64*)pb;
    return ref span[index]; // is safe?
}

内存中的修复(来自 GC 重定位的块)仅在执行该块时适用。当包含函数返回时,块将退出,GC 可以重新定位

Buffer
(或者,如上所述,包含
Buffer
的结构体)。

来自 https://learn.microsoft.com/en-gb/dotnet/csharp/language-reference/statements/fixed

您只能在相应的

fixed
语句中使用声明的指针。声明的指针是只读的,不能修改:


0
投票

从广义上讲,这有效的,并且为您提供了一个指向缓冲区的内部指针:

    fixed (byte* pb = Buffer)
    {
        IDValue64* pbs = (IDValue64*)pb;
        return ref pbs[index]; // is safe?
    }

但是,有一个问题:内部指针to在哪里?

考虑:

RepeatedIDValue64_8 SomeMethod() {...}
// ...
ref IDValue64 = ref SomeMethod().Get(0);

在此之后,我们有一个指向临时堆栈位置的指针;您想要的只是为 field 或定义的 local 位置合理定义 - 而不是堆栈帧中定义的局部变量上方(下方)的瞬态空间。这就是以下行为不合法的原因:

struct Foo // note: this problem does not apply to classes
{
    private int bar;
    ref int Bar => ref bar; // CS8170
}

因此,从 GC 的角度来看:是的,您可以将非托管指针强制指向固定区域内的托管指针 - 但是,仍然存在一些陷阱,您只需获取指向瞬态堆栈空间的内部指针即可最终导致损坏。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.